Почему мне нужно преобразовать себя в id?

У меня есть метод инициализации, который принимает аргумент (id):


    -(id) initWithObject:(id) obj;

Я пытаюсь назвать это так:


    [[MyClass alloc] initWithObject:self];

Но XCode жалуется на то, что аргумент является «отдельным типом Objective-C» (который обычно указывает на несоответствие типов или уровень косвенной ошибки).

Если я явно приведу себя к (id), предупреждение исчезнет. В любом случае код работает должным образом. Интересно, что в следующей строке я передаю self другому методу, который также принимает идентификатор, и он отлично работает.

Мне интересно, не хватает ли мне чего-то тонкого - или это особенность компилятора?

Мне не совсем комфортно просто кастовать это, пока я не уверен в причинах, по которым это необходимо.

[Редактировать]

Меня попросили предоставить больше кода. Не уверен, что есть еще что-то актуальное. Вот мой фактический код, который выполняет вызов. Обратите внимание, что он сам находится внутри метода инициализации. Это вызов initWithSource, который выдает предупреждение:


-(id) initWithFrame:(CGRect) frame
{
    self = [super initWithFrame: frame];
    if( self )
    {
        delegate = nil;
        touchDelegate = [[TBCTouchDelegate alloc] initWithSource:self];
        [touchDelegate.viewWasTappedEvent addTarget: self action:@selector(viewWasTapped:)];
    }
    return self;
}

И вот вызываемый метод init:


-(id) initWithSource:(id) sourceObject
{
    self = [super init];
    if (self != nil) 
    {
        // Uninteresting initialisation snipped
    }
    return self;
}

person philsquared    schedule 23.11.2008    source источник
comment
Возможно, вы захотите разместить здесь больше кода; ничего не выскакивает, но осталось работать всего две строчки, сложно сказать.   -  person Ben Gottlieb    schedule 23.11.2008
comment
Согласитесь с Беном, но нужно учесть одно: объявлен ли метод initWithObject?   -  person Jason Coco    schedule 23.11.2008
comment
Спасибо, что посмотрели, ребята. Я добавил немного больше кода выше, чего стоит. @Jason: Да, initWithSource (мое настоящее имя метода) объявлен в заголовке, и он определенно совпадает.   -  person philsquared    schedule 23.11.2008
comment
О, и чтобы предупредить ваш следующий вопрос - да, я включаю этот заголовок в файл, который вызывает вызов ;-)   -  person philsquared    schedule 23.11.2008
comment
Можете ли вы создать «чистый» пример того, как это происходит, вместо того, чтобы использовать существующий проект? Иногда я нахожу проблему, когда пытаюсь это сделать :)   -  person Brian C. Lane    schedule 23.11.2008


Ответы (1)


Обычно это означает, что существует несколько initWithSource: имен методов в разных классах с конфликтующими типами аргументов. Помните, что если переменная набрана как id, компилятор не знает, к какому классу она принадлежит. Таким образом, если вы вызываете initWithSource: для объекта с id типом, а несколько классов имеют метод initWithSource:, компилятор по существу просто выбирает один из двух. Если он выберет «неправильный», то вы получите ошибку «особого типа Objective-C».

Так почему это с тобой происходит? Я не уверен на 100%, но помните, что +[TBCTouchDelegate alloc] возвращает id. Таким образом, объединение вызовов alloc / init эквивалентно следующему:

id o = [TBCTouchDelegate alloc];
touchDelegate = [o initWithSource:self];

Следовательно, вы вызываете initWithSource: для переменной с типом id. Если есть конфликтующий initWithSource: метод, вы можете получить эту ошибку компилятора.

Есть конфликтный метод? Я проверил систему, и единственная конфликтующая была в NSAppleScript:

- (id)initWithSource:(NSString *)source;

Сейчас NSAppleScript является частью Foundation, но я заметил, что это код iPhone. Так, возможно, вы получаете эту ошибку только при компиляции для симулятора, а не устройства?

В любом случае, если это ваша проблема, вы можете обойти ее, разделив alloc / init на две разные строки:

touchDelegate = [TBCTouchDelegate alloc];
touchDelegate = [touchDelegate initWithSource:self];

Теперь вы вызываете initWithSource: для полностью типизированной переменной (вместо id-typed), поэтому компилятору больше не нужно угадывать, какую из них выбрать. Или вы можете использовать возврат от +alloc:

touchDelegate = [(TBCTouchDelegate *)[TBCTouchDelegate alloc] initWithSource:self];

Другое решение - переименовать initWithSource:, чтобы избежать конфликта и, возможно, сделать его более описательным. Вы не говорите, какое имя класс в настоящее время называется, или для чего предназначен «источник», поэтому я не могу отбросить какие-либо возможности.

person Dave Dribin    schedule 23.11.2008
comment
Я попытался скопировать ваш код в свой проект и дважды щелкнуть командой initWithSource. Несмотря на то, что это проект для iPhone, он действительно пытается сослаться на NSApplescript, согласно предложению Дейва. - person Ben Gottlieb; 23.11.2008
comment
Спасибо, Дэйв - это было точно - и тоже ясно объяснено! К счастью, источник слова был довольно произвольным, поэтому я легко смог его изменить. TouchDelegate - это прокси для событий касаний, а источником является объект, для которого он проксируется. Я сейчас назвал это TouchSource. - person philsquared; 23.11.2008
comment
Спасибо за дополнительное подтверждение, Бен, и за то, что нашли время помочь. - person philsquared; 23.11.2008
comment
Это правильный ответ. Он должен быть отмечен как «одобренный»! - person e.James; 23.11.2008