内容简介:我不大喜欢 runtime,尤其是在我刚刚接触 iOS 开发的时候,一个原因是刚开始的时候觉得,这个调用方法名怎么是用下划线的,那时候觉得看的非常别扭。而后慢慢熟悉了 iOS 开发之后,尤其在了解 OC 的 GCD 也是下划线命名的方法之后,也不觉得那么面目可憎了。不过相对于 GCD,runtime 在平时开发中,真的用得少,更后来到铃盛做开发,公司是用 Swift 开发的应用,用到 runtime 的时候就更少了。于是乎,平时对运行时这部分就用的比较少。也没有抽时间去专门了解一下。用得少并不代表不知道,
缘起
我不大喜欢 runtime,尤其是在我刚刚接触 iOS 开发的时候,一个原因是刚开始的时候觉得,这个调用方法名怎么是用下划线的,那时候觉得看的非常别扭。而后慢慢熟悉了 iOS 开发之后,尤其在了解 OC 的 GCD 也是下划线命名的方法之后,也不觉得那么面目可憎了。
不过相对于 GCD,runtime 在平时开发中,真的用得少,更后来到铃盛做开发,公司是用 Swift 开发的应用,用到 runtime 的时候就更少了。于是乎,平时对运行时这部分就用的比较少。也没有抽时间去专门了解一下。
用得少并不代表不知道,尤其是用 OC 的时候接触过 JSPatch,在了解了一下原理后,发现 runtime 在处理一些特殊领域的问题的时候尤其有用。如果你平时有 听过 JSPatch、FLEX、Swizzle这些名词,甚至用过这些应用或者是技术的话,那么你或多或少应该接触过 runtime。
Runtime 能做什么
Runtime 能做什么呢? 一个常见应用是 Method Swizzling,通俗的说是做方法实现的替换。它能解决的问题包括但不限于:
- 修复方法旧有实现的一些 bug
- 重构代码,比如给 VC 做打点
- 重新实现方法,然某个方法适配旧的系统
对于这个功能我其实没有很喜欢,因为它改变了某个方法的实现,如果出问题的时候还是比较迷茫的,而且要是滥用的话,还是蛮危险的,代码的质量得不到保证,所以之前在开发的时候还蛮少使用的。
另外一个应用是关联对象,也就是动态添加一个属性,比如给 cell 添加高度缓存属性之类的。
这两个应用都有 Swift 的版本。
不废话,我们上代码:
方法替换:
Swift 版本extension UIViewController { private static let swizzleMethod: Void = { let originalViewDidLoadSelector = #selector(viewDidLoad) let swizzledViewDidLoadSelector = #selector(swizzled_viewDidLoad) swizzlingForClass(UIViewController.self, originalSelector: originalViewDidLoadSelector, swizzledSelector: swizzledViewDidLoadSelector) }() @objc func swizzled_viewDidLoad() { swizzled_viewDidLoad() } private static func swizzlingForClass(_ forClass: AnyClass, originalSelector: Selector, swizzledSelector: Selector) { let originalMethod = class_getInstanceMethod(forClass, originalSelector) let swizzledMethod = class_getInstanceMethod(forClass, swizzledSelector) guard let oMethod = originalMethod, let sMethod = swizzledMethod else { return } if class_addMethod(forClass, originalSelector, method_getImplementation(sMethod), method_getTypeEncoding(sMethod)) { class_replaceMethod(forClass, swizzledSelector, method_getImplementation(oMethod), method_getTypeEncoding(oMethod)) } else { method_exchangeImplementations(oMethod, sMethod) } } }
OC 版本
+ (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Class selfClass = object_getClass([self class]); SEL originalSEL = @selector(viewDidLoad); Method oriMethod = class_getInstanceMethod(selfClass, originalSEL); SEL newSEL = @selector(swizzleViewDidLoad); Method newMethod = class_getInstanceMethod(selfClass, newSEL); if(class_addMethod(selfClass, originalSEL, newSEL, method_getTypeEncoding(newSEL))) { class_replaceMethod(selfClass, newSEL, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod)); }else{ method_exchangeImplementations(oriMethod, newMethod); } }); }
关联对象:
Swift 版本extension UIViewController { private struct AssociateKeys { static var storyboardName = "UIViewController_StoryboardName" } var storyboardName:String? { get { return objc_getAssociatedObject(self, &AssociateKeys.storyboardName) as? String } set{ if let newValue = newValue { objc_setAssociatedObject(self, &AssociateKeys.storyboardName, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } } }
OC 版本
//.h #import <UIKit/UIKit.h> NS_ASSUME_NONNULL_BEGIN @interface UIViewController (UIViewController_Ext) @property(nonatomic,copy) NSString *storyboardName; @end NS_ASSUME_NONNULL_END //.m #import <objc/runtime.h> #import "UIViewController+UIViewController_Ext.h" @implementation UIViewController (UIViewController_Ext) @dynamic storyboardName; //dynamic 声明是告诉编译器,这个属性是在运行时通过 get set 得到的 - (NSString *)storyboardName { return objc_getAssociatedObject(self, @selector(storyboardName)); } - (void)setStoryboardName:(NSString *)storyboardName { objc_setAssociatedObject(self, @selector(storyboardName), storyboardName, OBJC_ASSOCIATION_COPY_NONATOMIC); } @end
基本实现到这个程度就好了。如果你想了解更多细节,可以继续往下看。
Runtime 是什么
类结构
isa 指针
可以分为指针型 isa(isa 的值代表 Class 的地址)和非指针型 isa(值的部分代表 Class 的地址),
对于对象,指向类对象objc_object
也就是 id
objc_class
也就是 Class
,继承自 objc_object
,实际上也是一个类。objc_class 在整个 OC 类结构里处于中心。如图:
cache_t
用来快速查找方法执行函数,是一种哈希表
class_data_bits_t
是对 class_rw_t
的封装,存储了相关类的读写信息。class_rw_t是可以由开发改写的,而 class_ro_t 不能改写,类创建的时候就固定下来了。不过如果是动态添加的类,那么就绕过了编译阶段,这时候可以动态添加变量。
函数结构
对于 types,用到了一种叫 Type Encoding
的技术,它将 v 对应 void,@ 对应着 id 类型,:对应着 SEL。
消息传递
OC 中所有方法都会转为 objc_msgSend
的样式,即:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 【1】JavaScript 基础深入——数据类型深入理解与总结
- 深入理解 Java 函数式编程,第 5 部分: 深入解析 Monad
- 深入理解 HTTPS
- 深入理解 HTTPS
- 深入浅出Disruptor
- 深入了解 JSONP
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Java RESTful Web Service实战
韩陆 / 机械工业出版社 / 2014-10-1 / 69.00
国内首本完整、深度地讲解如何基于Java标准规范实现REST风格的Web服务的专著,阿里巴巴Java技术专家12年开发经验结晶,3位业内著名技术专家联袂推荐!不仅深刻解读了最新的JAX-RS标准和其API设计,以及Jersey的使用要点和实现原理,而且系统讲解了REST的基本理论,更重要的是从实践角度深度讲解了如何基于Jersey实现完整的、安全的、高性能的REST式的Web服务。 《Jav......一起来看看 《Java RESTful Web Service实战》 这本书的介绍吧!