本篇文章将从几个面试题出发,探究方法的归属以及isa与Superclass。
objc_object 与对象的关系,objc_object 与 NSObject的关系
- 所有
对象在底层都是以objc_object为模版继承来的。 - 所有
对象都是继承自NSObject(根类),而在底层中NSObject是一个objc_object(C/C++)结构体。
所以结论:objc_object与对象之间是继承关系。
属性、成员变量、实例变量
-
属性:带下划线的成员变量 +setter+getter,以@property开头定义的变量 -
成员变量:定义在类.h文件的{}中的,不带下划线的变量 -
实例变量:特殊的成员变量,经过实例化的成员变量对象,例如UIButton、UILabel等 -
成员变量中除去基本数据类型及{}中定义的NSString类型变量,剩下的都是实例化后的实例变量,实例变量可理解为是拥有属性的对象。
方法的归属分析
下面通过一个例子来探究实例方法及类方法的归属问题:
#import <Foundation/Foundation.h>
@interface LGPerson : NSObject
- (void)sayHello;
+ (void)sayHappy;
@end
#import "LGPerson.h"
@implementation LGPerson
- (void)sayHello{
NSLog(@"LGPerson say : Hello!!!");
}
+ (void)sayHappy{
NSLog(@"LGPerson say : Happy!!!");
}
@end
定义继承于NSObject的LGPerson类,并添加一个实例方法sayHello,一个类方法sayHappy,下面通过几个打印情况来分析:
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
// 0x0000000100000000
// LGTeacher *teacher = [LGTeacher alloc];
LGPerson *person = [LGPerson alloc];
Class pClass = object_getClass(person);
lgObjc_copyMethodList(pClass);
lgInstanceMethod_classToMetaclass(pClass);
lgClassMethod_classToMetaclass(pClass);
lgIMP_classToMetaclass(pClass);
NSLog(@"Hello, World!");
}
return 0;
}
方法从上而下的定义如下面:
#ifdef DEBUG
#define LGLog(format, ...) printf("%s\n", [[NSString stringWithFormat:format, ## __VA_ARGS__] UTF8String]);
#else
#define LGLog(format, ...);
#endif
void lgObjc_copyMethodList(Class pClass){
unsigned int count = 0;
Method *methods = class_copyMethodList(pClass, &count);
for (unsigned int i=0; i < count; i++) {
Method const method = methods[i];
//获取方法名
NSString *key = NSStringFromSelector(method_getName(method));
LGLog(@"Method, name: %@", key);
}
free(methods);
}
void lgInstanceMethod_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));
Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));
Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));
LGLog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
void lgClassMethod_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getClassMethod(pClass, @selector(sayHello));
Method method2 = class_getClassMethod(metaClass, @selector(sayHello));
Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
// 元类 为什么有 sayHappy 类方法 0 1
//
Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
LGLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
void lgIMP_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
// - (void)sayHello;
// + (void)sayHappy;
IMP imp1 = class_getMethodImplementation(pClass, @selector(sayHello));
IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayHello));
IMP imp3 = class_getMethodImplementation(pClass, @selector(sayHappy));
IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayHappy));
NSLog(@"%p-%p-%p-%p",imp1,imp2,imp3,imp4);
NSLog(@"%s",__func__);
}
下面我们先给出打印结果,然后逐个分析:

