oc中方法存在于类中,类方法存在于元类中
class_getInstanceMethod
//cls 类
Method class_getInstanceMethod(Class _Nullable cls, SEL _Nonnull name)
意思是这个
class_getInstanceMethod
会返回类的实例方法
问题1
LGPerson类
@interface LGPerson : NSObject
- (void)sayHello;
+ (void)sayHappy;
@end
@implementation LGPerson
- (void)sayHello{
NSLog(@"LGPerson say : Hello!!!");
}
+ (void)sayHappy{
NSLog(@"LGPerson say : Happy!!!");
}
@end
main.m
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);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGPerson *person = [LGPerson alloc];
Class pClass = object_getClass(person);
lgInstanceMethod_classToMetaclass(pClass);
NSLog(@"Hello, World!");
}
return 0;
}
问:输出情况?
-
sayHello
是实例方法,sayHappy
是类方法。 -
pClass
是类,metaClass
是元类
根据原则oc中实例方法存在与类中,类方法存在与元类中
大致可以推断出:
sayHello
是实例方法在LGPerson类中,所以
- method1:打印出sayHello的内存地址,
- method2:NULL
sayHappy
是累方法,在LGPerson的元类中 - method3:输出是0x00即NULL
- method4:输出是sayHappy的内存地址
class_getClassMethod
问题2
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));
Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
LGLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
问:输出情况?
-
sayHello
是实例方法,sayHappy
是类方法。 -
pClass
是类,metaClass
是元类
根据原则oc中实例方法存在与类中,类方法存在与元类中
输出情况
- method1:
0x00
即nil - method2:
0x00
即nil - method3:
0x100008148
- method4:
0x100008148
sayHello是实例方法,所以class_getClassMethod
不论怎么查询都是nil
sayHappy是类方法,类方法存在于元类中,但是method3和method4都打印了地址,, 这是为什么呢?
可以查看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();
}
//获取isa
inline Class
objc_object::ISA()
{
ASSERT(!isTaggedPointer());
#if SUPPORT_INDEXED_ISA
if (isa.nonpointer) {
uintptr_t slot = isa.indexcls;
return classForIndex((unsigned)slot);
}
return (Class)isa.bits;
#else
return (Class)(isa.bits & ISA_MASK);
#endif
}
通过查看源码发现
class_getClassMethod
本质还是调用class_getInstanceMethod
,在传入的第一个参数的时候传入的是Class的元类cls->getMeta()
。
所以method3自然就能打印出来
在getMeta()会判断当前的class是否为元类,如果是就返回本身。
所以method4自身也能打印出来。
isKindOfClass和isMemberOfClass
isKindOfClass分别有类方法和实例方法
- isKindOfClass:是给定类的实例还是从该类继承的任何类的实例
- isMemberOfClass:是否给定类的实例
问:
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);
+isKindOfClass 和 - isKindOfClass的区别,源码
//类方法
+ (BOOL)isKindOfClass:(Class)cls {
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;
}
通过源码结构isa的走位图
类方法是获取当前类的元类,不断获取元类父类,与传入的类型进行比较
实例方法是获取当前的类,不断获取父类,与传入的类型进行比较。
实际代码运行并没有调用上述代码,而是通过llvm优化后调用如下方法:
BOOL
objc_opt_isKindOfClass(id obj, Class otherClass)
{
#if __OBJC2__
if (slowpath(!obj)) return NO;
//obj如果是实例对象, obj->getIsa()获取的是类信息
//obj如果是类对象, obj->getIsa()获取的是元类信息
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);
}
id:typedef struct objc_object *id;
id是任意类型的objc_object
Class:class是objc_class类型,而objc_class是继承objc_object的。
struct objc_class : objc_object {}
typedef struct objc_class *Class;
所以objc_opt_isKindOfClass的第一个参数obj
- obj如果是实例对象, obj->getIsa()获取的是类class
- obj如果是类对象, obj->getIsa()获取的是元类MetaClass
解析1:
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
objc_opt_isKindOfClass的源码分析
传入的参数是[NSObject class],通过obj->getIsa()
获取到是NSObject的根元类,所以在第一次for循环的时候,cls是根元类与otherClass根类想比较,则第一次比较不相等。
第二次for循环cls获取其父类,根据isa的走位,根元类的父类是NSObject类,所以第二次循环相等。
Class cls = obj->getIsa();//根元类,根元类的父类是NSObject
if (fastpath(!cls->hasCustomCore())) {
for (Class tcls = cls; tcls; tcls = tcls->superclass) {
if (tcls == otherClass) return YES;
}
return NO;
}
所以输出YES
解析2
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
isMemberOfClass是判断一个对象是否给定类的实例
所以返回NO
解析3
BOOL re3 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]];
objc_opt_isKindOfClass在根据其源码和isa走位图。
传入的obj是LGPerson的类class,经过obj->getIsa()获取LGPerson元类metaclass,而otherclass是LGPerson的类class,metaclass与class比较所以第一次循环不相等。metaclass获取其父类,因此与LGPerson的类class相比不相等。所以不断循环后不相等
所以返回NO
解析4
BOOL re4 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]];
返回NO
解析5-7
objc_opt_isKindOfClass传入的第一个参数是个对象,经过objc->getIsa()获取的是类class,class与otherclass相比较是相等。
返回yes
解析6-8
返回yes
2020-09-30 17:50:06.470167+0800 KCObjc[80323:9507443] re1 :1
re2 :0
re3 :0
re4 :0
2020-09-30 17:50:06.471930+0800 KCObjc[80323:9507443] re5 :1
re6 :1
re7 :1
re8 :1