内容简介:我不大喜欢 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
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Practical Django Projects, Second Edition
James Bennett / Apress / 2009 / 44.99
Build a django content management system, blog, and social networking site with James Bennett as he introduces version 1.1 of the popular Django framework. You’ll work through the development of ea......一起来看看 《Practical Django Projects, Second Edition》 这本书的介绍吧!