一、方法探索
先看一段代码,Person类中定义了sayHello及sayHappy方法;
{
NSObject *objc;
NSString *nickName;
}
@property (nonatomic, copy) NSString *name;
@property (nonatomic, strong) NSObject *obj;
- (void)sayHello;
+ (void)sayHappy;
@end
/// main.m
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));
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);
IMP imp1 = class_getMethodImplementation(pClass, @selector(sayHello));
IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayHello));// 0
// sel -> imp 方法的查找流程 imp_farw
IMP imp3 = class_getMethodImplementation(pClass, @selector(sayHappy)); // 0
IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayHappy));
LGLog(@"%s %p-%p-%p-%p",__func__,imp1,imp2,imp3,imp4);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
// LGTeacher *teacher = [LGTeacher alloc];
LGPerson *person = [LGPerson alloc];
Class pClass = object_getClass(person);
lgObjc_copyMethodList(pClass);
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
NSLog(@"*************");
lgObjc_copyMethodList(metaClass);
//
lgInstanceMethod_classToMetaclass(pClass);
lgClassMethod_classToMetaclass(pClass);
lgIMP_classToMetaclass(pClass);
NSLog(@"Hello, World!");
}
return 0;
}
打印结果
Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
从打印结果,我们会发现method3、method4都有值,而且都是一样的值0x100003d61;
引出一个问题:类方法跟元类方法一样?元类里面存在类方法?
为了论证这一问题,需要进一步研究class_getClassMethod在底层的实现;
可以看到当我们获取类的类方法的时候,等于获取类的元类(cls->getMeta)的对象方法;即类方法在元类里面以一种对象方法形式存在;
直观结论:类.类方法 = 类的元类.对象方法
Method class_getClassMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
// 类.类方法 = 类的元类.对象方法 - + -> 方法 - 消息 OC 唯一
// cls->getMeta() 元类.对象
return class_getInstanceMethod(cls->getMeta(), sel);
}
// NOT identical to this->ISA when this is a metaclass
Class getMeta() {
if (isMetaClassMaybeUnrealized()) return (Class)this;
else return this->ISA();
}
从这一点可以反向论证一点,在所有的OC中,底层根本就没有-或+方法,方法在底层本质都是消息,只是-方法存储在类里面,而+存储在元类里面,这也可以说明Apple为什么要设计元类,为了把对象方法跟类方法区分开存储;
回到之前的问题:类的元类会有类方法吗?即method4是否存在?
答:在cls->getMeta中我们可以发现有一个判断,如果当前的类是元类的时候,返回的是本身,否则通过ISA去查找它的元类;即pClass、metaClass返回的都是metaClass,method3和method4是一个的意思,都是打印的元类的对象方法;
而之所以要设计类方法,主要是为了节省内存,对象方法每次调用都需要生成对象,开辟内存;
类方法跟静态方法有什么不同?
个人认为有以下几点:
一是为了保证命名冲突问题,static静态方法可以在不同文件中声明一样的方法,会导致冲突,而类方法则不会;
二是静态方法在启动的时候会被提前加载,而类方法则不一样,依靠iOS的懒加载机制,在使用的时候才会被加载进内存,节约启动运行时间;
二、方法实现探索
打印方法实现,会发现比较让人困惑的事情,一是元类里面有对象方法sayHello的实现,二是对象同样也有类方法sayHappy的实现,而且这两个值地址都是0x1a5a01d20;
方法实现IMP
方法调用流程
上面有说到未被定义的方法,当我们获取它的IMP指针的时候,也会返回有值,这主要跟消息的查找有关系;
image.png
方法 -> 消息查找 -> 找不到 -> 消息转发
之前有探索过写过一篇关于深入探究cache_t的,里面有提到cache_t方法的插入流程,但是没有说在哪里调用cache_t::insert,接下来我们就探究一下,方法是何时插入我们的缓存的;
- Cache readers (PC-checked by collecting_in_critical())
objc_msgSend*
cache_getImp
// Cache readers/writers (hold cacheUpdateLock during access; not PC-checked)
cache_t::copyCacheNolock (caller must hold the lock)
cache_t::eraseNolock (caller must hold the lock)
cache_t::collectNolock (caller must hold the lock)
cache_t::insert (acquires lock)
cache_t::destroy (acquires lock)
在objc-cache.mm里面找到一些注释,里面有提到一个方法的调用顺序,在cache_t结构体方法调用之前有提到两个方法objc_msgSend、cache_getImp,接下来就看看这俩方法是干嘛的;
三、方法本质
首先看一个简单的方法调用
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGPerson *p = [[LGPerson alloc] init];
[p sayHello];
NSLog(@"%@",p);
}
return 0;
}
通过clang -rewrite-objc main.m得到编译后的问题,main函数如下:
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
LGPerson *p = ((LGPerson *(*)(id, SEL))(void *)objc_msgSend)((id)((LGPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LGPerson"), sel_registerName("alloc")), sel_registerName("init"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)p, sel_registerName("sayHello"));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_1j_qwn3218x4x7037_0c5kty2cr0000gn_T_main_3eef07_mi_0,p);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_1j_qwn3218x4x7037_0c5kty2cr0000gn_T_main_3eef07_mi_1);
}
return 0;
}










网友评论