iOS中runtime的实践

306 阅读2分钟

原文地址:https://mp.weixin.qq.com/s/mHZqBZAfa-sMevWIdtZF4g

runtime 是ios中objective-c的语言特性,提供给coder一个修改类和类方法的hook途径。主要有下面三个方面的使用。

##用途1:category的Associated属性 category的属性需要下面三个函数。

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);

objc_setAssociatedObject 用于给对象添加关联对象,传入 nil 则可以移除已有的关联对象; objc_getAssociatedObject 用于获取关联对象; objc_removeAssociatedObjects 用于移除一个对象的所有关联对象。

这里直接看下第三方库SVPullToRefresh的使用(省略一些代码)

@implementation UIScrollView (SVPullToRefresh)
– (SVPullToRefreshView *)pullToRefreshView {
return objc_getAssociatedObject(self, &UIScrollViewPullToRefreshView);
}
– (void)setPullHeaderBgView:(SVPullHeaderBgView *)pullHeaderBgView {
[self willChangeValueForKey:@”SVPullHeaderBgView”];
objc_setAssociatedObject(self, &UIScrollViewPullBackgroundView,
pullHeaderBgView,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[self didChangeValueForKey:@”SVPullHeaderBgView”];
}
@end

objc_setAssociatedObject的key 值可以使用 static void *kAssociatedObjectKey = &kAssociatedObjectKey; 使用 kAssociatedObjectKey 作为 key 值; 以及_cmd 也就是 selector,这个方法最优雅。 Associated属性的实现原理是存储在对象AssociationsHashMap中实现。

##用途2 Method Swizzling 的最佳实践

Method swizzling 用于改变一个已经存在的 selector 的实现。这样可以修改原来系统类库的相应方法,修正bug和做对应记录。例如AOP的上报技术就是利用Method Swizzling ;对网络情况的监控也是使用 Method Swizzling 方法。 实现基本方法:

#import <objc/runtime.h>
@implementation UIViewController (Tracking)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalSelector = @selector(viewWillAppear:);
SEL swizzledSelector = @selector(xxx_viewWillAppear:);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
// When swizzling a class method, use the following:
// Class class = object_getClass((id)self);
// …
// Method originalMethod = class_getClassMethod(class, originalSelector);
// Method swizzledMethod = class_getClassMethod(class, swizzledSelector);
BOOL didAddMethod =
class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
#pragma mark – Method Swizzling
– (void)xxx_viewWillAppear:(BOOL)animated {
[self xxx_viewWillAppear:animated];
NSLog(@”viewWillAppear: %@”, self);
}

开源框架中RSSwizzle (https://github.com/rabovik/RSSwizzle) 对swizzle 做了良好的封装。RSSwizzle中做了很多安全性保护。 常被用作AOP的 Aspects https://github.com/steipete/Aspects,也使用了Swizzling

##用途3 类的访问处理

查看runntime.h 文件可以看到很多对类本身的处理函数,如object_getIvar,objc_getClassList等等。 YYModel 中 https://github.com/ibireme/YYModel 将model json convert就是使用了runntime中对对象属性相关的操作。 强大的调试工具 FLEX https://github.com/Flipboard/FLEX 中对类的属性查看也使用了runntime。 总之,runntime在iOS 中应用广泛,建议大家深入了解掌握。 参考: http://blog.leichunfeng.com/blog/2015/06/26/objective-c-associated-objects-implementation-principle/ https://nshipster.cn/method-swizzling/