当先锋百科网

首页 1 2 3 4 5 6 7

方法的底层原理

我们都知道OC是一门动态的语言,runtime是指我们的程序在运行的过程中可以对于我们的类、对象、属性、方法、变量进行修改,可以改变他们的数据结构,可以添加或者删除一些函数,可以去改变一些变量的值等等的操作.runtime就是可以实现语言动态的一组api,runtime所有都是围绕两个核心
1:类的各方面的动态配置(可以详细看各种的API)
2:消息传递(消息的发送和消息的转发,消息的发送就是runtime通过sel找imp,然后实现对应的方法)

本文就是来看看消息发送objc_msgSend方法,具过程:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        LGPerson *p = [LGPerson alloc];
        [p study];
        [p happy];
        
    }
    return NSApplicationMain(argc, argv);
}

然后我们cd到项目根目录下,输入代码clang -rewrite-objc main.m , 将上面代码输出为.cpp文件。
然后可以看到上面代码转换成:

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
            LGPerson *p = ((LGPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LGPerson"), sel_registerName("alloc"));
            ((void (*)(id, SEL))(void *)objc_msgSend)((id)p, sel_registerName("study"));
            ((void (*)(id, SEL))(void *)objc_msgSend)((id)p, sel_registerName("happy"));
    }
    return NSApplicationMain(argc, argv);
}

我们发现,alloc这种类方法和study、happy这种对象方法都被包装成了((void (*)(id, SEL))(void *)objc_msgSend)(receiver, sel)格式.
当我们的方法都没有参数,可转换成objc_msgSend后都会带有两个默认的参数消息的接收者receiver、消息的方法名sel.
类方法的接收者是类对象,他就会通过类对象的isa指针找到我们的元类,从元类中找对应的方法;
实例对象方法的接收者是实例对象,他就会通过实例对象的isa指针找到我们的类对象,从类对象中找对应的方法.
当我们的方法有参数时,我们的参数会放在objc_msgSend的第三个参数及以后中。
加上参数和返回值后如下:
请添加图片描述

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        LGPerson *p = ((LGPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LGPerson"), sel_registerName("alloc"));
        ((void (*)(id, SEL))(void *)objc_msgSend)((id)p, sel_registerName("study"));
        ((void (*)(id, SEL))(void *)objc_msgSend)((id)p, sel_registerName("happy"));
        ((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)p, sel_registerName("say:"), (NSString *)&__NSConstantStringImpl__var_folders_kk_qj63rdg529qgrqh6k3c_cz2r0000gn_T_main_a8c8ed_mi_5);
        ((BOOL (*)(id, SEL))(void *)objc_msgSend)((id)p, sel_registerName("hadEat"));
    }
    return NSApplicationMain(argc, argv);
}

在这里插入图片描述
这里可以看到.cpp文件下,繁多的msgSend方法,正是这些方法够足了消息发送的多样性。

objc_msgSend 和 objc_msgSendSuper 的区别

- (instancetype)init {
    if (self = [super init]) {
        NSLog(@"%@", [self class]);
        NSLog(@"%@", [super class]);
    }
    return self;
}

.cpp下

static instancetype _I_WTStudent_init(WTStudent * self, SEL _cmd) {
    if (self = ((WTStudent *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("WTStudent"))}, sel_registerName("init"))) {
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_x6_75ftxbbd4t97nl_2t2sh7kww0000gn_T_WTStudent_8d6cff_mi_0, ((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class")));
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_x6_75ftxbbd4t97nl_2t2sh7kww0000gn_T_WTStudent_8d6cff_mi_1, ((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("WTStudent"))}, sel_registerName("class")));
    }
    return self;
}

结果可以遇见不论是[self class] 抑或是[super class],都是当前类,而不是打印父类为什么呢 ?
[self class]发送的消息是objc_msgSend,消息接收者是self,方法编号是class
[super class]本质是objc_msgSendSuper, 消息接收者也是self, 方法编号class, 所以[super class]打印出来的一样是self这个对象所指的类,也就是LGStudent。

小结

[super class]中的super只是一个编译指示器,就是让当前对象获取该方法,越过本类,直接从父类找。
消息的接受这还是本类self,所以在执行的时候打印出来的还是本来。

结合之前的章节----方法的快速查找流程

objc_msgSend(receiver,sel…)

  1. 判断 receiver是否存在
  2. receiver - isa -class
  3. class --内存平移 --cache
  4. cache – buckets
  5. buckets – 对于sel(如何保证查找速度呢?二分法)
  6. buckets 有对应的sel – cacheHit - 调用imp
  7. buckets 没有对应的sel – _objec_msgSend_uncached
    类方法?

当前的缓存里面没有该方法----- 方法的慢查找

lookUpImpForward
– 先找当前累的methodList
–父类的cache
–父类的methodList
– 父类为niu
– forward
– Imp