# 介绍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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://coderiding.gitbook.io/dgjj/hu-lian-wang-gai-nian/qian-duan/ios/jie-shao-runtime.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
