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

При программировании в Цели 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;
}

1:

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

2:

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

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

Ответы [ 5 ]

4 голосов
/ 01 июня 2012

1:

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

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

2:

[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 там уже работает.

4 голосов
/ 12 марта 2012

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

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

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

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

0 голосов
/ 12 марта 2012

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

0 голосов
/ 12 марта 2012

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

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

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

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

Другим аспектом динамической типизации 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];
0 голосов
/ 12 марта 2012

// Q1:

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

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

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

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

// Q2:

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

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

...