Как работают предупреждения о неопределенных селекторах в Objective C? - PullRequest
1 голос
/ 16 января 2011

Есть ли способ заставить компилятор выдавать предупреждение при вызове селектора, который не определен? Например, у меня есть этот вызов где-то:

methodcall time1:[[self.H1time copy] stringValue]

Метод stringValue больше не существует в классе H1time, и компилятор ничего не вызывал.

copyWithZone объявлен как

- (NSHour*)copyWithZone:(NSZone *)zone;

Компилятор выдает предупреждение внутри NSHour, если я вызываю [self stringValue]. но не в methodcall time1:[[self.H1time copy] stringValue]

Я видел этот промах только из-за интенсивного теста, за которым последовал сбой.

Я бы хотел, чтобы компилятор предотвращал такие неправильные вызовы. Как я могу это сделать?

Некоторые тесты, в которых говорится, что компилятор может предупредить в приведенном выше случае:

NSHour *titi = [NSHour hourWithString:@"00:00:00"];
    id toto = [titi copy];
NSString *str2 = [toto doThis];  --- Warning : "No doThis Method found"

Еще один тест:

NSHour *titi = [NSHour hourWithString:@"00:00:00"];
NSString *str3 = [[titi copy] doThis];  --- Warning : "No doThis Method found"

Ответы [ 3 ]

6 голосов
/ 16 января 2011

Если вы хотите, чтобы компилятор предупреждал вас, приведите объект, возвращенный -copy, к ожидаемому вами типу, например,

[(NSHour *)[self.H1time copy] stringValue]

(кстати, я бы не использовал NSпрефикс для моих классов, так как это префикс, используемый Apple.)

Как уже говорили другие, -copy объявляется как возвращающее значение типа id.Поскольку id обозначает универсальный объект, компилятор не имеет никакой информации о том, на какие селекторы может возвращаться возвращаемое значение.Он знает, что существует селектор stringValue (потому что несколько классов Какао объявляют методы -stringValue), поэтому он принимает это.

Но я переопределил -copyWithZone:, чтобы вернуть NSHour *!

И это правильно, если вам нужно пользовательское поведение копирования, поскольку -copy - это просто удобный метод, который вызывает -copyWithZone:.Однако, как указано выше, ваш код вызывает -copy, который не был переопределен для возврата NSHour *.

Что если я также переопределю -copy, чтобы он возвращал NSHour *?

Тогда компилятор узнает об этом и предупредит вас в случае, если вы пытаетесь отправить сообщение, содержащее селектор, у которого нет соответствующего метода в NSHour.

Должен ли я сделать это?

Ну, по большей части, не совсем.

Вы когда-нибудь задумывались, почему все примеры переопределения -init возвращают id вместо класса?тип?Это происходит потому, что среда выполнения Objective C создает единственное описание метода для данного селектора.Следовательно, методы в разных классах, имеющие одинаковые селекторы (т. Е. Одинаковые имена методов), должны использовать одни и те же типы аргументов и возвращаемые типы.

Таким образом, с точки зрения времени выполнения селектор init описывается как метод сэто имя и тип возвращаемого значения id.Аналогично, селектор copy описывается как метод с таким именем и типом возврата id.Если вы повторно используете имя селектора в методах с различными типами возвращаемых значений или типами аргументов, вы путаете систему времени выполнения, что может привести к сбою или искажению данных.

При этом, когда вы статически печатаетеваши объекты, вы даете информацию компилятору, чтобы он знал, какие методы доступны, и он будет использовать соответствующие методы независимо от методов в других классах, имеющих разные типы возвращаемых данных и типы аргументов.

Эта тема на сайте cocoabuilder.com содержит больше информации по этому вопросу, включая ответ Грега Паркера, который работает над средой выполнения Objective-C.

2 голосов
/ 16 января 2011

Кажется, что определен метод copyWithZone: для возврата id в NSCopying:

- (id) copyWithZone: (NSZone*) zone;

И так же удобен copy метод NSObject:

- (id) copy;

Вы не получите предупреждение для сообщения stringValue, поскольку получатель id (= любой объект) и где-то определено сообщение stringValue(как на NSNumber).С другой стороны, нигде не определено doThis, так что компилятор может быть достаточно уверен, что это, скорее всего, опечатка, независимо от типа получателя.(А если вы настаиваете, вы всегда можете позвонить performSelector:, чтобы избавиться от предупреждения.)

Странно, что компилятор позволяет вам получить copyWithZone: с другой сигнатурой типа.Может быть, ваш родитель уже реализует NSCopying, а ваша copyWithZone: версия просто пропускается из-за другой подписи?

1 голос
/ 16 января 2011

Редактировать: этот ответ предполагал, что копия возвращала объект типа id, что не соответствует действительности, поэтому здесь не применимо.


Во-первых, важно понимать, что в Objective-C отправлять сообщения не вызывать методы .Это очень важное различие, поскольку оно означает, что цель сообщения (в данном случае 'stringValue') разрешается во время выполнения .

Когда вы вызываете:

[self stringValue]

... XCode достаточно умен, чтобы выяснить, что «self» не отвечает на сообщение «stringValue», потому что сообщение receive является тем же объектом, что и сообщение sender .. Но когда вы звоните:

[[self.H1time copy] stringValue]

... XCode не знает , что [self.H1time copy] разрешит до времени выполнения.Система не знает заранее, на что будет реагировать [self.H1time copy], поэтому вы не получите предупреждение компилятора.

Это кажется странным, но это сделано специально.Это одно из больших различий между Objective-C и C ++.Так что я не верю (и я счастлив, что доказал, что ошибался), что вы можете получить предупреждение о неизвестном селекторе, потому что Obj-C ориентирован на дизайн.

...