一、基本概念
RunTime简称运行时,就是系统在运行的时候的一些机制,其中最重要的是消息机制
C及其他语言,函数的调用在编译的时候决定调用哪个函数,完成编译后直接顺序执行,无任何二义性;OC的函数调用成为消息发送,在编译的时候并不能决定调用哪个函数,只有在真正运行的时候才会根据函数名称找到对应的函数来调用。
二、面试题一
1.runtime 怎么添加属性,方法等
【1】ivar -- 表示成员变量
Ivar ivar = class_getInstanceVariable([XXClass class],"privateName")
【2】class_addIvar -- 添加一个成员变量
NSUInteger size;
NSUInteger alignment;
NSGetSizeAlignment("*",&size,&alignment)
class_addIvar(kclass,"expression",size,alignment,"*")
【3】class_addMethod -- 添加成员方法
class_addMethod(kclass,@selector(setExpressionFormula:),(IMP)setExpressionFormula,"v@:@")
static void setExpressionFormula(id self,SEL cmd,id value)
【4】class_addProperty -- 添加属性
class_addProperty([XXClass class],"name",attrs,3)
【5】class_addProtocol -- 添加协议
【6】class_replaceProperty -- 替换类的属性
2. runtime 如何实现 weak 属性
【1】weak属性的特点 weak策略表明该属性定义了一种“非拥有关系”。
【2】runtime如何实现weak变量的自动值nil runtime对注册的类,会进行布局,会将weak对象放入一个hash表中。用weak指向的对象内存地址作为key,当此对象的引用计数为0的时候会调用对象的dealloc方法,假设weak指向的对象内存地址a,那么就会以a为key,在这个weak hash表中搜索,找到所有以a为key的weak对象,从而设置为nil
【3】weak属性需要在dealloc中置nil么 在ARC环境无论是强指针还是弱指针都无需再dealloc设置为nil,ARC会自动帮我们处理。即使是编译器不帮我们做这些,weak也不需要在dealloc中置nil,在属性所指的对象遭到摧毁时,属性值也会清空
3. runtime如何通过selector找到对应的IMP地址?
每一个类对象中都有一个对象方法列表
类方法列表是存放类对象中isa指针指向的原类对象中(类方法缓存)
方法列表中每个方法结构体中记录着方法的名称,方法实现,以及参数类型,其实selector本质就是方法名称,通过这个方法名称就可以在方法列表中找到对应的方法实现。
当我们发送一个消息给一个NSObject对象时,这条消息会在对象的类对象方法列表里查找
当我们发送一个消息一个类时,这条消息会在类的MetaClass对象的方法列表里查找
4.使用runtime Associate 方法关联的对象,需要在主对象dealloc的时候释放么
无论在MRC下还是ARC下均不需要,被关联的对象在生命周期内要比对象本身释放的晚很多,它们会在被NSObject -dealloc 调用的object_disponse()方法中释放。
对象的内存销毁事件表,分四个步骤:
【1】调用 -release:引用计数变为零 对象正在被销毁,生命周期即将结束;不能再有新的 __weak 弱引用,否则将指向 nil;调用[self dealloc]
【2】父类调用 -dealloc 继承关系中最直接继承的父类再调用 -dealloc;如果是 MRC 代码,则会手动释放实例变量们(iVars);继承关系中每一层的父类都再调用 -dealloc
【3】NSObject 调 -dealloc 只做一件事:调用Objective-C runtime 中object_disponse()方法
【4】调用object_disponse() 为C++的实例变量们(iVars)调用 destructors;为 ARC 状态下的实例变量们(iVars)调用 -release;解除所有使用 runtime Associate 方法关联的对象;解除所有 __weak 引用;调用 free()
5._objc_msgForward函数是做什么的?直接调用它将会发生什么
_objc_msgForward是IMP类型,用于消息转发的:当向一个对象发送一条消息,但它并没有实现的时候,_objc_msgForward会尝试做消息转发,直接调用_objc_msgForward是非常危险的事,这是把双刃刀,如果用不好会直接导致程序Crash。
6.能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么?
不能向编译后得到的类中增加实例
分析:因为编译后的类已经注册在runtime中,类结构体重的objc_ivar_list实例变量的链表和instance_size实例变量的内存大小已经确定,同时runtime会调用class_setIvarLayout或class_setWeakIvarLayout来处理strong weak引用,所以不能向存在的类中添加实例变量
能向运行时创建的类中添加实例变量
分析:运行时创建的类是可以添加实例变量,调用 class_addIvar 函数,但是得在调用 objc_allocateClassPair 之后,objc_registerClassPair之前,原因同上。
7. 简述下Object-C中调用方法的过程(runtime)
Objective-C是动态语言,每个方法在运行时会被动态转为消息发送,即:objc_msgSend(receiver,selector),整个过程介绍如下:
objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际属性的类;然后在该类中的方法列表以及其父类方法列表中寻找方法运行;如果,在最顶层的父类(一般也就NSObject)中依然找不到相应的方法时,程序在运行时会挂掉并抛出异常unrecognized selector sent to xxx;但是在这之前,objc的运行时会给出三次拯救程序崩溃的机会。
补充说明:Runtime 铸就了Objective-C是动态语言的特性
8.什么是method swizzling(俗称黑魔法)
简单说就是进行方法交换
【1】在Objective-C中调用一个方法,其实是向一个对象发送消息,查找消息的唯一依据是selector的名字
【2】每个类都有一个方法列表,存放着方法的名字和方法实现的映射关系,selector的本质其实就是方法名,IMP有点类似函数指针,执行具体的Method实现,通过selector就可以找到对象的IMP
【3】交换方法的几种实现方式
-1.利用 method_exchangeimplementations 交换两个方法的实现
-2.利用 class_replaceMethod 替换方法的实现
-3.利用 method_setImplementation 来直接设置某个方法的IMP
三、项目运用
3.1消息机制
-1.方法调用底层实现
NSObject *objc = objc_msgSend([NSObject class],@selector(alloc));
objc = objc_msgSend(objc,@selector(init));
-2.不要随便使用,不得已才使用
作用:调用已知私有方法,如调用没有在.h文件声明但是在.m文件实现了的方法 用runtime调用私有方法:方法编号后面开始,异常就是传入给方法的参数
objc_msgSend(p,@selector(run: str:),20,@"haha") objc_msgSend(p,@selector(eat))
-3.对象如何找到对应的方法去调用
1.方法保存到什么地方? 对象方法保存到类中,类方法保存到元类,每一个类都有方法列表methodList
2.明确去哪个类中调用? 通过isa指针。(1)根据对象的isa去对应的类查找方法,isa:判断去哪个类查找对应的方法,指向方法调用的类。 (2)根据传入的方法编号SEL,里面有个哈希列表,在列表中找到对应方法Method(方法名) (3)根据方法名(函数入口)找到函数实现,函数实现在方法区
3.2 交换方法
需求:已有项目中使用的是 UIImage去加载图片,现在调用imageNamed,就需要给一个提示。
方法:1.提供分类 2.写一个加提示的方法 3.用系统方法与这个功能方法交互实现,在+load方法中实现
代码实现方式: method_exchangeImplementations 如:https://www.jianshu.com/p/75849d7ea832
3.3 动态添加方法
为什么动态添加方法? OC是懒加载,有些方法可能很久不会调用
应用场景? 电商,视频,社交,收费项目:会员机制中,只要会员才拥有这些功能
class_addMethod(self,sel,(IMP)run,@"v@:")
3.4 动态添加属性
需求:给NSObject添加一个name属性,动态添加属性 -> runtime
set方法: objc_setAssociateObject(self,"name",name,OBJC_ASSOCIATION_RETAIN_NONATOMIC)
get方法: objc_getAssociatedObject(self,"name")
3.5 自动生成属性代码
需求:从网络数据中解析出字典数组,将数组转为模型时,有太多的key,可以使用下面的方法
代码:https://www.jianshu.com/p/75849d7ea832
二、面试题二
1.OC的消息机制
OC中的方法调用其实都是转成objc_msgSend函数的调用,给receiver(方法调用者)发送一条消息(selector方法名)
objc_msgSend底层有3大阶段
消息发送(当前类、父类中查找)、动态方法解析、消息转发
2.什么是Runtime?平时项目中用过么?
OC是一门动态性比较强的编程语言,允许很多操作推迟到程序运行时再进行
OC是动态性就是由Runtime来支撑和实现的,Runtime是一套C语言的API,封装了很多动态性相关函数
平时编写的OC代码,底层都是转换成了Runtime API进行调用
3.runtime具体应用
利用关联对象给分类添加属性
遍历类的所有成员变量
交换方法实现
利用消息转发机制解决方法找不到的异常问题
4.说说什么是runtime
OC是一个动态语言,OC的一切都是基本Runtime实现的平时编写的OC代码,在程序运行过程中,其实最终都是转成了runtime的C语言代码,runtime算是OC的幕后工作者
runtime是一套比较底层的纯C语言API,属于1个C语言库,包含了很多底层的C语言API
runtimeAPI的实现是用C++开发的(源码中的实现文件都是mm),是一套苹果开源的框架
三、主要使用场景