Использование (id) в Objective-C - PullRequest
7 голосов
/ 30 июля 2009

У меня есть функция, которую я хочу использовать для двух разных пользовательских объектов. Моей первой мыслью было принять аргумент как (id) и оперировать объектом id. Однако я не могу понять, как это сделать.

Оба класса (скажем, яблоки и апельсины) имеют переменные интерфейса:

NSDecimalNumber *count;

Я хочу сделать что-то похожее на это:

-(NSDecimalNumber*)addCount:(id)addObject{

    return [count decimalNumberByAdding:addObject.count];
}

Я не могу понять синтаксис, чтобы это произошло. Это правильный подход или было бы лучше создать подкласс (скажем, класс фруктов) и оперировать родительским классом?

-(NSDecimalNumber*)addCount:(Fruit*)addFruit{

    return [count decimalNumberByAdding:addFruit.count];
}

Ответы [ 5 ]

12 голосов
/ 30 июля 2009

Хотя вы можете отправить сообщение любому объекту (id) - средства доступа к свойствам требуют, чтобы компилятор знал о типе, с которым вы имеете дело - это потому, что средства доступа к свойствам являются синтаксическим сахаром при вызове определенных методов получения и установки. *

У вас есть несколько способов обойти это:

  1. Вместо доступа к свойству count, вызовите соответствующие методы [getCount].

  2. Если разные классы имеют разные версии этого метода, вы можете использовать проверку типа во время выполнения:

  3. Укажите базовый класс для обоих типов, чтобы вы могли передать нечто более конкретное, чем (id).

  4. Определите и реализуйте протокол, реализуемый обоими объектами, который определяет свойство счетчика (или метод).

Пример проверки динамического типа:

if( [object isKindOfClass:[Apple Class] )
   // call one overload of getCount
else if( [object isKindOfClass:[Orange Class] )
   // call another overload of getCount

Лично я предпочитаю строгую типизацию в моем коде, потому что это облегчает понимание цели. Это также позволяет IDE поддерживать ваши усилия по написанию кода с помощью функций intellisense, статического анализа и рефакторинга. Итак, в вашем случае я бы использовал либо № 3, либо № 4 в качестве подхода - в зависимости от того, действительно ли наследование подходит для данной проблемы.

8 голосов
/ 30 июля 2009

Вы должны стараться не обращаться к переменным экземпляра из другого класса.

В Objective-C достаточно, чтобы два объекта отвечали на один и тот же селектор (скажем, count), однако это выдает предупреждение компилятора.

Существует два способа избавиться от этого предупреждения: либо путем создания подкласса из общего класса Fruit, либо с помощью двух ваших классов, соответствующих протоколу. Я бы пошел с протоколом:

@protocol FruitProtocol

- (NSDecimalNumber *)count;

@end

@interface Orange : NSObject<FruitProtocol>
@end

@interface Apple : NSObject<FruitProtocol>
@end

Тогда ваш метод может выглядеть так:

-(NSDecimalNumber*)addCount:(id<FruitProtocol>)addFruit {
    return [count decimalNumberByAdding:[addFruit count]];
}

Здесь вы говорите, что ваш addCount ожидает любой объект, соответствующий протоколу FruitProtocol, и, следовательно, может отвечать на селектор count, поэтому компилятор примет его.

1 голос
/ 31 июля 2009

Проблема в том, что вы пытаетесь получить доступ к addFruit.count. Точечный синтаксис предназначен только для свойств, объявленных с помощью @property (или для структур). Если вы измените его на

[addFruit count]

и добавьте

-(NSDecimalNumber*)count
{
     return [[count retain] autorelease];
}

каждому классу, тогда это будет работать. Однако вы заметите, что получите предупреждение о том, что «id» может не отвечать на сообщение «count», и если вы не можете быть абсолютно уверены, что элементы, отправленные этому методу, реализуют метод «count», это проблематичный подход. ,

Я согласен с подходом pgb. Вы должны определить протокол и объявить оба класса для реализации этого протокола. Это устраняет проблему незнания того, будет ли объект реагировать на «подсчет» или нет, поскольку теперь у вас есть своего рода «контракт».

Если вы хотите сохранить синтаксис точки со свойством, вы можете объявить его в протоколе:

@protocol FruitProtocol

@property(readonly) NSDecimalNumber * count;

- (NSDecimalNumber *)count

@end

и тогда ваша функция будет:

-(NSDecimalNumber*)addCount:(id<FruitProtocol>)addObject{

    return [count decimalNumberByAdding:addObject.count];

}
0 голосов
/ 30 июля 2009

Насколько я понимаю, id не имеет никаких методов или переменных, связанных с ним, потому что это общий указатель, который не ссылается на какой-либо конкретный класс. Эта страница содержит полезную информацию об идентификаторах, если вы прокрутите немного вниз.

anObject это не будет иметь переменной count, поэтому ваша первая попытка не сработает. Создание базового класса и использование его в качестве параметра метода представляется мне лучшей идеей.

0 голосов
/ 30 июля 2009

Вы отправляете сообщение на счет, что такое счет? id - это указатель на любой тип объекта. Если вы ожидаете, что объект будет иметь свойство count, тогда вы сможете передавать только массив (или какое-либо другое ограничение типа).

-(NSDecimalNumber*)addCount:(NSArray*) Object{

return [count decimalNumberByAdding: [Object count]]; 

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...