Runtime简介
Objective-C 扩展了 C 语言,并加入了面向对象特性和 Smalltalk 式的消息传递机制。而这个扩展的核心是一个用 C 和 编译语言 写的 Runtime 库。它是 Objective-C 面向对象和动态机制的基石。
Objective-C 是一个动态语言,这意味着它不仅需要一个编译器,也需要一个运行时系统来动态得创建类和对象、进行消息传递和转发。理解 Objective-C 的 Runtime 机制可以帮我们更好的了解这个语言,适当的时候还能对语言进行扩展,从系统层面解决项目中的一些设计或技术问题。了解 Runtime ,要先了解它的核心 - 消息传递 (Messaging)。
Runtime应用
Runtime简直就是做大型框架的利器。它的应用场景非常多,下面就介绍一些常见的应用场景。
- 关联对象(Objective-C Associated Objects)给分类增加属性
- 方法魔法(Method
- Swizzling)方法添加和替换和KVO实现
- 消息转发(热更新)解决Bug(JSPatch)
- 实现NSCoding的自动归档和自动解档
- 实现字典和模型的自动转换(MJExtension)
关联对象(Objective-C Associated Objects)给分类增加属性
我们都是知道分类是不能自定义属性和变量的。下面通过关联对象实现给分类添加属性。
关联对象Runtime提供了下面几个接口:
关联对象
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
//获取关联的对象
id objc_getAssociatedObject(id object, const void *key)
//移除关联的对象
void objc_removeAssociatedObjects(id object)
参数解释
id object:被关联的对象
const void *key:关联的key,要求唯一
id value:关联的对象
objc_AssociationPolicy policy:内存管理的策略
内存管理的策略
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
OBJC_ASSOCIATION_ASSIGN = 0, /**< Specifies a weak reference to the associated object. */
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object.
* The association is not made atomically. */
OBJC_ASSOCIATION_COPY_NONATOMIC = 3, /**< Specifies that the associated object is copied.
* The association is not made atomically. */
OBJC_ASSOCIATION_RETAIN = 01401, /**< Specifies a strong reference to the associated object.
* The association is made atomically. */
OBJC_ASSOCIATION_COPY = 01403 /**< Specifies that the associated object is copied.
* The association is made atomically. */
};
下面实现一个UIView的Category添加自定义属性defaultColor。
#import "ViewController.h"
#import "objc/runtime.h"
@interface UIView (DefaultColor)
@property (nonatomic, strong) UIColor *defaultColor;
@end
@implementation UIView (DefaultColor)
@dynamic defaultColor;
static char kDefaultColorKey;
- (void)setDefaultColor:(UIColor *)defaultColor {
objc_setAssociatedObject(self, &kDefaultColorKey, defaultColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (id)defaultColor {
return objc_getAssociatedObject(self, &kDefaultColorKey);
}
@end
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIView *test = [UIView new];
test.defaultColor = [UIColor blackColor];
NSLog(@"%@", test.defaultColor);
}
@end
打印结果:
2018-03-02 13:23:42.892712+0800 ocram[2053:63739] UIExtendedGrayColorSpace 0 1
打印结果来看,我们成功在分类上添加了一个属性,实现了它的setter和getter方法。
通过关联对象实现的属性的内存管理也是有ARC管理的,所以我们只需要给定适当的内存策略就行了,不需要操心对象的释放。
我们看看内存测量对于的属性修饰。
内存策略
属性修饰
描述
OBJC_ASSOCIATION_ASSIGN
@property (assign) 或 @property (unsafe_unretained)
指定一个关联对象的弱引用。
OBJC_ASSOCIATION_RETAIN_NONATOMIC
@property (nonatomic, strong)
@property (nonatomic, strong) 指定一个关联对象的强引用,不能被原子化使用。
OBJC_ASSOCIATION_COPY_NONATOMIC
@property (nonatomic, copy)
指定一个关联对象的copy引用,不能被原子化使用。
OBJC_ASSOCIATION_RETAIN
@property (atomic, strong)
指定一个关联对象的强引用,能被原子化使用。
OBJC_ASSOCIATION_COPY
@property (atomic, copy)
指定一个关联对象的copy引用,能被原子化使用。
替换系统方法
系统接口
class_getInstanceMethod(Class _Nullable cls, SEL _Nonnull name) // 获取对象方法
class_getClassMethod(Class _Nullable cls, SEL _Nonnull name) // 获取类方法
method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2) // 替换方法
替换系统方法代码
创建一个分类,替换系统方法就可以在此对象调用这个方法时插入自己想做的事情
@implementation UIView (Runtime)
+(void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
Method ios_layout = class_getInstanceMethod(class,@selector(layoutSubviews));
Method run_layout = class_getInstanceMethod(class,@selector(runtime_layoutSubviews));
method_exchangeImplementations(ios_layout, run_layout);
}
-(void)runtime_layoutSubviews{
NSLog(@"runtime insert");
[self runtime_layoutSubviews]; // 调用系统方法
}
runtime的更多用法在下一篇文章中继续分享。。。