简析OC的消息发送机制

什么是消息转发?其实OC的方法调用就是消息转发的过程,比如创建一个名为Persion的类,调用alloc和init方法,其实就是给系统发送了“alloc”和“init”消息,代码如下:

    // alloc :分配内存空间 malloc()用来分配内存的,告诉系统,给我一块空间                 
    // init  :初始化对象,属性&方法
    Persion *p = [[Persion alloc] init];

接下来我们就具体看一下他是怎么发送消息的
首先我们的导入头文件#import ,因为从ios5以后苹果是不推荐使用这个底层函数的,所以得设置一下,就是在Build Settings里搜索msg,把这个参数改为NO,如图:


image.png

平时我们调OC的方法是这样,比如先在perison类里实现一个方法,然后在外边调用一下

#import "Persion.h"

@implementation Persion
-(void)sleep {
    NSLog(@"睡了!!!");
}
@end
    Persion *p = [[Persion alloc] init];
    [p sleep];
2018-05-25 14:59:13.229804+0800 消息转发机制[11297:262465] 睡了!!!

然后我们点进的头文件,可以看到有这样一个方法

objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)

没错这个就是用来发送消息的方法,从提示可以大致看出需要传什么参数了,接下来就用它替换一下上边的代码,像这样:

    Persion *p = objc_msgSend([Persion class], @selector(alloc));
    p = objc_msgSend(p, @selector(init));
    [p sleep];
2018-05-25 15:51:23.654009+0800 消息转发机制[11939:332368] 睡了!!!

试试开始明白点什么了,嘿嘿!
上边的代码中还是有一些OC的代码,我们还能用C语言的函数来替换一下:

    Persion *p = objc_msgSend(objc_getClass("Persion"), sel_registerName("alloc")); 
    p = objc_msgSend(p, sel_registerName("init"));
    [p sleep];
2018-05-25 15:55:47.188866+0800 消息转发机制[12018:338981] 睡了!!!

其实在.m文件编译后,我们的alloc和init方法就被编译成了上边的C语言代码。
还有就是我们看objc_msgSend(id _Nullable self, SEL _Nonnull op, ...),参数最后又三个点,说明它是可扩展,那我们就试着传个参数试试,首先把Persion的方法改下:

-(void)sleepWithTime:(NSString *)time {
    NSLog(@"睡了%@!!!",time);
}
    Persion *p = objc_msgSend(objc_getClass("Persion"), sel_registerName("alloc"));
    p = objc_msgSend(p, sel_registerName("init"));
    objc_msgSend(p, sel_registerName("sleepWithTime:"), @"3小时");
2018-05-25 16:17:55.577908+0800 消息转发机制[12294:365274] 睡了3小时!!!

完美!!!

到这里在给大家介绍一个方法,objc_msgSendSuper(<#struct objc_super * _Nonnull super#>, <#SEL _Nonnull op, ...#>),从方法名可以看出,它就是给父类发消息的方法了,首先创建一个Persion的子类,oldPersion,然后实现父类的方法

-(void)sleepWithTime:(NSString *)time {
    NSLog(@"子类的方法");
}

objc_msgSendSuper从提示可以看出第一个参数是个父类的结构体,点进去看


image.png

两个参数:

  1. receiver:一个实例,也就是我们oldPersion的实例
  2. super_class父类的类型
    于是我们先创建一个结构体,然后把它的地址作为objc_msgSendSuper的第一个参数。

然后第二步就是给父类发消息了,像这样

    oldPersion *p = [[oldPersion alloc] init];
    
    struct objc_super oldSuper = {p ,class_getSuperclass(objc_getClass("oldPersion"))};
    objc_msgSendSuper(&oldSuper, sel_registerName("sleepWithTime:"), @"8小时");
2018-06-28 10:46:58.155 消息转发机制[232:7780] 睡了8小时!!!

最后oldPersion跳过自己的方法,调用了父类的方法。

你可能感兴趣的:(简析OC的消息发送机制)