实例方法和类方法的分析
@interface LGPerson : NSObject
@property (nonatomic, copy) NSString *name;
-(void)myInstanceMethod;
+(void)myClassMethod;
@end
@implementation LGPerson
+(void)myClassMethod {
}
-(void)myInstanceMethod {
}
@end
@interface LGTeacher : LGPerson
-(void)myTeacherInstanceMethod;
+(void)myTeacherClassMethod;
@end
@implementation LGTeacher
-(void)myTeacherInstanceMethod {
}
+(void)myTeacherClassMethod {
}
@end
我们通过class的c++实现objc_class,在进行内存平移访问,得出LGPerson的方法共4个方法分别如下
(lldb) p $6.get(0)
(method_t) $7 = {
name = "myInstanceMethod"
types = 0x0000000100000f82 "v16@0:8"
imp = 0x0000000100000dc0 (KCObjc`-[LGPerson myInstanceMethod])
}
(lldb) p $6.get(1)
(method_t) $8 = {
name = ".cxx_destruct"
types = 0x0000000100000f82 "v16@0:8"
imp = 0x0000000100000e30 (KCObjc`-[LGPerson .cxx_destruct])
}
(lldb) p $6.get(2)
(method_t) $9 = {
name = "name"
types = 0x0000000100000f96 "@16@0:8"
imp = 0x0000000100000dd0 (KCObjc`-[LGPerson name])
}
(lldb) p $6.get(3)
(method_t) $10 = {
name = "setName:"
types = 0x0000000100000f9e "v24@0:8@16"
imp = 0x0000000100000e00 (KCObjc`-[LGPerson setName:])
}
我们没看到类方法我们猜测类方法可能在元类中,
(lldb) p $16.list
(method_list_t *const) $17 = 0x0000000100002088
(lldb) p *$17
(method_list_t) $18 = {
entsize_list_tt<method_t, method_list_t, 3> = {
entsizeAndFlags = 26
count = 1
first = {
name = "myClassMethod"
types = 0x0000000100000f82 "v16@0:8"
imp = 0x0000000100000db0 (KCObjc`+[LGPerson myClassMethod])
}
}
}
(lldb) p $18.get(0)
(method_t) $19 = {
name = "myClassMethod"
types = 0x0000000100000f82 "v16@0:8"
imp = 0x0000000100000db0 (KCObjc`+[LGPerson myClassMethod])
}
具体寻址过程,可以参考类结构分析
class_getInstanceMethod
从源码看class_getInstanceMethod的方法执行过程
class_getInstanceMethod===>_class_getMethod()===>getMethod_nolock()
instanceMethod.png
LogInstanceMethod_classAndMetaClass([LGTeacher class]);
void LogInstanceMethod_classAndMetaClass(Class pClass) {
const char * className =class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getInstanceMethod(pClass, @selector(myTeacherInstanceMethod));
Method method2 = class_getInstanceMethod(pClass, @selector(myTeacherClassMethod));
Method method3 = class_getInstanceMethod(pClass, @selector(myInstanceMethod));
Method method4 = class_getInstanceMethod(pClass, @selector(myClassMethod));
Method method5 = class_getInstanceMethod(metaClass, @selector(myTeacherInstanceMethod));
Method method6 = class_getInstanceMethod(metaClass, @selector(myTeacherClassMethod));
Method method7 = class_getInstanceMethod(metaClass, @selector(myInstanceMethod));
Method method8 = class_getInstanceMethod(metaClass, @selector(myClassMethod));
NSLog(@"\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p",method1,method2,method3,method4,method5,method6,method7,method8);
}
打印结果
0x100002110
0x0
0x1000021e0
0x0
0x0
0x1000020a8
0x0
0x100002178
class_getInstanceMethod方法总结
LGTeacher.class
@selector(myTeacherInstanceMethod):自己的实例方法,所以有值
@selector(myTeacherClassMethod):自己的类方法,返回为空
@selector(myInstanceMethod):父类的实例方法,因为class_getInstanceMethod会遍历LGTeacher的父类,所以有返回值
@selector(myClassMethod):父类的类方法,所以返回有值
LGTeacher的元类分析,LGTeacher的元类的实例方法也就是LGTeacher类的类方法
@selector(myTeacherInstanceMethod):元类的实例方法是本类的类方法,所以返回为空
@selector(myTeacherClassMethod):本类的类方法就是本元类的实例方法,所以返回有值
@selector(myInstanceMethod):本类的实例方法,不在本元类的实例方法中,所以为空
@selector(myClassMethod):通过流程图可知,本类的实例方法还包括父类的实例方法,所以父类的类方法也是本元类的实例方法(会通过继承树寻找)
class_getClassMethod
我们先看一下源码
Method class_getClassMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
return class_getInstanceMethod(cls->getMeta(), sel);
}
//cls->getMeta()
Class getMeta() {
if (isMetaClass()) return (Class)this;
else return this->ISA();
}
class_getClassMethod实质是让元类调用实例方法。先拿到传来类的元类,如果传来的类本来是元类,直接return
我们根据class_getInstanceMethod可知,class_getClassMethod是取类方法和父类的类方法,如果cls是元类,也会取到类方法和本类的父类类方法
验证
void logClassMethod_classAndMetaClass(Class pClass) {
const char * className =class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getClassMethod(pClass, @selector(myTeacherInstanceMethod));
Method method2 = class_getClassMethod(pClass, @selector(myTeacherClassMethod));
Method method3 = class_getClassMethod(pClass, @selector(myInstanceMethod));
Method method4 = class_getClassMethod(pClass, @selector(myClassMethod));
Method method5 = class_getClassMethod(metaClass, @selector(myTeacherInstanceMethod));
Method method6 = class_getClassMethod(metaClass, @selector(myTeacherClassMethod));
Method method7 = class_getClassMethod(metaClass, @selector(myInstanceMethod));
Method method8 = class_getClassMethod(metaClass, @selector(myClassMethod));
NSLog(@"\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p",method1,method2,method3,method4,method5,method6,method7,method8);
}
打印日志
0x0 //类的实例方法为空
0x1000020b0 //返回类的类方法
0x0 //父类的实例方法为空
0x100002180//返回父类的类方法
0x0 //类的实例方法为空
0x1000020b0//返回类的类方法
0x0 //父类的实例方法为空
0x100002180//返回父类的类方法
*** 类和元类调用class_getClassMethod,效果相同,因为class_getClassMethod的底层是调用元类的class_getInstanceMethod,在从类得到元类中有一个判断如果传入的类为元类直接返回,如果类不是元类,则通过isa取到元类***
isKindOfClass和isMemberOfClass面试题
isKindOfClass源码分析,通过探索底层的方法,我们在源码中定位到objc_opt_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);
}
函数分析,通过isa寻找类或者元类,如果obj是通过类初始化的对象,则cls是本类,如果obj本来是类,则cls是本类的元类。遍历父类和otherClass对比。
下面一个实例说明
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]]; //
BOOL re2 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]]; //
BOOL re3 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]];
BOOL re4 = [(id)[LGPerson alloc] isKindOfClass:[LGPerson class]]; //
BOOL re5 = [(id)[LGTeacher alloc] isKindOfClass:[LGPerson class]];
NSLog(@" \nre1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\nre5 :%hhd\n",re1,re2,re3,re4,re5);
打印结果
re1 :1
re2 :1
re3 :0
re4 :1
re5 :1
isa流程图.png
1 . [(id)[NSObject class] isKindOfClass:[NSObject class]]; //
通过上面源码可知,(id)[NSObject class]在内部实现中被转化成NSObject元类并沿着NSObject元类继承树和NSObject对比,我们知道NSObject的元类的父类是NSObject所以返回为true
2 . [(id)[NSObject alloc] isKindOfClass:[NSObject class]]; //
[NSObject alloc]被转化成NSObject类,NSObject类和[NSObject class]相同所以返回为true
- [(id)[LGPerson class] isKindOfClass:[LGPerson class]];
LGPerson的元类和LGPerson元类的父类是否等于LGPerson,我们从上图中可知不想等 - [(id)[LGPerson alloc] isKindOfClass:[LGPerson class]]; //
[LGPerson alloc]被转化成LGPerson类和LGPerson类比较相同,所以返回是true - [(id)[LGTeacher alloc] isKindOfClass:[LGPerson class]];
[LGTeacher alloc]被转化成LGTeacher类,LGTeacher类和LGTeacher类的父类同LGPerson对比,可知LGTeacher的父类就是LGPerson,所以返回是true/
isMemberOfClass源码分析
isMemberOfClass底层实现有两个,一个类方法一个实例方法
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
类方法是元类和cls做对比
实例方法是通过[self class]取到对象所属的类,然后做对比。
下面示例
BOOL re6 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL re7 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];
BOOL re8 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]]; //
BOOL re9 = [(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]];
BOOL re10 = [(id)[LGTeacher alloc] isMemberOfClass:[LGPerson class]];
Class metaPerson = objc_getMetaClass("LGPerson");
BOOL re11 = [(id)[LGPerson class] isMemberOfClass:metaPerson]; //
NSLog(@" \nre6 :%hhd\n re7 :%hhd\n re8 :%hhd\n re9 :%hhd\nre10 :%hhd\n",re6,re7,re8,re9,re10);
打印结果
re6 :0
re7 :1
re8 :0
re9 :1
re10 :0
re11 :1
- [(id)[NSObject class] isMemberOfClass:[NSObject class]]
会调用底层实现的类方法,取NSObject的元类和NSObject做对比,所以返回是false - [(id)[NSObject alloc] isMemberOfClass:[NSObject class]]
调用底层的实例方法,拿到[NSObject alloc] 对象的类NSObject,然后和NSObject对比返回是true - [(id)[LGPerson class] isMemberOfClass:[LGPerson class]]
调用底层的类方法,LGPerson的元类和LGPerson对比返回是false - [(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]]
调用底层的实例方法,LGPerson类和LGPerson类做对比返回是true - [(id)[LGTeacher alloc] isMemberOfClass:[LGPerson class]]
调用底层的实例方法,LGTeacher类和LGPerson对对比,返回是false。 - [(id)[LGPerson class] isMemberOfClass:metaPerson]
调用底层的类方法,LGPerson的元类和LGPerson的元类对比返回时true。
总结
isKindOfClass和isMemberOfClass
两个都分为类调用或者对象调用,类调用是类的元类做判断,对象调用的是类做判断,另外isKindOfClass会沿着继承树和后面的类做对比,而isMemberOfClass只有本类(或者元类)做对比
class_getInstanceMethod和class_getClassMethod
元类的实例方法就是类的类方法
class_getInstanceMethod如果是类调用,类及父类的实例方法方法返回为YES,如果是元类调用的,类的类方法及父类的类方法返回YES。
class_getClassMethod如果是类调用,类及父类的类方法返回为YES,如果是元类调用,类的类方法和父类的类方返回是YES.因为class_getClassMethod中调用的是class_getInstanceMethod,传入的类是cls->getMeta(),getMeta会取cls的元类,如果cls本来就是元类会直接返回cls。
网友评论