Objective-C 类与实例调用 self 方法的区分
声明一个 Person
类,考虑如下代码:
id aa = [Person self];
id bb = [self self];
Person *cc = self;
id dd = [cc self];
我们知道在 <NSObject>
协议中声明有实例方法:- (instancetype)self;
,但是并没有 self
类方法。
那么调用类方法 self
为什么不会出错,将上面的代码转为 C 语言,如下:
id aa = ((id (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("self"));
id bb = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("self"));
Person *cc = self;
id dd = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)cc, sel_registerName("self"));
可见,类方法 self
的调用,实际是向 Class 类对象发送了一个 self
消息,使用下面的方法,获取 self
方法在实例与类中的实现。
Method method1 = class_getClassMethod(object_getClass(Person.class), @selector(self));
IMP imp1 = method_getImplementation(method1);
Method method2 = class_getClassMethod(object_getClass(self.class), @selector(self));
IMP imp2 = method_getImplementation(method2);
Method method3 = class_getClassMethod(object_getClass(self), @selector(self));
IMP imp3 = method_getImplementation(method3);
使用 po
命令,打印得到的结果均如下:
(libobjc.A.dylib`+[NSObject self])
Method method1 = class_getInstanceMethod(object_getClass(Person.class), @selector(self));
IMP imp1 = method_getImplementation(method1);
Method method2 = class_getInstanceMethod(object_getClass(self.class), @selector(self));
IMP imp2 = method_getImplementation(method2);
Method method3 = class_getInstanceMethod(object_getClass(self), @selector(self));
IMP imp3 = method_getImplementation(method3);
使用 po
命令,打印得到的结果均如下:
(lldb) po imp1
(libobjc.A.dylib`+[NSObject self])
(lldb) po imp2
(libobjc.A.dylib`+[NSObject self])
(lldb) po imp3
(libobjc.A.dylib`-[NSObject self])
从上面的结果来看,就明白为何将类对象称为元类的实例了。
self.class、Person.class、object_getClass(self) 获取的都是描述 Person 类的 Class 类对象,
其中包含了实例所包含的属性及方法。而 object_getClass(self.class)、object_getClass(Person.class) 得到的是描述 Class 类对象的 Class 元类对象,
其中包含了类属性及类方法。换言之,get_getClass() 函数总是获取参数的描述对象,而 class 方法,则只是返回调用者的类对象。
去 NSObject 中查找 self
方法,如下:
[self printClassMethod:object_getClass(NSObject.class)];
NSObject *object = [[NSObject alloc]init];
[self printClassMethod:object_getClass(object)];
- (void)printClassMethod:(Class)class {
unsigned int count;
Method *list = class_copyMethodList(class, &count);
NSLog(@"===count : %d===",count);
for (int i = 0; i < count; i++) {
SEL name = method_getName(*(list+i));
NSLog(@"%s",sel_getName(name));
}
}
此次测试打印的数量如下:
===count : 114===
===count : 355===
最终结果显示,NSObject 中是存在类方法 self
的,这也是 [Person self]
能够编译执行的原因。
实际 [Person self]
等同于 [Person class]
。
运行时的相关方法可以参考 iOS 运行时方法列表。