runtime基本知识

一,RunTime是什么,究竟什么时候用

1.第一个问题, 
1》runtime实现的机制是什么,怎么用,一般用于干嘛? 
 runtime是一套比较底层的纯C语言API, 属于1个C语言库, 包含了很多底层的C语言API。

 话不多说  LIST:

OC : 

[[MJPerson alloc] init] 的RunTime

objc_msgSend(objc_msgSend(“MJPerson” , “alloc”), “init”)

2.第二个问题 
runtime 用来干什么呢??用在那些地方呢?怎么用呢? 
runtime是属于OC的底层, 可以进行一些非常底层的操作(用OC是无法现实的, 不好实现)

  • 在程序运行过程中, 动态创建一个类(比如KVO的底层实现)

  • 在程序运行过程中, 动态地为某个类添加属性\方法, 修改属性值\方法

  • 遍历一个类的所有成员变量(属性)\所有方法 

二,RunTime的基本语法

1.NSObject的方法

Cocoa 中大多数类都继承于NSObject类,也就自然继承了它的方法。最特殊的例外是NSProxy,它是个抽象超类,它实现了一些消息转发有关的方法,可以通过继承它来实现一个其他类的替身类或是虚拟出一个不存在的类,说白了就是领导把自己展现给大家风光无限,但是把活儿都交给幕后小弟去干。


有的NSObject中的方法起到了抽象接口的作用,比如description方法需要你重载它并为你定义的类提供描述内容。NSObject还有些方法能在运行时获得类的信息,并检查一些特性,比如class返回对象的类;isKindOfClass:isMemberOfClass:则检查对象是否在指定的类继承体系中;respondsToSelector:检查对象能否响应指定的消息;conformsToProtocol:检查对象是否实现了指定协议类的方法;methodForSelector:则返回指定方法实现的地址。


Runtime术语

objc_msgSend:它的真身是这样的:



id objc_msgSend ( id self, SEL op, ... );


下面将会逐渐展开介绍一些术语,其实它们都对应着数据结构。


SEL:选择子

objc_msgSend函数第二个参数类型为SEL,它是selector在Objc中的表示类型(Swift中是Selector类)。selector是方法选择器,可以理解为区分方法的 ID,而这个 ID 的数据结构是SEL:



typedef struct objc_selector *SEL;

不同类中相同名字的方法所对应的方法选择器是相同的,即使方法名字相同而变量类型不同也会导致它们具有相同的方法选择器,于是 Objc 中方法命名有时会带上参数类型( NSNumber 一堆抽象工厂方法拿走不谢),Cocoa 中有好多长长的方法哦。

id

objc_msgSend第一个参数类型为id,大家对它都不陌生,它是一个指向类实例的指针:


typedef struct objc_object *id;


objc_object又是啥呢:


struct objc_object { Class isa; };


objc_object结构体包含一个isa指针,根据isa指针就可以顺藤摸瓜找到对象所属的类。


Class

之所以说isa是指针是因为Class其实是一个指向objc_class结构体的指针:


typedef struct objc_class *Class;


objc_class就是我们摸到的那个瓜,里面的东西多着呢:具体不在赘述,对我们理解这个无关紧要


最后要提到的还有一个objc_cache,顾名思义它是缓存,它在objc_class的作用很重要,在后面会讲到。


一个 ObjC 类同时也是一个对象,为了处理类和对象的关系,runtime 库创建了一种叫做元类 (Meta Class) 的东西。当你发出一个类似[NSObject alloc]的消息时,你事实上是把这个消息发给了一个类对象 (Class Object) ,这个类对象必须是一个元类的实例,而这个元类同时也是一个根元类 (root meta class) 的实例。你会说 NSObject 的子类时,你的类就会指向 NSObject 做为其超类。但是所有的元类最终都指向根元类为其超类。所有的元类的方法列表都有能够响应消息的类方法。所以当 [NSObject alloc] 这条消息发给类对象的时候,objc_msgSend()会去它的元类里面去查找能够响应消息的方法,如果找到了,然后对这个类对象执行方法调用。


Method

Method是一种代表类中的某个方法的类型。



typedef struct objc_method *Method;


objc_method在上面的方法列表中提到过,它存储了方法名,方法类型和方法实现:


struct objc_method {

SEL method_name OBJC2_UNAVAILABLE;

char *method_types OBJC2_UNAVAILABLE;

IMP method_imp OBJC2_UNAVAILABLE;

}


  • 方法名类型为SEL,前面提到过相同名字的方法即使在不同类中定义,它们的方法选择器也相同。

  • 方法类型method_types是个char指针,其实存储着方法的参数类型和返回值类型。

  • method_imp指向了方法的实现,本质上是一个函数指针


Ivar

Ivar是一种代表类中实例变量的类型。



typedef struct objc_ivar *Ivar;


objc_ivar在上面的成员变量列表中也提到过:


struct objc_ivar {

char *ivar_name OBJC2_UNAVAILABLE;

char *ivar_type OBJC2_UNAVAILABLE;

int ivar_offset OBJC2_UNAVAILABLE;

#ifdef __LP64__

int space OBJC2_UNAVAILABLE;

#endif

}

IMP

IMPobjc.h中的定义是:



typedef id (*IMP)(id, SEL, ...);


它就是一个函数指针,这是由编译器生成的。当你发起一个 ObjC 消息之后,最终它会执行的那段代码,就是由这个函数指针指定的。而IMP这个函数指针就指向了这个方法的实现。既然得到了执行某个实例某个方法的入口,我们就可以绕开消息传递阶段,直接执行方法,这在后面会提到。


你会发现IMP指向的方法与objc_msgSend函数类型相同,参数都包含idSEL类型。每个方法名都对应一个SEL类型的方法选择器,而每个实例对象中的SEL对应的方法实现肯定是唯一的,通过一组idSEL参数就能确定唯一的方法实现地址;反之亦然。

Cache

runtime.h中Cache的定义如下:


typedef struct objc_cache *Cache


还记得之前objc_class结构体中有一个struct objc_cache *cache吧,它到底是缓存啥的呢,先看看objc_cache的实现:


struct objc_cache {

unsigned int mask /* total = mask + 1 */ OBJC2_UNAVAILABLE;

unsigned int occupied OBJC2_UNAVAILABLE;

Method buckets[1] OBJC2_UNAVAILABLE;

};


Cache为方法调用的性能进行优化,通俗地讲,每当实例对象接收到一个消息时,它不会直接在isa指向的类的方法列表中遍历查找能够响应消息的方法,因为这样效率太低了,而是优先在Cache中查找。Runtime 系统会把被调用的方法存到Cache中(理论上讲一个方法如果被调用,那么它有可能今后还会被调用),下次查找的时候效率更高。这根计算机组成原理中学过的 CPU 绕过主存先访问Cache的道理挺像,而我猜苹果为提高Cache命中率应该也做了努力吧。


消息

前面做了这么多铺垫,现在终于说到了消息了。Objc 中发送消息是用中括号([])把接收者和消息括起来,而直到运行时才会把消息与方法实现绑定。



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值