Почему мне нужно преобразовать себя в 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;
}

Возможно, вы захотите разместить здесь больше кода; ничего не выскакивает, но с двумя строчками работать трудно сказать.

Ben Gottlieb 23.11.2008 20:26

Согласитесь с Беном, но нужно учесть одно: объявлен ли метод initWithObject?

Jason Coco 23.11.2008 20:53

Спасибо, что посмотрели, ребята. Я добавил немного больше кода выше, чего он стоит. @Jason: Да, initWithSource (мое настоящее имя метода) объявлен в заголовке, и он определенно совпадает.

philsquared 23.11.2008 22:11

О, и чтобы предупредить ваш следующий вопрос - да, я включаю этот заголовок в файл, который вызывает вызов ;-)

philsquared 23.11.2008 22:12

Можете ли вы создать «чистый» пример того, как это происходит, вместо того, чтобы использовать существующий проект? Иногда я нахожу проблему, когда пытаюсь это сделать :)

Brian C. Lane 23.11.2008 22:39
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
5
5
2 599
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 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), поэтому компилятору больше не нужно угадывать, какую из них выбрать. Или вы можете использовать возврат из +alloc:

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

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

Я попытался скопировать ваш код в свой проект и дважды щелкнуть командой initWithSource. Несмотря на то, что это проект для iPhone, он действительно пытается сослаться на NSApplescript, согласно предложению Дейва.

Ben Gottlieb 23.11.2008 23:11

Спасибо, Дэйв - это было точно - и тоже ясно объяснено! К счастью, слово «источник» было довольно произвольным, поэтому я легко смог его изменить. TouchDelegate - это прокси для событий касаний, а «источник» - это объект, для которого он проксируется. Сейчас я назвал это TouchSource.

philsquared 23.11.2008 23:13

Спасибо за дополнительное подтверждение, Бен, и за то, что нашли время помочь.

philsquared 23.11.2008 23:14

Это правильный ответ. Он должен быть отмечен как «одобрено»!

e.James 23.11.2008 23:33

Другие вопросы по теме