runtime01-消息机制

OC及其动态性

Objective-C是基于C封装的一门面向对象的语言,底层实现是通过C/C++代码实现的。OC语言最大的特点就是其动态性,它会尽可能地把决策从编译时和连接时推迟到运行时(简单来说,就是编译后的文件不全是机器指令,还有一部分中间代码,在运行的时候,通过Runtime再把需要转换的中间代码再翻译成机器指令)。

Runtime与消息机制

Runtime是OC的一套由C和汇编编写的库(一些调用频率较高的方法是由汇编编写的),它是OC具有动态的的最主要条件。当程序执行[object doSomething]时,会向消息接收者(object)发送一条消息(doSomething),runtime会根据消息接收者是否能响应该消息而做出不同的反应。因此OC方法在运行时,都是作为消息在传递的,我们甚至可以把方法叫做消息,甚至可以说OC就是一门消息语言。

OC对象

在我们讲消息机制前首先要了解OC的对象,才能了解对象的方法调用过程。

OC的的底层其实就结构体,长这个样子。

OC对象.png

这个就是结构体的内部;
由上而下,objc_class这个结构体就是一个对象,它由三部分组成,

  • isa (指向对象父类的指针)
  • superclass(它的父类)
  • cache_t(对象的调用过的方法列表)
  • bits(对象的更多信息)

class_rw_t是将bit通过位运算的结果取其[3, 47]位,转换而成。这里包含了类的方法方法列表,属性列表及协议列表等。ro就是rootclass的意思他其中包含了类的成员变量等。

OC类的继承体系

NSString *str = [NSString string]

