# 介绍Runtime

* write by coderiding，erliucxy 中山 东区 广东 2018-02-23- 17：19：20
* 开始之前，你可以直接查看图片[文章图片链接](https://res.cloudinary.com/dwkmwbcne/image/upload/v1537154338/blog/2018/09/OC-runtime%E4%BD%A0%E5%87%A0%E5%B2%81%E4%BA%86.png)

## 第一部分：runtime的第一神器-Associated Objects

* Associated Objects的介绍：中文翻译为关联对象、对象关联、关联引用
* 文字总结write by erliucxy
* 可以实现什么：给系统的类增加属性对象。
* 方便的地方是：//TODO

### **1.1主要涉及3个方法**

* objc\_setAssociatedObject 设置

```objectivec
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 获取

```
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 值来清除一个关联；因为可能会导致其他客户对其添加的属性也被移除了
* 基本参数说明

```
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

```
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常见的使用例子**

* 001--使用例子
* 相当于加了一个 @property (nonatomic, strong)

```
@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));
}
```

* 002--使用例子
* 相当于加了一个 @property (assign) 或 @property (unsafe\_unretained)

```
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];
}
```

* 003—使用例子
* 相当于加了一个 @property (nonatomic, strong)

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

* 004 使用例子
* 相当于加了一个 @property (atomic, strong)

```
static const NSString *ViewControllerInfo = @"ViewControllerInfo”;
- (void)setRouterUserInfo:(NSDictionary *)routerUserInfo{
    objc_setAssociatedObject(self, &ViewControllerInfo, routerUserInfo, OBJC_ASSOCIATION_RETAIN);
}

- (NSDictionary *)routerUserInfo{
    return objc_getAssociatedObject(self, &ViewControllerInfo);
}
```

* 005 使用例子
* 相当于加了一个 @property (nonatomic, 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);
}
```

* 006 使用例子
* 相当于加了一个 @property (nonatomic, strong)

```
- (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];
}
```

* 007 使用例子
* // 相当于加了一个 @property (nonatomic, strong)

```
static char RedTipViewKey;

- (void)setRedTipView:(UILabel *)redTipView
{
    objc_setAssociatedObject(self, &RedTipViewKey, redTipView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (UILabel *)redTipView
{
    return objc_getAssociatedObject(self,&RedTipViewKey);
}
```

* 008合体例子
* Associated Objects合体使用的例子

```
- (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;
}
```

* 009 合体例子

```
- (_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

```
- (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 三个方面接触过：**

1. 通过 Objective-C 源代码 - 接触runtime
   * 在runtime中，通过数据结构来定义的一些类、方法、协议等；
2. 通过 Foundation 框架的NSObject类定义的方法 - 接触runtime
   * 如isKindOfClass:
   * 如isMemberOfClass:
   * 如respondsToSelector:
   * 如methodForSelector:
3. 通过对 runtime 函数的直接调用 - 接触runtime
   * objc\_ 开头的方法
     1. 如objc\_ivar\_list
     2. 如objc\_property\_attribute\_t
   * ivar\_ 开头的方法
     1. 如ivar\_getOffset
     2. 如ivar\_list
   * protocol\_ 开头的方法
     1. 如protocol\_getName
     2. 如protocol\_copyPropertyList
   * method\_ 开头的方法
     1. 如method\_getName
     2. 如method\_setImplementation{第二神器用到}
     3. 如method\_exchangeImplementations {第二神器用到}
     4. 如method\_getTypeEncoding{第二神器用到}
   * sel\_ 开头的方法
     1. 如sel\_getName
     2. 如sel\_registerName
   * imp\_ 开头的方法
     1. 如imp\_getBlock
     2. 如imp\_removeBlock
     3. 如imp\_implementationWithBlock
   * class\_ 开头的方法
     1. 如class\_addIvar
     2. 如class\_getName
     3. 如class\_addProperty
     4. 如class\_getInstanceMethod {第二神器用到}write by erliucxy
     5. 如class\_replaceMethod {第二神器用到}
     6. 如class\_addMethod {第二神器用到}

## 第三部分：runtime的第二神器-Method Swizzling

### **3.1本质就是**

1. 交换IMP和SEL的对应关系，以达到替换方法实现的目的；
2. Method Swizzling就是OC中的Hook

### **3.2必须明确的概念**

* IMP本质：是一个函数指针，保存了方法地址，指向方法的实现，这是由编译器生成的。表示同一个id + 同一个SEL = 同一个IMP

```
/// 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

```
typedef struct objc_object *id;
struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY;
}
```

* SEL的本质：SEL 是系统在编译过程中，根据 方法的名字 以及 参数序列 生成一个用来区分这个方法的唯一 ID 编号；这个 ID 就是 SEL 类型的。和C的函数指针还不一样，函数指针直接保存了方法的地址，但是SEL只是方法编号。

```
Selector（typedef struct objc_selector *SEL）
```

* Method的本质：指向objc\_method结构体指针；是一种代表类中的某个方法的类型。
* method\_name：方法名类型为SEL，前面提到过相同名字的方法即使在不同类中定义，它们的方法选择器也相同。
* method\_types：方法类型method\_types是个char指针，其实存储着方法的参数类型和返回值类型。
* method\_imp：指向了方法的实现，本质上是一个函数指针，后面会详细讲到

```
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

```
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.
* // 作用：解释：为给定的类替换imp

```
IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types);
```

* // 当仅仅需要为一个方法设置其实现方式时使用

```
IMP method_setImplementation(Method m, IMP imp);
```

* // OC实现的编码类型

```
const char * method_getTypeEncoding(Method m);
```

### **3.3 用一个图来表示Method Swizzling在做什么？**

* 交换IMP和SEL的对应关系，以达到替换方法实现的目的；

### **3.4 Method Swizzling的实现方案**

* Method Swizzling的实现方案一：

```
+ (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。

```
+ (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 中的宏定义也是采用这种方式，比较推荐：

```
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常见的使用例子**

* 001 使用例子

```
#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  
```

* 002 使用例子--RNSwizzle

```
//  直接操作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  
```

* 003 使用例子—猿题库

```
// 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));
    }
}
```

* 004 使用例子

```
@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
```

* 005 使用例子

```
#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
* 为什么呢？

```
+ (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常见的问题**

1. 多继承关系的类使用Method Swizzling时，应该注意的方面是？
   * 多个有继承关系的类的对象Swizzle时，先从父对象开始。 这样才能保证子类方法拿到父类中的被Swizzle的实现。
   * 在load中Swizzle不用担心这种问题，因为load类方法会默认从父类开始调用。
   * 深入了解//TODO
2. 使用Method Swizzling时解决命名冲突
3. 为什么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就失效了。
4. 为什么Method Swizzling应该在dispatch\_once中完成？
   * 由于 swizzling 改变了全局的状态，所以我们需要确保每个预防措施在运行时都是可用的。原子操作就是这样一个用于确保代码只会被执行一次的预防措施，就算是在不同的线程中也能确保代码只执行一次。Grand Central Dispatch 的 dispatch\_once 满足了所需要的需求，并且应该被当做使用 swizzling 的初始化单例方法的标准。
   * dispatch\_once保证方法交叉只交叉一次

> write by coderiding，erliucxy 中山 东区 广东 2018-02-23- 17：19：20

## 第四部分：疑问

### 4.1 Method Swizzling危险吗？

[What are the Dangers of Method Swizzling in Objective C?](https://stackoverflow.com/questions/5339276/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)
* Difficult to debug
