runtime
参考:
https://www.jianshu.com/p/8345a79fd572
1.runtime
如何实现 weak
属性
weak策略表明该属性定义了一种“非拥有关系” (nonowning relationship)。为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特质同assign类似;然而在属性所指的对象遭到销毁时,属性值也会清空(nil out)
2.那么runtime
如何实现weak
变量的自动置nil
?
runtime对注册的类,会进行布局,会将 weak 对象放入一个 hash 表中。用 weak 指向的对象内存地址作为 key,当此对象的引用计数为0的时候会调用对象的 dealloc 方法,从而设置为 nil。所以weak属性不需要在dealloc中置nil
3.weak
实现原理
1、初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。
2、添加引用时:objc_initWeak函数会调用 objc_storeWeak() 函数, objc_storeWeak() 的作用是更新指针指向,创建对应的弱引用表。
3、释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。
4.runtime
如何通过selector
找到对应的IMP
地址?(分别考虑类方法和实例方法)
每一个类对象中都一个对象方法列表
方法列表中,每个方法结构体都记录着方法的名称,方法实现,以及参数类型,其实selector本质就是方法名称,通过这个方法名称就可以在方法列表中找到对应的方法实现.
类方法列表是存放在类对象 isa指针指向的元类对象中(类方法缓存)
1、当我们发送一个消息给一个对象时,这条消息会在对象的类对象方法列表里查找
2、当我们发送一个消息给一个类时,这条消息会在类的Meta Class对象的方法列表里查找
5.使用runtime Associate
方法关联的对象,需要在主对象dealloc
的时候释放么?
无论在MRC下还是ARC下均不需要被关联的对象在生命周期内要比对象本身释放的晚很多,它们会在被 NSObject -dealloc 调用的object_dispose()方法中释放
补充:对象的内存销毁时间表,分四个步骤
6._objc_msgForward
函数是做什么的?直接调用它将会发生什么?
_objc_msgForward是IMP类型,用于消息转发的:当向一个对象发送一条消息,但它并没有实现的时候,_objc_msgForward会尝试做消息转发
直接调用_objc_msgForward是非常危险的事,这是把双刃刀,如果用不好会直接导致程序Crash,但是如果用得好,能做很多非常酷的事
JSPatch就是直接调用_objc_msgForward来实现其核心功能的
7.能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么?
不能向编译后得到的类中增加实例变量;能向运行时创建的类中添加实例变量;
因为编译后的类已经注册在runtime中,类结构体中的objc_ivar_list 实例变量的链表和instance_size实例变量的内存大小已经确定,同时runtime 会调用class_setIvarLayout 或 class_setWeakIvarLayout来处理strong weak引用,所以不能向存在的类中添加实例变量
运行时创建的类是可以添加实例变量,调用 class_addIvar函数,但是得在调用objc_allocateClassPair之后,objc_registerClassPair之前,原因同上。
8.简述下Objective-C
中调用方法的过程(runtime)
Objective-C是动态语言,每个方法在运行时会被动态转为消息发送,即:objc_msgSend(receiver, selector),整个过程介绍如下:
1、objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类
2、然后在该类中的方法列表以及其父类方法列表中寻找方法运行
3、如果,在最顶层的父类(一般也就NSObject)中依然找不到相应的方法时,程序在运行时会挂掉并抛出异常unrecognized selector sent to XXX
4、但是在这之前,objc的运行时会给出三次拯救程序崩溃的机会,这三次拯救程序奔溃的说明见问题《什么时候会报unrecognized selector的异常》中的说明
9.什么是method swizzling
(俗称黑魔法)
简单说就是进行方法交换
在OC中调用一个方法,其实是向一个对象发送消息,查找消息的唯一依据是selector的名字。利用OC的动态特性,可以实现在运行时偷换selector对应的方法实现,进行方法交换
10.分类添加属性
应用场景: 给系统的类增加属性的时候,可以使用runtime动态添加属性.
本质: 动态添加属性,就是让某个属性与对象产生关联
runtime一般针对系统的类
static const char *key = "name";
// set方法
- (void)setName:(NSString *)name{
/*
param1:object:给哪个对象添加属性
param2:key:属性名称
param3:value:属性值
param4:policy:保存策略
*/
objc_setAssociatedObject(self, key, name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
// get 方法
- (NSString *)name{
return objc_getAssociatedObject(self, key);
}
12.交换方法
//类方法
+ (void)exchangeClassMethod:(Class)anClass methodOri:(SEL)methodOri methodNew:(SEL)methodNew{
Method oriM = class_getClassMethod(anClass, methodOri);
Method newM = class_getClassMethod(anClass, methodNew);
method_exchangeImplementations(oriM, newM);
}
//对象方法
+ (void)exchangeInstanceMethod:(Class)anClass methodOri:(SEL)methodOri methodNew:(SEL)methodNew{
Method originalMethod = class_getInstanceMethod(anClass, methodOri);
Method swizzledMethod = class_getInstanceMethod(anClass, methodNew);
BOOL didAddMethod = class_addMethod(anClass,
methodOri,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(anClass,
methodNew,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
13.添加方法
/*
Class cls:类对象(⚠️:我们可以看到获取方法的函数有两个class_getInstanceMethod,class_getClassMethod,分别获取实例方法和类方法,但是如果我们要添加获或者替换方法就需要注意你操作的是实例方法还是类方法,如果是类方法这个参数一定要传本类的元类)
SEL name:方法的selector
IMP imp:函数对应实现
const char *types:代表函数类型,比如无参数无返回值->”v@:”,int类型返回值,一个参数传入->”i@:@”,如果你知道了对应的Method,你可以直接通过method_getTypeEncoding函数获取。
*/
+ (void)load{
Class class = NSClassFromString(@"WebActionDisablingCALayerDelegate");
class_addMethod(class, NSSelectorFromString(@"setBeingRemoved"), (IMP)setBeingRemoved, "v@:");
class_addMethod(class, NSSelectorFromString(@"willBeRemoved"), (IMP)willBeRemoved, "v@:");
class_addMethod(class, NSSelectorFromString(@"removeFromSuperview"), (IMP)willBeRemoved, "v@:");
}
id setBeingRemoved(id self, SEL selector, ...){
return nil;
}
id willBeRemoved(id self, SEL selector, ...){
return nil;
}