write by coderiding,erliucxy 中山 东区 广东 2018-02-23- 17:19:20
第一部分:runtime的第一神器-Associated Objects
Associated Objects的介绍:中文翻译为关联对象、对象关联、关联引用
1.1主要涉及3个方法
objc_setAssociatedObject 设置
Copy objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,
id _Nullable value, objc_AssociationPolicy policy)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0, 2.0);
objc_getAssociatedObject 获取
Copy OBJC_EXPORT id objc_getAssociatedObject ( id object , const void *key )
OBJC_AVAILABLE ( 10.6 , 3.1 , 9.0 , 1.0 );
objc_removeAssociatedObjects 移除
不应该自己手动调用这个函数,规范的方法是:调用 objc_setAssociatedObject 方法并传入一个 nil 值来清除一个关联;因为可能会导致其他客户对其添加的属性也被移除了
Copy object 【源对象—{一般是self}】
key 【关键字{属性}】write by erliucxy
1.属性最好是 static char 类型的,当然更推荐是指针型的。
2.然而可以用更简单的方式实现:用selector}
3.{Since SELs are guaranteed to be unique and constant, you can use _cmd as the key for objc_setAssociatedObject().采用隐藏参数_cmd作为key}
value【被关联的对象】
policy 【关联策略{关联对象的行为} objc_AssociationPolicy】
1.2关联策略objc_AssociationPolicy
关联对象的行为,它可以指定Objc内存管理的引用计数机制。
以 OBJC_ASSOCIATION_ASSIGN 类型关联在对象上的弱引用不代表0 retian的 weak 弱引用,行为上更像 unsafe_unretained 属性,所以当在你的视线中调用weak的关联对象时要相当小心。write by erliucxy
Copy Behavior
@property Equivalent
Description
OBJC_ASSOCIATION_ASSIGN
@property (assign) 或 @property (unsafe_unretained)
指定一个关联对象的弱引用。
OBJC_ASSOCIATION_RETAIN_NONATOMIC
@property (nonatomic, strong)
指定一个关联对象的强引用,不能被原子化使用。
OBJC_ASSOCIATION_COPY_NONATOMIC
@property (nonatomic, copy)
指定一个关联对象的copy引用,不能被原子化使用。
OBJC_ASSOCIATION_RETAIN
@property (atomic, strong)
指定一个关联对象的强引用,能被原子化使用。
OBJC_ASSOCIATION_COPY
@property (atomic, copy)
指定一个关联对象的copy引用,能被原子化使用。
1.3 Associated Objects常见的使用例子
相当于加了一个 @property (nonatomic, strong)
Copy @dynamic associatedObject;《OC-checklist》[※※]@synthesize和@dynamic分别有什么作用?
- (void)setAssociatedObject:(id)object {
objc_setAssociatedObject(self, @selector(associatedObject), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (id)associatedObject {
return objc_getAssociatedObject(self, @selector(associatedObject));
}
相当于加了一个 @property (assign) 或 @property (unsafe_unretained)
Copy static const int TagKey = 0;
- (void)setTag:(NSInteger)tag{
objc_setAssociatedObject(self, &TagKey, @(tag), OBJC_ASSOCIATION_ASSIGN);
}
- (NSInteger)tag{
return [objc_getAssociatedObject(self, &TagKey) integerValue];
}
相当于加了一个 @property (nonatomic, strong)
Copy static const NSString *ViewHudKey = @"ViewHud”;
- (void)setHUD:(MBProgressHUD *)HUD{
objc_setAssociatedObject(self, &ViewHudKey, HUD, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (MBProgressHUD *)HUD{
return objc_getAssociatedObject(self, &ViewHudKey);
}
相当于加了一个 @property (atomic, strong)
Copy static const NSString *ViewControllerInfo = @"ViewControllerInfo”;
- (void)setRouterUserInfo:(NSDictionary *)routerUserInfo{
objc_setAssociatedObject(self, &ViewControllerInfo, routerUserInfo, OBJC_ASSOCIATION_RETAIN);
}
- (NSDictionary *)routerUserInfo{
return objc_getAssociatedObject(self, &ViewControllerInfo);
}
相当于加了一个 @property (nonatomic, copy)
Copy - (void)setFd_willAppearInjectBlock:(_FDViewControllerWillAppearInjectBlock)block
{
objc_setAssociatedObject(self, @selector(fd_willAppearInjectBlock), block, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (_FDViewControllerWillAppearInjectBlock)fd_willAppearInjectBlock
{
return objc_getAssociatedObject(self, _cmd);
}
相当于加了一个 @property (nonatomic, strong)
Copy - (void)setFd_prefersNavigationBarHidden:(BOOL)hidden
{
objc_setAssociatedObject(self, @selector(fd_prefersNavigationBarHidden), @(hidden), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)fd_prefersNavigationBarHidden
{
return [objc_getAssociatedObject(self, _cmd) boolValue];
}
// 相当于加了一个 @property (nonatomic, strong)
Copy static char RedTipViewKey;
- (void)setRedTipView:(UILabel *)redTipView
{
objc_setAssociatedObject(self, &RedTipViewKey, redTipView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UILabel *)redTipView
{
return objc_getAssociatedObject(self,&RedTipViewKey);
}
Associated Objects合体使用的例子
Copy - (AFActivityIndicatorViewNotificationObserver *)af_notificationObserver
{
AFActivityIndicatorViewNotificationObserver *notificationObserver = objc_getAssociatedObject(self, @selector(af_notificationObserver));
if (notificationObserver == nil)
{
// 相当于加了一个 @property (nonatomic, strong)
notificationObserver = [[AFActivityIndicatorViewNotificationObserver alloc] initWithActivityIndicatorView:self];
objc_setAssociatedObject(self, @selector(af_notificationObserver), notificationObserver, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return notificationObserver;
}
Copy - (_FDFullscreenPopGestureRecognizerDelegate *)fd_popGestureRecognizerDelegate
{
_FDFullscreenPopGestureRecognizerDelegate *delegate = objc_getAssociatedObject(self, _cmd);
if (!delegate) {
// 相当于加了一个 @property (nonatomic, strong)
delegate = [[_FDFullscreenPopGestureRecognizerDelegate alloc] init];
delegate.navigationController = self;
objc_setAssociatedObject(self, _cmd, delegate, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return delegate;
}
010 合体例子write by erliucxy
Copy - (void)bk_addEventHandler:(void (^)(id sender))handler forControlEvents:(UIControlEvents)controlEvents
{
NSParameterAssert(handler);
NSMutableDictionary *events = objc_getAssociatedObject(self, BKControlHandlersKey);
if (!events) {
// 相当于加了一个 @property (nonatomic, strong)
events = [NSMutableDictionary dictionary];
objc_setAssociatedObject(self, BKControlHandlersKey, events, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
NSNumber *key = @(controlEvents);
NSMutableSet *handlers = events[key];
if (!handlers) {
handlers = [NSMutableSet set];
events[key] = handlers;
}
BKControlWrapper *target = [[BKControlWrapper alloc] initWithHandler:handler forControlEvents:controlEvents];
[handlers addObject:target];
[self addTarget:target action:@selector(invoke:) forControlEvents:controlEvents];
}
第二部分:你在什么地方接触过runtime?
2.1 三个方面接触过:
通过 Objective-C 源代码 - 接触runtime
在runtime中,通过数据结构来定义的一些类、方法、协议等;
通过 Foundation 框架的NSObject类定义的方法 - 接触runtime
通过对 runtime 函数的直接调用 - 接触runtime
objc_ 开头的方法
如objc_property_attribute_t
protocol_ 开头的方法
如protocol_copyPropertyList
method_ 开头的方法
如method_setImplementation{第二神器用到}
如method_exchangeImplementations {第二神器用到}
如method_getTypeEncoding{第二神器用到}
imp_ 开头的方法
如imp_implementationWithBlock
class_ 开头的方法
如class_getInstanceMethod {第二神器用到}write by erliucxy
如class_replaceMethod {第二神器用到}
如class_addMethod {第二神器用到}
第三部分:runtime的第二神器-Method Swizzling
3.1本质就是
交换IMP和SEL的对应关系,以达到替换方法实现的目的;
Method Swizzling就是OC中的Hook
3.2必须明确的概念
IMP本质:是一个函数指针,保存了方法地址,指向方法的实现,这是由编译器生成的。表示同一个id + 同一个SEL = 同一个IMP
Copy /// A pointer to the function of a method implementation.
#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ );
#else
typedef id
id的本质:id其实就是一个指向objc_object结构体指针,它包含一个Class isa成员;根据isa指针就可以顺藤摸瓜找到对象所属的类write by erliucxy
Copy typedef struct objc_object *id;
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
}
SEL的本质:SEL 是系统在编译过程中,根据 方法的名字 以及 参数序列 生成一个用来区分这个方法的唯一 ID 编号;这个 ID 就是 SEL 类型的。和C的函数指针还不一样,函数指针直接保存了方法的地址,但是SEL只是方法编号。
Copy Selector(typedef struct objc_selector *SEL)
Method的本质:指向objc_method结构体指针;是一种代表类中的某个方法的类型。
method_name:方法名类型为SEL,前面提到过相同名字的方法即使在不同类中定义,它们的方法选择器也相同。
method_types:方法类型method_types是个char指针,其实存储着方法的参数类型和返回值类型。
method_imp:指向了方法的实现,本质上是一个函数指针,后面会详细讲到
Copy typedef struct objc_method *Method;
struct objc_method {
SEL method_name OBJC2_UNAVAILABLE ; // 方法名,作为key
char *method_types OBJC2_UNAVAILABLE ; // 参数类型以及返回值类型编码
IMP method_imp OBJC2_UNAVAILABLE ; // 方法实现指针,作为value
}
// 疑问:什么时候调用class_addMethod?调用它有什么作用?
// 作用:官方解答:Adds a new method to a class with a given name and implementation.class_addMethod will add an override of a superclass's implementation
// 作用:中文翻译:通过给的name和imp添加一个新的方法到类中。它将添加一个父类已经实现的imp。
// 注意:调用这个方法不能直接改变该类中已经存在的imp,如果要改变,使用method_setImplementation
Copy BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types);
// 当需要替换的方法可能有不存在的情况时,可以考虑使用该方法。write by erliucxy
cls:The class you want to modify.
name:A selector that identifies the method whose implementation you want to replace.
imp:The new implementation for the method identified by name for the class identified by cls.
types:An array of characters that describe the types of the arguments to the method. For possible values, see Objective-C Runtime Programming Guide > Type Encodings. Since the function must take at least two arguments—self and _cmd, the second and third characters must be “@:” (the first character is the return type).
// 疑问:什么时候调用class_replaceMethod?调用它有什么作用?
// 作用:官方:Replaces the implementation of a method for a given class.
Copy IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types);
Copy IMP method_setImplementation(Method m, IMP imp);
Copy const char * method_getTypeEncoding(Method m);
3.3 用一个图来表示Method Swizzling在做什么?
交换IMP和SEL的对应关系,以达到替换方法实现的目的;
3.4 Method Swizzling的实现方案
Copy + (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class aClass = [self class];
SEL originalSelector = @selector(method_original:);
SEL swizzledSelector = @selector(method_swizzle:);
Method originalMethod = class_getInstanceMethod(aClass, originalSelector);
Method swizzledMethod = class_getInstanceMethod(aClass, swizzledSelector);
// 直接交换IMP{有风险,因为当前类可以没有实现要交换的方法,而是继承父类的}
method_exchangeImplementations(originalMethod, swizzledMethod);
});
}
Method Swizzling的实现方案二:--最佳方案
如果类中没有实现 Original selector 对应的方法,那就先添加 Method,并将其 IMP 映射为 Swizzle 的实现。然后替换 Swizzle selector 的 IMP 为 Original 的实现;否则交换二者 IMP。
Copy + (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class aClass = [self class];
SEL originalSelector = @selector(method_original:);
SEL swizzledSelector = @selector(method_swizzle:);
Method originalMethod = class_getInstanceMethod(aClass, originalSelector);
Method swizzledMethod = class_getInstanceMethod(aClass, swizzledSelector);
// 通过class_addMethod:判断该类实现了原始方法还是父类实现了原始方法
* aClass,当前类
* originalSelector ,如果class_addMethod返回YES,originalSelector就是指向父类的方法;如果返回NO,originalSelector就是指向当前类的方法;
* method_getImplementation(swizzledMethod) ,自定义方法的IMP
* method_getTypeEncoding(swizzledMethod),自定义方法的类型
BOOL didAddMethod =
class_addMethod(aClass,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
// 如果添加成功,证明这个类没有实现要替换的方法,而是继承了父类的实现;接下来要做的是--使用class_replaceMethod交换IMP,下面就是将(swizzledSelector)与(method_getImplementation(originalMethod))的IMP交换;
class_replaceMethod(aClass,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod)
);
} else {
// 【如果添加失败,证明这个类实现了原始方法;交换IMP,originalMethod—swizzledMethod】write by erliucxy
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
有时为了避免方法命名冲突和参数 _cmd 被篡改,也会使用下面这种『静态方法版本』的 Method Swizzle。CaptainHook 中的宏定义也是采用这种方式,比较推荐:
Copy typedef IMP *IMPPointer;
static void MethodSwizzle(id self, SEL _cmd, id arg1);
static void (*MethodOriginal)(id self, SEL _cmd, id arg1);
static void MethodSwizzle(id self, SEL _cmd, id arg1) {
// do custom work
MethodOriginal(self, _cmd, arg1);
}
BOOL class_swizzleMethodAndStore(Class class, SEL original, IMP replacement, IMPPointer store)
{
IMP imp = NULL;
Method method = class_getInstanceMethod(class, original);
if (method) {
const char *type = method_getTypeEncoding(method);
imp = class_replaceMethod(class, original, replacement, type);
if (!imp) {
imp = method_getImplementation(method);
}
}
if (imp && store) { *store = imp; }
return (imp != NULL);
}
+ (BOOL)swizzle:(SEL)original with:(IMP)replacement store:(IMPPointer)store
{
return class_swizzleMethodAndStore(self, original, replacement, store);
}
+ (void)load
{
[self swizzle:@selector(originalMethod:) with:(IMP)MethodSwizzle store:(IMP *)&MethodOriginal];
}
3.5 Method Swizzling常见的使用例子
Copy #import "NSArray+Swizzle.h"
@implementation NSArray (Swizzle)
- (id)myLastObject
{
id ret = [self myLastObject];
NSLog(@"********** myLastObject *********** ");
return ret;
}
@end
#import <objc/runtime.h>
#import "NSArray+Swizzle.h"
int main(int argc, char *argv[])
{
@autoreleasepool {
Method ori_Method = class_getInstanceMethod([NSArray class], @selector(lastObject));
Method my_Method = class_getInstanceMethod([NSArray class], @selector(myLastObject));
method_exchangeImplementations(ori_Method, my_Method);
NSArray *array = @[@"0",@"1",@"2",@"3"];
NSString *string = [array lastObject];
NSLog(@"TEST RESULT : %@",string);
return 0;
}
}
结果输出:
:********** myLastObject ***********
:TEST RESULT : 3
Copy // 直接操作IMP
// RNSwizzle.m
// MethodSwizzle
#import "RNSwizzle.h"
#import <objc/runtime.h>
@implementation NSObject (RNSwizzle)
+ (IMP)swizzleSelector:(SEL)origSelector
withIMP:(IMP)newIMP {
Class class = [self class];
Method origMethod = class_getInstanceMethod(class,
origSelector);
IMP origIMP = method_getImplementation(origMethod);
if(!class_addMethod(self, origSelector, newIMP,
method_getTypeEncoding(origMethod)))
{
method_setImplementation(origMethod, newIMP);
}
return origIMP;
}
@end
Copy // ImagePickerReplaceMethodsHolder.h
* @interface ImagePickerReplaceMethodsHolder : NSObject
* - (BOOL)shouldAutorotate;
* - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation;
* @end
// ImagePickerReplaceMethodsHolder.m
@implementation ImagePickerReplaceMethodsHolder
- (BOOL)shouldAutorotate {
return NO;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationPortrait;
}
@end
// 开始替换
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self hackForImagePicker];
});
}
+ (void)hackForImagePicker {
// fix bug of image picker under iOS 6.0
// http://stackoverflow.com/questions/12522491/crash-on-presenting-uiimagepickercontroller-under-ios-6-0
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"6.0")
&& SYSTEM_VERSION_LESS_THAN(@"6.1"))
{
Method oldMethod1 = class_getInstanceMethod([UIImagePickerController class], @selector(shouldAutorotate));
Method newMethod1 = class_getInstanceMethod([ImagePickerReplaceMethodsHolder class], @selector(shouldAutorotate));
method_setImplementation(oldMethod1, method_getImplementation(newMethod1));
Method oldMethod2 = class_getInstanceMethod([UIImagePickerController class], @selector(preferredInterfaceOrientationForPresentation));
Method newMethod2 = class_getInstanceMethod([ImagePickerReplaceMethodsHolder class], @selector(preferredInterfaceOrientationForPresentation));
method_setImplementation(oldMethod2, method_getImplementation(newMethod2));
}
}
Copy @interface UIViewController (MRCUMAnalytics)
@end
@implementation UIViewController (MRCUMAnalytics)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalSelector = @selector(viewWillAppear:);
SEL swizzledSelector = @selector(mrc_viewWillAppear:);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
// class_addMethod 判断能否添加将要替换的方法
BOOL success = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
if (success) {
class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
} else {
// 主类已经实现,直接替换
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
#pragma mark - Method Swizzling
- (void)mrc_viewWillAppear:(BOOL)animated {
[self mrc_viewWillAppear:animated];
[MobClick beginLogPageView:NSStringFromClass([self class])];
}
@end
Copy #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:);
// class表示UIViewController
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));
// class_replaceMethod的最后结果和method_exchangeImplementations一样,
// 都可以实现交换方法
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 {
// 在交换了方法的实现后,xxx_viewWillAppear:方法的实现已经被替换为了 UIViewController -viewWillAppear:的原生实现,
// 所以这里并不是在递归调用
// 下面的方法实际上实在调用viewWillAppear
// 而调用UIViewController的viewWillAppear实际是调用xxx_viewWillAppear方法
[self xxx_viewWillAppear:animated];
NSLog(@"viewWillAppear: %@", self);
}
@end
这是因为class_replaceMethod方法其实能够覆盖到class_addMethod和method_setImplementation两种场景, 对于第一个class_replaceMethod来说, 如果viewWillAppear:实现在父类, 则执行class_addMethod, 否则就执行method_setImplementation将原方法的IMP指定新的代码块; 而第二个class_replaceMethod完成的工作便只是将新方法的IMP指向原来的代码.
但此处需要特别注意交换的顺序,应该优先把新的方法指定原IMP,再修改原有的方法的IMP.write by erliucxy
Copy + (void)load {
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);
if (!originalMethod || !swizzledMethod) {
return;
}
IMP originalIMP = method_getImplementation(originalMethod);
IMP swizzledIMP = method_getImplementation(swizzledMethod);
const char *originalType = method_getTypeEncoding(originalMethod);
const char *swizzledType = method_getTypeEncoding(swizzledMethod);
// 这儿的先后顺序是有讲究的,如果先执行后一句,那么在执行完瞬间方法被调用容易引发死循环
// 先将新方法指定给原来的IMP,这样调用原来的方法名,就会执行新的IMP
// 再将原方法指定给新的IMP,这样调用原来的方法名,就会执行新的IMP
class_replaceMethod(class,swizzledSelector,originalIMP,originalType);
class_replaceMethod(class,originalSelector,swizzledIMP,swizzledType);
}
3.6 Method Swizzling常见的问题
多继承关系的类使用Method Swizzling时,应该注意的方面是?
多个有继承关系的类的对象Swizzle时,先从父对象开始。 这样才能保证子类方法拿到父类中的被Swizzle的实现。
在load中Swizzle不用担心这种问题,因为load类方法会默认从父类开始调用。
使用Method Swizzling时解决命名冲突
为什么Method Swizzling应该在+load方法中实现?write by erliucxy
在 Objective-C 的运行时中,每个类有两个方法都会自动调用。+load 是在一个类被初始装载时调用,+initialize 是在应用第一次调用该类的类方法或实例方法前调用的。两个方法都是可选的,并且只有在方法被实现的情况下才会被调用。
+load在每个类被装载到Runtime的时候调用
+initialize 在每个类第一次被发送消息的时候调用。
之所以要在+load中进行,是因为方法交叉影响的是全局状态,+load中能保证在class 装载的时候进行交叉,而initialize没办法做到。
Swizzling在+load中执行时,不要调用[super load];因为如果是多继承,并且对同一个方法都进行了Swizzling,那么调用[super load]以后,父类的Swizzling就失效了。
为什么Method Swizzling应该在dispatch_once中完成?
由于 swizzling 改变了全局的状态,所以我们需要确保每个预防措施在运行时都是可用的。原子操作就是这样一个用于确保代码只会被执行一次的预防措施,就算是在不同的线程中也能确保代码只执行一次。Grand Central Dispatch 的 dispatch_once 满足了所需要的需求,并且应该被当做使用 swizzling 的初始化单例方法的标准。
write by coderiding,erliucxy 中山 东区 广东 2018-02-23- 17:19:20
第四部分:疑问
4.1 Method Swizzling危险吗?
What are the Dangers of Method Swizzling in Objective C?
分析 Here are some of the pitfalls of method swizzling:
Method swizzling is not atomic
Changes behavior of un-owned code
Possible naming conflicts
Swizzling changes the method's arguments
The order of swizzles matters
Difficult to understand (looks recursive)