runtime这个东西,项目是很少用到的,但面试又避不可少,了解其内部的机制对底层的理解还是很有必要的。

1.动态添加属性

拓展类别属性的简单实现

a.定义字面量指针  static char dynamicAttributes;

b.设置属性  objc_setAssociatedObject(self,&dynamicAttributes,(id)value,OBJC_ASSOCIATION_COPY_NONATOMIC|OBJC_ASSCOCIATION_RETAIN)

c.获取属性值  objc_getAsscociatedObject(self,&dynamicAttributes)

2.model转字典

每个类中都有一个属性列表,我们要做的就是遍历该列表,利用KVC取值,装入字典

unsigned int count = 0;
objc_property_t *ptyList = class_copyPropertyList(self.class,&count);
NSMutableDictonary *dic = [NSMutableDictionary dictionary];
for (int i=0;i<count;i++) {
objc_property pty = ptyList[i];
const char *cname = property_getName(pty);
NSString *name = [NSString stringWithUTF8String:cname];
if (name.length > 0) {
id value = [self valueForKey:name];
[dic setObject:value forKey:name];
}
}

3.方法交换

C中方法是编译时就得实现。OC中方法是编译时无需实现,可在运行是动态插入方法及实现。利用这一点,可以轻松实现运行时方法的动态交换。

因为load方法早于main方法,并且不会覆盖父类实现,为了提高代码的可读性,一半是在分类中实现相关方法的交换。

+ load {
SEL original = @selector(willMoveToSuperView:);
SEL exchange = @selector(gl_willMoveToSuperView:);
Method originalMethod = class_getInstanceMethod(self.class,original);
Method exchangeMethod = class_getInstanceMethod(self.class,exchange);
// 动态添加方法,若选择器存在方法实现,会失败
BOOL isAdd = class_addMethod(self,original,method_getImplementation(exchangeMethod),method_getTypeEncoding(exchangeMethod));
if (isAdd) {
// 添加成功,将新的选择器替换为旧实现
class_replaceMethod(self,exchange,method_getImplementation(originalMethod),method_getTypeEncoding(originalMethod));
}else {
// 直接交换两方法实现
method_changeImplementations(originalMethod,exchangeMethod);
}
} - (void)gl_villMoveToSuperView:(UIView *)superView {
// do something ...
}

isa指针

  是一个指向所属类的指针。OC中消息机制是依靠objc_msgSend(receiver,selector)这个函数发送消息的。

  objc_msgSend会根据实例对象的isa指针查找对象的类,然后查找该类的dispatch_table中的selector,找不到就依次查找父类,直到NSObject类。实在找不到方法就抛出异常。找到selector后,会根据dispatch_table中的内存地址该selector。为了提高转发效率,系统会将所有的selector地址和已使用的selector地址缓存起来,通过类的形式划分不同的缓冲区域。obj_msgSend去查找dispatch_table前,会先检查该类的缓存,如果缓存命中,就直接调用selector。

消息转发机制(前提是dispatch_table找不到对应的selector)

  1.对象收到无法解读的消息后,首先会调用resolveInstanceMethod:询问是否有动态添加方法来处理

