文章分享至我的个人技术博客:https://cainluo.github.io/15074742481003.html
在上一章节里晓得了怎么在Category
里关联对象, 以及利用RunTime
转换模型的时候预防了三种转换时的情况, 如果没有去看的朋友可以到看看.
转载声明:如需要转载该文章, 请联系作者, 并且注明出处, 以及不能擅自修改本文.
利用Runtime归档
在以前我们在使用归档的时候都会有一个烦恼, 就是写的太多, 不信? 我们来声明一个对象:
#import@interface RunTimeCoderModel : NSObject @property (nonatomic, copy) NSString *cl_name;@property (nonatomic, copy) NSString *cl_height;@property (nonatomic, copy) NSString *cl_age;@end复制代码
常规归档的写法:
- (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:_cl_name forKey:@"name"]; [aCoder encodeObject:_cl_height forKey:@"height"]; [aCoder encodeObject:_cl_age forKey:@"age"];}复制代码
常规解档的写法:
- (instancetype)initWithCoder:(NSCoder *)aDecoder { self = [super init]; if (self) { self.cl_name = [aDecoder decodeObjectForKey:@"name"]; self.cl_height = [aDecoder decodeObjectForKey:@"height"]; self.cl_age = [aDecoder decodeObjectForKey:@"age"]; } return self;}复制代码
现在看着好像也不怎么样, 但在实际开发中, 我们要写的属性可不是只有这三个, 如果遇到变态的, 有上百个那怎么办呢?
逐个逐个去写么? 万一写完之后突然要改属性怎么办? 逐个去改? 这样子就会大量的浪费我们的时间, 这是不明智的写法.
回想一下, 每个类都有一个isa
的结构体指针, 里面可以拿到所有的每个类的信息, 那我们是否可以通过这个特性, 来给归档解档操作一番呢? 试试看:
RunTime
归档的写法:
- (void)cl_runtimeEncoderWithCoder:(NSCoder *)coder { unsigned int count = 0; Ivar *ivarList = class_copyIvarList([self class], &count); for (int i = 0; i < count; i++) { Ivar ivar = ivarList[i]; const char *name = ivar_getName(ivar); NSString *key = [NSString stringWithUTF8String:name]; id value = [self valueForKey:key]; [coder encodeObject:value forKey:key]; } free(ivarList);}复制代码
RunTime
解档写法:
- (void)cl_runtimeDecideWithCoder:(NSCoder *)decoder { unsigned int count = 0; Ivar *ivarList = class_copyIvarList([self class], &count); for (int i = 0; i < count; i++) { Ivar ivar = ivarList[i]; const char *name = ivar_getName(ivar); NSString *key = [NSString stringWithUTF8String:name]; id value = [decoder decodeObjectForKey:key]; [self setValue:value forKey:key]; } free(ivarList);}复制代码
最终的使用:
- (void)encodeWithCoder:(NSCoder *)aCoder { [self cl_runtimeEncoderWithCoder:aCoder];}- (instancetype)initWithCoder:(NSCoder *)aDecoder { self = [super init]; if (self) { [self cl_runtimeDecideWithCoder:aDecoder]; } return self;}复制代码
最终的效果:
这的确是可行的, 这样子我们就把这个写成一个通用的类, 并且遵守<NSCoding>
协议, 就可以把所有继承与NSObject
的类全部一次性归档.
在这里我就不对归档和解档的方法进行封装了, 都写在RunTimeCoderController
这个控制器上, 有想法的朋友可以自行进行封装, 这样子就可以抽成一个通用类.
RunTime黑魔法
前段时间搜了一下关于RunTime
的一些博客, 发现有很多人都说RunTime
黑魔法, 那什么是黑魔法?
- 简单的来说, 其实就是进行方法交换.
- 我们都知道, 在
Objective-C
中调用一个方法, 其实是向一个对象发送消息, 而查找消息的唯一依据就是selector
的名字, 利用Objective-C
的动态特性, 我们可以实现在运行时偷偷的换掉selector
对应的方法实现. - 而我们也逗知道, 每一个类都有一个方法列表, 存放着方法的名字和方法实现的映射关系,
selector
其实就是方法名, 而IMP
类似函数指针, 指向具体的Method
实现, 通过selector
就可以找到对应的IMP
.
- 交换方法的实现方式
- 利用
method_exchangeImplementations
来交换两个方法的实现 - 利用
class_replaceMethod
替换方法的实现 - 利用
method_setImplementation
来直接设置某个方法的IMP
- 利用
除了我们在演示里写过的代码, 在实际上又是怎么运用呢? 这里收集到了几种场景:
- 替换
ViewController
的生命周期方法 - 解决集合类的获取索引, 添加, 删除元素越界崩溃的问题
- 防止按钮重复暴力点击
- 全局更换控件初始化的效果
- App的热修复
- App空数据时的占位图工具类封装
- 全局修改
UINavigationBar
的BackButtonItem
代码这里就不写了, 想详细了解的朋友可以到下面的文章去了解.
推荐文章
工程地址
项目地址: https://github.com/CainRun/iOS-Project-Example/tree/master/RunTime/Five