Почему -respondsToSelector: метод экземпляра может использоваться для имени класса или объекта класса?

В Программировании в Цели C, 4e, Глава 9, Программа 9.3:

#import "Square.h"
int main (int argc, char * argv[])
{
   @autoreleasepool {
      Square *mySquare = [[Square alloc] init];
      ...
      // respondsTo:
      if ( [mySquare respondsToSelector: @selector (setSide:)] == YES )
         NSLog (@"mySquare responds to setSide: method");
      ...
      if ( [Square respondsToSelector: @selector (alloc)] == YES )
         NSLog (@"Square class responds to alloc method");
      ...
   }
   return 0;
}

Q1:

поскольку -respondsToSelector: это метод экземпляра, а не метод класса, почему его можно использовать на Square класс прямо?

Q2:

Книга говорит, что вы можете использовать Square здесь вместо [Square class], Это всего лишь исключительный ярлык или за этим стоит какой-то механизм?

Любая помощь могла бы быть полезна! Заранее спасибо!

5 ответов

Q1:

Простой ответ заключается в том, что в дополнение к методам класса вы можете вызывать любой метод экземпляров корневого класса (каким бы ни был корневой класс вашего класса; в этом случае NSObject) на объекте класса.

Более сложный ответ состоит в том, что объекты класса являются экземплярами метаклассов. Принимая во внимание, что методы экземпляра являются методами в экземпляре, которые определены в классе; Методы класса - это методы объекта класса, которые определены в метаклассе. У каждого класса есть свой метакласс. Наследование метаклассов следует наследованию их классов; т.е. NSStringметакласс наследует от NSObjectметакласс. В конечном итоге метакласс корневого класса наследуется от корневого класса; т.е. NSObjectметакласс наследует от NSObject, Вот почему все методы экземпляров NSObject доступны для объектов классов.

Q2:

[Square class] вызывает метод класса +class (это не связано с -class). +class по сути, это метод идентификации, который просто возвращает вызываемую вещь (так же, как -self); т.е. если foo указатель на объект класса, то [foo class] такой же как foo,

Так +class кажется довольно бесполезным; Почему мы используем это? Это связано с тем, что в грамматике языка Objective-C имя класса не является допустимым выражением (в отличие от Smalltalk). Так что вы не можете сказать, id bar = Square;; это не будет компилироваться. Как особый случай в грамматике, имя получателя разрешается вместо получателя в выражении вызова сообщения, и сообщение отправляется объекту класса; т.е. [Square something], Поэтому, если вы хотите использовать объект класса в любом другом контексте выражения, мы делаем это окольным путем, вызывая метод идентификатора, такой как +class; т.е. [Square class] это выражение, которое можно использовать в любом контексте выражения ([Square self] также будет работать, но мы используем [Square class] по соглашению, что вызывает сожаление, так как его путают с -class); мы хотели бы просто использовать Square, но не может из-за языка.

В вашем случае это уже получатель в выражении вызова сообщения, поэтому делать это не нужно [Square class]; Square там уже работает.

Из языка программирования Objective-C, объектов, классов и сообщений,

Все объекты, классы и экземпляры должны иметь интерфейс с системой времени выполнения. И объекты классов, и экземпляры должны иметь возможность анализировать свои способности и сообщать о своем месте в иерархии наследования. Это область класса NSObject для предоставления этого интерфейса.

Так что методы NSObject не должны быть реализованы дважды - один раз, чтобы обеспечить интерфейс времени выполнения для экземпляров, и снова дублировать этот интерфейс для объектовкласса - объектам класса предоставляется специальное разрешение для выполнения методов экземпляра, определенных в корневом классе. Когда объект класса получает сообщение, на которое он не может ответить методом класса, система времени выполнения определяет, существует ли метод корневого экземпляра, который может ответить. Единственные методы экземпляра, которые может выполнять объект класса, это те, которые определены в корневом классе, и только если нет метода класса, который может выполнить эту работу.

В этом случае, NSObject это корневой класс. Как NSObject все случаи соответствуют NSObject protocol, где -respondsToSelector: определено, большинство объектов класса должны быть в состоянии выполнить -respondsToSelector:,

В настоящее время среда выполнения Objective C реализует класс как объект экземпляра некоторого другого класса. Таким образом, класс будет отвечать на определенные методы экземпляра.

//Q1:

Поскольку -respondsToSelector: является методом экземпляра, а не методом класса, почему можно было бы использовать его непосредственно в классе Square?//

Похоже, у вас есть представление о том, что методы класса нельзя вызывать из методов экземпляра (и наоборот). Напротив, казалось бы, что метод -respondsToSelector намеревался сделать это, скорее всего, получая класс отправителя с помощью метода -class, а затем запрашивая, отвечает ли класс на селектор и возвращает YES или NO. В более локализованном примере рассмотрим следующее:

-(void)someInstanceMethod{
     [MyCurrentClass doClassMethod]; //basic equivalent of [self doClassMethid];
}

Совершенно верно в Objective-C, при условии, что MyCurrentClass полностью выделен и инициализирован.

// Q2:

В книге сказано, что вы можете использовать Square вместо [Square class]. Это всего лишь исключительный ярлык или за этим стоит какой-то механизм?//

Полностью отсылать -класс в класс! Это не имеет большого смысла, а просто лишний ненужный код. -класс просто запрашивает класс получателя, независимо от того, является ли он экземпляром или объектом класса.

Реальная реализация, прямо из NSObject.m, такова:

- (BOOL)respondsToSelector:(SEL)aSelector {
        PF_HELLO("")
        return class_respondsToSelector( isa, aSelector );
}

Теперь я понятия не имею, почему это PF_HELLO("") есть, но, как вы можете видеть, это буквально спрашивает класс в RUNTIME "Эй, у вас есть метод для этого isa [instance], называемый aSelector?"

И в Objective-C методы класса ALSO принадлежат экземплярам, ​​но, тем не менее, имеют меньший приоритет (метод экземпляра с тем же именем, что и метод класса, вызывается перед методом класса).

Другим аспектом динамической типизации Objective-C является то, что id тип фактически объявлен следующим образом:

typedef struct objc_class *Class;
typedef struct objc_object {
    Class isa;
} *id;

Таким образом, ваш экземпляр объекта на самом деле является указателем класса. Это означает, что ваши сообщения -respondsToSelector отправляются также в класс типа экземпляра. В вашем случае это означает, что -respondsToSelector собирается в objc_class FIRST.

Теперь в тестовом примере (прямо из libFoundation) мой ответ будет суммирован так:

Test *tst = [Test new];

    fail_unless([tst respondsToSelector:@selector(testInstanceMethod)], "-[Test respondsToSelector:] returned NO for a valid instance method (testInstanceMethod).");
    fail_if([tst respondsToSelector:@selector(testClassMethod)], "-[Test respondsToSelector:] returned YES for a class method (testInstanceMethod).");
    fail_unless([Test respondsToSelector:@selector(testClassMethod)], "+[Test respondsToSelector:] returned NO for a valid class method (testClassMethod).");
    fail_if([Test respondsToSelector:@selector(testInstanceMethod)], "+[Test respondsToSelector:] returned YES for an instance method (testInstanceMethod).");
    fail_unless([tst respondsToSelector:@selector(init)], "-[Test respondsToSelector:] returned NO for an inherited instance method (-[NSObject init].");
    fail_unless([Test respondsToSelector:@selector(alloc)], "+[Test respondsToSelector:] returned NO for an inherited class method (+[NSObject alloc]).");

    [tst release];
Другие вопросы по тегам