一、方法的存储位置
实验代码
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface AClass: NSObject
+ (void)classMethod;
- (void)instanceMethod;
@end
@implementation AClass
+ (void)classMethod {
}
- (void)instanceMethod {
}
@end
void log_getInstanceMethod(Class cls) {
const char *className = class_getName(cls);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getInstanceMethod(cls, @selector(instanceMethod));
Method method2 = class_getInstanceMethod(metaClass, @selector(instanceMethod));
Method method3 = class_getInstanceMethod(cls, @selector(classMethod));
Method method4 = class_getInstanceMethod(metaClass, @selector(classMethod));
NSLog(@"%s-%p-%p-%p-%p", __func__, method1, method2, method3, method4);
}
void log_getClassMethod(Class cls) {
const char *className = class_getName(cls);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getClassMethod(cls, @selector(instanceMethod));
Method method2 = class_getClassMethod(metaClass, @selector(instanceMethod));
Method method3 = class_getClassMethod(cls, @selector(classMethod));
Method method4 = class_getClassMethod(metaClass, @selector(classMethod));
NSLog(@"%s-%p-%p-%p-%p", __func__, method1, method2, method3, method4);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
Class aClass = [AClass class];
log_getInstanceMethod(aClass);
log_getClassMethod(aClass);
}
return 0;
}
// 输出
log_getInstanceMethod-0x1000020f8-0x0-0x0-0x100002090
log_getClassMethod-0x0-0x0-0x100002090-0x100002090
实例方法的存储位置
log_getInstanceMethod - 0x1000020f8 - 0x0 - 0x0 - 0x100002090
-instanceMethod 实例方法存储在类对象 cls 中
-classMethod 类方法(类对象的实例方法)存储在元类对象 metaClass 中。
class_getInstanceMethod 源码
Method class_getInstanceMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
#warning fixme build and search caches
// Search method lists, try method resolver, etc.
// 1.初始化类对象,以及类对象的方法索引
lookUpImpOrForward(nil, sel, cls, LOOKUP_RESOLVER);
#warning fixme build and search caches
return _class_getMethod(cls, sel);
}
static Method _class_getMethod(Class cls, SEL sel)
{
mutex_locker_t lock(runtimeLock);
return getMethod_nolock(cls, sel);
}
static method_t *
getMethod_nolock(Class cls, SEL sel)
{
method_t *m = nil;
runtimeLock.assertLocked();
// fixme nil cls?
// fixme nil sel?
ASSERT(cls->isRealized());
while (cls && ((m = getMethodNoSuper_nolock(cls, sel))) == nil) {
// 3.当前类查不到,就查父类,父类也没找到就停止
cls = cls->superclass;
}
return m;
}
static method_t *
getMethodNoSuper_nolock(Class cls, SEL sel)
{
runtimeLock.assertLocked();
ASSERT(cls->isRealized());
// fixme nil cls?
// fixme nil sel?
// 2.遍历匹配当前 cls 对象的 bits 中的 data()存储的方法
auto const methods = cls->data()->methods();
for (auto mlists = methods.beginLists(),
end = methods.endLists();
mlists != end;
++mlists)
{
// <rdar://problem/46904873> getMethodNoSuper_nolock is the hottest
// caller of search_method_list, inlining it turns
// getMethodNoSuper_nolock into a frame-less function and eliminates
// any store from this codepath.
method_t *m = search_method_list_inline(*mlists, sel);
if (m) return m;
}
return nil;
}
类方法的存储位置
log_getClassMethod - 0x0 - 0x0 - 0x100002090 - 0x100002090
- instanceMethod 实例方法用 class_getClassMethod 获取不到
- classMethod 从类对象 cls 和 元类对象 metaClass 都可以拿到,这是为什么呢?
上源码
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;
// 如果当前不是元类,就返回当前类对象的 isa
else return this->ISA();
}
从源码可知,cls 获取类方法时,其实是取 cls->isa 的存储实例方法,而 cls->isa 就是元类对象 metaClass。而使用 metaClass 获取类方法时,由于是元类,因此返回了他自己,获取它自己存储的实例方法。
二、类的判断逻辑
实验代码
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface AClass: NSObject
@end
@implementation AClass
@end
void log_classKindInfo() {
BOOL re1 = [[NSObject class] isKindOfClass:[NSObject class]];
BOOL re2 = [[NSObject class] isMemberOfClass:[NSObject class]];
BOOL re3 = [[AClass class] isKindOfClass:[AClass class]];
BOOL re4 = [[AClass class] isMemberOfClass:[AClass class]];
NSLog(@"\n re1: %hhd\n re2: %hhd\n re3: %hhd\n re4: %hhd\n", re1, re2, re3, re4);
BOOL re5 = [[NSObject alloc] isKindOfClass:[NSObject class]];
BOOL re6 = [[NSObject alloc] isMemberOfClass:[NSObject class]];
BOOL re7 = [[AClass alloc] isKindOfClass:[AClass class]];
BOOL re8 = [[AClass alloc] isMemberOfClass:[AClass class]];
NSLog(@"\n re5: %hhd\n re6: %hhd\n re7: %hhd\n re8: %hhd\n", re5, re6, re7, re8);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
log_classKindInfo();
}
return 0;
}
// 输出信息
re1: 1
re2: 0
re3: 0
re4: 0
re5: 1
re6: 1
re7: 1
re8: 1
分析 isMemberOfClass
+ (BOOL)isMemberOfClass:(Class)cls {
// 类对象查询,判断当前类对象的 isa 和 传进来的 cls 是否一致
return self->ISA() == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
// 实例对象查询,判断当前实例的 isa 和 传进来的 cls 是否一致
return [self class] == cls;
}
- (Class)class {
return object_getClass(self);
}
Class object_getClass(id obj)
{
// 获取当前实例的 isa
if (obj) return obj->getIsa();
else return Nil;
}
如下表:
| 对象 | 含义 | isa | 对比isa | 对比结果 |
|---|---|---|---|---|
| [NSObject class] | NSObject 的类对象 | NSObject 元类对象 | [NSObject class] | re2 = false |
| [AClass class] | AClass 的类对象 | AClass 元类对象 | [AClass class] | re4 = false |
| [NSObject alloc] | NSObject 的实例对象 | NSObject 类对象 | [NSObject class] | re6 = true |
| [AClass alloc] | AClass 的实例对象 | AClass 类对象 | [AClass class] | re6 = true |
分析 isKindOfClass
源码
// Calls [obj isKindOfClass]
BOOL
objc_opt_isKindOfClass(id obj, Class otherClass)
{
#if __OBJC2__
if (slowpath(!obj)) return NO;
// 获取当前对象的 isa
Class cls = obj->getIsa();
if (fastpath(!cls->hasCustomCore())) {
// 如果当前 isa 与目标类对象不同,则上溯父类查找
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);
}
如下表: isKindOfClass
| 对象 | 含义 | isa | 对比isa | 对比结果 |
|---|---|---|---|---|
| [NSObject class] | NSObject 的类对象 | NSObject 元类对象 | [NSObject class] | re1 = true |
| [AClass class] | AClass 的类对象 | AClass 元类对象 | [AClass class] | re3 = false |
| [NSObject alloc] | NSObject 的实例对象 | NSObject 类对象 | [NSObject class] | re5 = true |
| [AClass alloc] | AClass 的实例对象 | AClass 类对象 | [AClass class] | re7 = true |
问题出现了 re1 为什么是 true? 明明 [NSObject class] 的 isa 是 NSObject 的元类对象,为什么 [NSObject class] 相等?
其实并不是这两个相等,而是在 NSObject 根元类的 superClass 是 NSObject 类,因此这里 re1 为true。
注意
isMemberOfClass 只会查询当前对象的 isa,不会上溯父类
isKindOfClass 当前 isa 不匹配,会上溯父类查找
三、NSObject +(BOOL)isKindOfClass: 什么时候调用
在第二部分分析中,我们发现,NSObject 中实现的 +isKindOfClass 和 -isKindOfClass 并没有调用,而是优化为了 objc_opt_isKindOfClass 函数调用。那么有没有办法能调用到呢?
如下代码,可以调用到 +isKindOfClass 方法,但是只调用了一次,第二次调用没有执行 NSObject 定义到 +isKindOfClass 方法
[AClass isKindOfClass:[NSObject class]];
[AClass isKindOfClass:[NSObject class]];
为什么呢?我们断点调试以下发现:
即 cls->hasCustomCore() == true 而第二次 cls->hasCustomCore() == false
上源码追踪
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);
}
bool hasCustomCore() const {
// 此处取反
return !cache.getBit(FAST_CACHE_HAS_DEFAULT_CORE);
}
void setHasDefaultCore() {
return cache.setBit(FAST_CACHE_HAS_DEFAULT_CORE);
}
void setHasCustomCore() {
return cache.clearBit(FAST_CACHE_HAS_DEFAULT_CORE);
}
bool getBit(uint16_t flags) const {
return _flags & flags;
}
由此我们可以判断,第一次的时候 _flags 不包含 FAST_CACHE_HAS_DEFAULT_CORE;第二次调用的时候,_flags 已经包含了 FAST_CACHE_HAS_DEFAULT_CORE。
我们在 setHasDefaultCore 和 setHasCustomCore 打上断点,看什么时候赋值了。
由此可知,在调用 objs_msgSend 的时候, 调用到了 lookUpImpOrForward 函数。
而且在 lookUpImpOrForward 函数,触发了 initializeAndLeaveLocked 函数,原因是 !cls->isInitialized() !!!
破案了,因为当前的 AClass 类对象未初始化,所以 _flags 为空,进而导致 cls->hasCustomCore() == true。
而类初始化之后,就不满足此条件了。
注意 [AClass class] 和 [AClass alloc] 都会触发 AClass 对象的初始化方法。