lgObjc_copyMethodList
object_getClass 获取当前实例对象person的类对象。
此方法参数为Class类型的pClass,一个类对象,class_copyMethodList 获取当前类对象中的方法列表,遍历此类对象所有的实例方法,根据打印情况只打印sayHello实例方法,可以验证类对象里面存放实例方法,类方法不存放在类对象中。
lgInstanceMethod_classToMetaclass
方法中的metaClass为当前方法参数类对象pClass的元类,由objc_getMetaClass 根据类获取到元类,class_getInstanceMethod 获取实例方法的源码分析后可知,在传入类及传入类的父类一级一级找下去直到找到返回,没找到返回null,下面分别分析几个打印情况:
- method1:
0x1000031b0
因为
sayHello是实例方法,当前pClass刚好也是存放sayHello的LGPerson类,所以能找到方法并打印
- method2:
0x0
因为
metaClass为元类,元类(LGPerson)--->根元类(NSObject)--->NSObject--->nil,发现找不到当前的sayHello方法,说明返回null,打印为0x0
- method3:
0x0
当前需要找方法
sayHappy,类(LGPerson)--->根类(NSObject)--->nil,没找到sayHappy方法,所以返回null,打印0x0
- method4:
0x100003148
寻找类方法
sayHappy,metaClass为当前类的元类,类方法存在类的元类中,所以此时在metaClass中就找到sayHppy方法,即可返回并打印0x100003148
lgClassMethod_classToMetaclass
跟上面一样,方法参数pClass类对象,metaClass为当前类的元类,class_getClassMethod 获取类方法源代码如下:
Method class_getClassMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
return class_getInstanceMethod(cls->getMeta(), sel);
}
Class getMeta() {
if (isMetaClass()) return (Class)this;
else return this->ISA();
}
class_getClassMethod 源码可见,获取类的类方法就是去获取当前类的元类的实例方法,getMeta()就是返回当前类的元类(如果getMeta()方法当前调用者为元类时,直接返回自己,不是的话就返回当前调用者的元类),下面分别分析打印情况:
- method1:
0x0
sayhello实例方法,class_getClassMethod当前传入为类,不是元类,拿到元类,然后class_getInstanceMethod方法寻找,元类--->根元类--->NSObject--->nil,没找到sayHello,返回null,打印0x0
- method2:
0x0
class_getClassMethod当前传入为metaClass元类,直接进行class_getInstanceMethod方法查找当前元类--->根元类--->NSObject--->nil,没找到实例方法sayHello方法,所以返回null,打印0x0
- method3:
0x100003148
当前查找方法为类方法
sayHappy,在pClass类中寻找,所以class_getClassMethod中要拿到元类,然后进行查找,发现在元类中找到类方法sayHappy,所以能打印方法地址0x100003148
- method4:
0x100003148
class_getClassMethod传入即为类的元类,所以直接进行class_getInstanceMethod方法查找,在当前元类找到了sayHappy类方法,所以返回找到的方法
lgIMP_classToMetaclass
class_getMethodImplementation 返回方法的具体实现:
IMP class_getMethodImplementation(Class cls, SEL sel)
{
IMP imp;
if (!cls || !sel) return nil;
imp = lookUpImpOrNil(nil, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER);
// Translate forwarding function to C-callable external version
if (!imp) {
return _objc_msgForward;
}
return imp;
}
如果在当前类对象里没找到对应的方法实现,就会触发底层的消息转发,所以打印结果是四个都有值,而不是0x0
- method1:
0x100001d10
sayHello实例方法,在当前pClass类中能找到当前方法,所以method1返回的是sayHello方法地址指针
- method2:
0x7fff6cd17580
在
metaClass元类中寻找实例方法sayHello,是找不到的,所以进行了底层的消息转发,返回的不是sayHello的实现地址
- method3:
0x7fff6cd17580
sayHappy类方法,因为类方法是存储在元类里的,所以在类pClass中找不到sayHappy方法实现地址,所以触发了底层消息转发
- method4:
0x100001d40
在
metaClass元类 中找到了sayHappy方法实现,所以返回的就是真正的sayHappy实现地址
isKindOfClass & isMemberOfClass
同样,利用上面的类LGPerson进行下面探究:
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]]; //
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]]; //
BOOL re3 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]]; //
BOOL re4 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]]; //
NSLog(@" re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4);
BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]]; //
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]]; //
BOOL re7 = [(id)[LGPerson alloc] isKindOfClass:[LGPerson class]]; //
BOOL re8 = [(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]]; //
NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);
}
return 0;
}

首先理清一下isKindOfClass 方法及isMemberOfClass 方法:
-
isKindOfClass方法:+ (BOOL)isKindOfClass:(Class)cls { // 类 vs 元类 // 根元类 vs NSObject // NSObject vs NSObject // LGPerson vs 元类 (根元类) (NSObject) for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) { if (tcls == cls) return YES; } return NO; }
源码所示,类方法- (BOOL)isKindOfClass:(Class)cls { for (Class tcls = [self class]; tcls; tcls = tcls->superclass) { if (tcls == cls) return YES; } return NO; }+isKindOfClass比较线路:元类--->根元类--->NSObject--->nil与当前cls类比较。实例方法-isKindOfClass比较路线:对象的类--->父类--->根类--->nil与cls比较。
注意(有坑!!!)
你以为就是这样了?当然不是,isKindOfClass 方法在llvm编译器在编译期做了编译优化,isKindOfClass 在底层走的方法是objc_opt_isKindOfClass ,打开源码,搜索objc_opt_isKindOfClass 打断点调试,发现调用isKindOfClass类方法及实例方法,走的都是这个方法:
// Calls [obj isKindOfClass]
BOOL
objc_opt_isKindOfClass(id obj, Class otherClass)
{
#if __OBJC2__
if (slowpath(!obj)) return NO;
Class cls = obj->getIsa();
if (fastpath(!cls->hasCustomCore())) {
for (Class tcls = cls; tcls; tcls = tcls->superclass) {
if (tcls == otherClass) return YES;
}
return NO;
}
#endif
return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
}
可见,无论是实例方法还是类方法调用,比较的对象链:元类 ---> 根元类 ---> NSObject ---> nil ,也就是说isKindOfClass 就是遵循isa走向的规则
- isMemberOfClass方法:
+ (BOOL)isMemberOfClass:(Class)cls { return self->ISA() == cls; }
类方法- (BOOL)isMemberOfClass:(Class)cls { return [self class] == cls; }+isMemberOfClass判断的是当前类是否是元类,实例方法-isMemberOfClass判断是否是当前对象的类。
类方法:re1、re2、re3、re4
实例方法:re5、re6、re7、re8
-
re1:1
首先拿到前者
NSObject的元类(根元类),与后者NSObject类比较,不相等,找根元类的父类,也就是NSObject类,比较结果为相等,所以返回YES,打印1
-
re2:0
NSObject元类与NSObject类当然不相等,所以返回NO
-
re3:0
LGPerson类 与LGPerson元类不想等,与LGPerson根元类不想等,与NSObject类不想等,与nil不想等,所以返回NO
-
re4:0
LGPerson类与LGPerson元类不相等,返回NO
-
re5:1
NSObject实例对象的类NSObject当然与NSObject类相等,返回YES
-
re6:1
NSObject实例对象的类NSObject当然与NSObject类相等
-
re7:1
LGPerson实例对象的类LGPerson与LGPerson类相等
-
re8:1
LGPerson实例对象的类LGPerson与LGPerson类相等