str是一个事例对象,它内部的isa指针指向它的类NSString,
NSString也是一个OC类,它内部也有isa,isa指向它的元类对象NSString meta-class (NSString的元类对象是NSString)
NSString meta-class````也是个OC类,它的isa指向它的元类meta-classmeta-class也是一个对象,它的isa指向哪里?为了防止它无限延伸下去,设计出了meta-class指向基类的meta-class以此作为它们的所属类。即,任何NSObject继承体系下的meta-class都使用NSObjectmeta-class```作为自己的所属类,而基类的meta-class的isa指针是指向它自己。这样就形成了一个完美的闭环。
事例如下图。

实例对象、类、元类关系

继承体系

消息机制

已经介绍类OC对象,现在可以runtime消息机制的主题了。我们的调用类方法也好,对象方法也好,都会被转成 objc_msgSend的消息。由于OC的底层是由C和C++实现的,我们就在OC文件目录下把它转成C++文件。

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc +要转的OC文件
例子:xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m

 //定义一个Dog类,它有两个方法一个是eat对象方法,另一个是eat类方法。
        Dog *wangCai = [[Dog alloc]init];
        [wangCai eat];
        [Dog eat];
        
//        (objc_msgSend)(wangCai, sel_registerName("eat"));  //对象方法
//        (objc_msgSend)(objc_getClass("Dog"), sel_registerName("eat"));  //类方法

这是的Dog类的.h文件

@interface Dog : NSObject
- (void)eat;
- (void)bark;
+ (void)play;
@end

我们注释掉它的eat方法实现,

#import "Dog.h"
#import <objc/runtime.h>
#import "Cat.h"

@implementation Dog

//- (void)eat{
//    NSLog(@"dog--eat");
//}
- (void)bark{
    NSLog(@"dog--bark");
}
+ (void)play{
    NSLog(@"classfunc-dog--play");
}
@end
消息发送

消息机制--动态方法解析

现在调用eat方法它会出错,其实OC在找不到方法实现的时候,它会动态调用runtime的这个方法+ (BOOL)resolveInstanceMethod:(SEL)sel

#import "Dog.h"
#import <objc/runtime.h>
#import "Cat.h"

@implementation Dog

//- (void)eat
//{
//    NSLog(@"dog--eat");
//}


- (void)bark{
    NSLog(@"dog--bark");
}

+ (void)play{
    NSLog(@"classfunc-dog--play");
}

/*
 2.0动态方法解析
 */
+ (BOOL)resolveInstanceMethod:(SEL)sel{
    if (sel == @selector(eat)) {
        // 获取其他方法
        Method method = class_getInstanceMethod(self, @selector(bark)); //调用Dog类的bark方法, 打印输出的结果是dog--bark

        // 动态添加test方法的实现
        class_addMethod(self, sel,
                        method_getImplementation(method),
                        method_getTypeEncoding(method));

        // 返回YES代表有动态添加方法
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

这样我们就实现了动态给OC对象寻找实现,防止崩溃的方法


动态方法解析

消息机制--消息转发

如果我们不实现resolveInstanceMethod程序必然会崩溃吗?别急runtime还有第二个机制防止奔溃- (id)forwardingTargetForSelector:(SEL)aSelector消息转发机制,你不是处理不了吗?那你吧消息转给别人,让有能力的类处理。
我们定义一个处理这eat方法的Cat类,.h的声明写不写都成,因为它会直接在方法实现中搜取

#import "Cat.h"

@implementation Cat

- (void)eat{
    NSLog(@"Cat--eat");
}
@end

我们在Dog的类中需要做如下处理,把消息转发给Cat让Cat帮它去处理

///Dog.m类
/*
 3.0 消息转发
 */
- (id)forwardingTargetForSelector:(SEL)aSelector
{
    if (aSelector == @selector(eat)) {
        return [[Cat alloc] init]; //返回空否
    }
    
    return [super forwardingTargetForSelector:aSelector];
}

//这样处理Cat的eat类会被调用,打印出Cat--eat

当然消息转发的时候也不知道转给谁(即- (id)forwardingTargetForSelector:(SEL)aSelector返回的是空对象nil),可以在- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector 方法里自己生成个签名,然后实现
- (void)forwardInvocation:(NSInvocation *)anInvocation方法,收集日志防止程序崩溃

///Dog.m类
/*
 3.0 消息转发
 */
- (id)forwardingTargetForSelector:(SEL)aSelector{
    return nil;
}

/*
 3.1消息转发
 */
// 方法签名:返回值类型、参数类型
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    if (aSelector == @selector(eat)) {
        return [NSMethodSignature signatureWithObjCTypes:"@@:*"];//手动创建一个方法签名
    }

    return [super methodSignatureForSelector:aSelector];
}

/*
 3.1.1消息转发
 */
//自定义的方法
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    NSLog(@"调用的方法找不到实现");
}

如果你这会儿知道谁能处理这个消息,也可以这样处理,

#import "Dog.h"
#import <objc/runtime.h>
#import "Cat.h"

@interface Dog ()
@property (nonatomic,strong) Cat *miCat;
@end

@implementation Dog

//- (void)eat{
//    NSLog(@"dog--eat");
//}
- (void)bark{
    NSLog(@"dog--bark");
}
+ (void)play{
    NSLog(@"classfunc-dog--play");
}
/*
 3.0 消息转发
 */
- (id)forwardingTargetForSelector:(SEL)aSelector{
    return nil;
}

/*
 3.1消息转发
 */
// 方法签名:返回值类型、参数类型
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    if (aSelector == @selector(eat)) {
        self.miCat = [Cat new];
        return [self.miCat methodSignatureForSelector:aSelector];
    }
    return [super methodSignatureForSelector:aSelector];
}

/*
 3.1.1消息转发
 */
//自定义的方法
- (void)forwardInvocation:(NSInvocation *)anInvocation{
//    NSLog(@"调用的方法找不到实现");
    if (anInvocation.selector == @selector(eat)) {
        [anInvocation invokeWithTarget:self.miCat];
    }
}
@end

/// Cat类的eat方法同样会被调用,打印出Cat--eat
消息转发

消息链总计起来如下:

  • 1.查找

1.本类查找方法,若有响应,若无去父类查找;

  1. 父类查找,若有响应,如无去父类查找,直至元类;
  2. 元类有响应,元类无,走消息分发机制
  • 2.消息转发

1.消息重新交给被掉用类,被掉用类可以让自己别的方法替代响应

  • 3.动态方法解析
  1. 被掉用类将方法抛给指定的类, 让它响应该方法
  • 4.消息转发

1.方法重新回到被掉用类自身,被掉用类,手动生成方法签名

  1. 将改消息交给指定的类,让它体自己响应

方法查找不到时,只有(2、3、4)三层保护全没有处理才会报错。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,264评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,549评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,389评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,616评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,461评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,351评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,776评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,414评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,722评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,760评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,537评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,381评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,787评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,030评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,304评论 1 252
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,734评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,943评论 2 336

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,672评论 0 9
  • 参考链接: http://www.cnblogs.com/ioshe/p/5489086.html 简介 Runt...
    乐乐的简书阅读 2,125评论 0 9
  • 我们常常会听说 Objective-C 是一门动态语言,那么这个「动态」表现在哪呢?我想最主要的表现就是 Obje...
    Ethan_Struggle阅读 2,161评论 0 7
  • 一、Runtime简介 Runtime简称运行时。OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的是消...
    林安530阅读 1,056评论 0 2
  • 转载:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麦子阅读 723评论 0 2