void speak(id self, SEL _cmd){
NSLog(@"Now I can speak.");
}
+ (BOOL)resolveInstanceMethod:(SEL)sel { NSLog(@"resolveInstanceMethod: %@", NSStringFromSelector(sel));
if (sel == @selector(speak)) {
class_addMethod([self class], sel, (IMP)speak, "V@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}

  2.第一步若没有添加新方法,那就询问有没别人帮忙处理,调用的是forwardingTargetForSelector:

- (id)forwardingTargetForSelector:(SEL)aSelector {
NSLog(@"forwardingTargetForSelector: %@", NSStringFromSelector(aSelector));
Bird *bird = [[Bird alloc] init];
if ([bird respondsToSelector: aSelector]) {
return bird;
}
return [super forwardingTargetForSelector: aSelector];
}
// Bird.m
- (void)fly {
NSLog(@"I am a bird, I can fly.");
}

  3.当前两步均没有响应时,走到第三部,调用forwardInvocation:,该方法会首先调用methodSignatureForSelector:方法获取选择子的方法签名

- (void)forwardInvocation:(NSInvocation *)anInvocation {
NSLog(@"forwardInvocation: %@", NSStringFromSelector([anInvocation selector]));
if ([anInvocation selector] == @selector(speak)) {
Monkey *monkey = [[Monkey alloc] init];
[anInvocation invokeWithTarget:monkey];
}
} - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSLog(@"method signature for selector: %@", NSStringFromSelector(aSelector));
if (aSelector == @selector(code)) {
return [NSMethodSignature signatureWithObjCTypes:"V@:@"];
}
return [super methodSignatureForSelector:aSelector];
}

  4.若前三步均没有处理,则抛出异常doesNotRecognizeSelector:

NSInvocation

  对方法的另一种封装。相对于performSelector:withObject:只能传2参数的弊端,invocation类可以设置封装多参数

  a.对选择子签名  

    NSMethodSignature *instanceSignature = [self instanceMethodSignatureWithSelector:@selector(run:)];//实例方法签名

    NSMethodSignature *classSignature = [self methodSignatureWithSelector:@selector(run:)];//类方法签名

  b.实例化invocation,并设置参数

NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
//设置方法调用者
invocation.target = self;
//注意:这里的方法名一定要与方法签名类中的方法一致
invocation.selector = @selector(run:);
NSString *way = @"byCar";
//这里的Index要从2开始,以为0跟1已经被占据了,分别是self(target),selector(_cmd)
[invocation setArgument:&way atIndex:2];
//3、调用invoke方法
[invocation invoke];

  c.获取invocation返回值

id res = nil;
if (signature.methodReturnLength != 0) {//有返回值
//将返回值赋值给res
[invocation getReturnValue:&res];
}
return res;

IMP指针

  指向选择子方法实现的指针,直接调用它相对于调用方法,会提高程序运行效率。

  相对于Method的方法交换,method_exchangeImplementations(method1,method2),使用IMP指针可以简化其对方法的额外实现,显得更加优雅。

  如果想IMP指针带参数或者返回值,需要将proprecessing:enable strict checking of objc_msgSend calls 配置为NO,默认是YES,表示IMP无参无返回值。

typedef id (*_IMP)(id,SEL,...);
typedef void (*_VIMP)(id,SEL,...); + (void)load {
static dispatch_once onceToken;
dispatch_once(&onceToken,^{
Method viewDidLoad = class_getInstanceMethod(self,@selector(viewDidLoad));
_IMP viewDidLoad_IMP = (_IMP)method_getImplementation(viewDidLoad);
method_setImplementation(viewDidLoad,imp_implementationWithBlock(^(id target,SEL action){
viewDidLoad_IMP(target,@selector(viewDidLoad));
// 新增代码 do extra things
}));
});
}

最后贴一张class的内部结构图,容以后细细研究

runtime使用总结的更多相关文章

  1. runtime梳理。

    一.runtime简介 RunTime简称运行时.OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的是消息机制. 对于C语言,函数的调用在编译的时候会决定调用哪个函数. 对于OC的函数,属于 ...

  2. myeclipse 无法启动 java.lang.IllegalStateException: Unable to acquire application service. Ensure that the org.eclipse.core.runtime bundle is resolved and started (see config.ini).

    把myeclipse10 按照目录完整拷贝到了另外一台电脑, 另外的目录 原安装目录 D\:\soft\i\myeclipse10 新安装目录 E\:\soft\myeclipse10 双击启动失败, ...

  3. Objective-C runtime初识

    Objective-C Runtime Describes the macOS Objective-C runtime library support functions and data struc ...

  4. Objective-C runtime的常见应用

    用Objective-C等面向对象语言编程时,"对象"(object)就是"基本构造单元"(building block).开发者可以通过对象来存储并传递数据. ...

  5. Runtime应用防止按钮连续点击 (转)

    好久之前就看到过使用Runtime解决按钮的连续点击的问题,一直觉得没啥好记录的.刚好今天旁边同时碰到这个问题,看他们好捉急而且好像很难处理,于是我先自己看看… 前面自己也学习了很多Runtime的东 ...

  6. iOS开发-- 通过runtime kvc 移除导航栏下方的阴影效果线条

    网上查了很多, 都是重新绘制, 感觉有点蠢, 恰巧工作有会闲, 就简单的通过runtime遍历了下属性找寻了下私有类和方法, 这里直接贴方法, 找寻过程也发出来, 能看懂的直接就能看懂, 看不太明白的 ...

  7. VS2015 出现 .NETSystem.Runtime.Remoting.RemotingException: TCP 错误

    错误内容: 界面显示内容为: .NET�������������System.Runtime.Remoting.RemotingException: TCP 淇¢亾鍗忚鍐茬獊: 搴斾负鎶ュご銆� 鍦 ...

  8. DirectX runtime

    DirectX 9.0 runtime etc https://www.microsoft.com/en-us/download/details.aspx?id=7087 DirectX 11 run ...

  9. runtime

    7.runtime实现的机制是什么,怎么用,一般用于干嘛. 你还能记得你所使用的相关的头文件或者某些方法的名称吗? 运行时机制,runtime库里面包含了跟类.成员变量.方法相关的API,比如获取类里 ...

  10. runtime 第四部分method swizzling

    接上一篇 http://www.cnblogs.com/ddavidXu/p/5924597.html 转载来源http://www.jianshu.com/p/6b905584f536 http:/ ...

随机推荐

  1. Linux系统编程【5】——stty的学习

    从文件的角度看设备 之前几篇文章介绍的编程是基于文件的.数据可以保存在文件中,也可以从文件中取出来做处理,再存回去.不仅如此,Linux操作系统还专门为这个东西建立了一套规则,就是前期介绍的" ...

  2. oepncv实现——图像去水印

    功能简介:通过拖动鼠标实现指定区域水印或是斑点的去除. 实现原理:利用opencv鼠标操作setMouseCallback函数框选(左上到右下)需要处理的区域,按下鼠标开始选中,松开鼠标结束,对选中区 ...

  3. F5 api接口开发实战(一)

    本人从18年下旬,开始从事F5负载均衡的自动化开发工作,主要使用python编程语言,开发的F5功能模块为LTM和GTM. F5开发简介 1.F5管理模式 F5的管理模式主要有4种(不包含snmp), ...

  4. python中的时间戳和格式化之间的转换

    import time #把格式化时间转换成时间戳 def str_to_timestamp(str_time=None, format='%Y-%m-%d %H:%M:%S'): if str_ti ...

  5. GO语言复合类型02---数组

    package main import "fmt" /* 固定长度.固定类型的数据容器 */ /*数组的声明*/ func main031() { //var array [5]i ...

  6. deeplearning算法优化原理

    deeplearning算法优化原理目录· 量化原理介绍 · 剪裁原理介绍 · 蒸馏原理介绍 · 轻量级模型结构搜索原理介绍 1. Quantization Aware Training量化介绍1.1 ...

  7. UF_EVAL 曲线或边分析

    Open C UF_EVAL_ask_arc  圆形曲线或边分析,得到曲线或边的信息UF_EVAL_ask_ellipse  椭圆曲线或边分析,得到曲线或边的信息UF_EVAL_ask_hyperbo ...

  8. 【NX二次开发】大开眼界,DLL还可以这么调!

    NX二次开发应用程序有交互式.批处理和远程3中模式.交互模式的应用程序(.dll)在NX界面环境下运行.dll以动态链接库的方式被加载到NX的进程空间中.dll可以通过下列几种方式执行. 1.直接激活 ...

  9. Docker开启安全的TLS远程连接

    目录 1.1 不安全的远程访问方式 1.1.1 编辑docker.service文件: 1.1.2 重新加载Docker配置生效 1.1.3 警告! 2.1 建立基于TLS数字签名的安全连接 1.1 ...

  10. 会点自动化就要25k? 现在年轻人这么浮躁吗

    面试中一问元素定位就对答如流.一问实际项目框架如何搭建就避重就轻.含糊其辞,这样的自动化实战能力也能拿25K?静待下文: 一.为什么现在自动化测试工资那么高呢? 结合现在的职场环境与企业用人需求,自动 ...