zoukankan      html  css  js  c++  java
  • Runtime 函数 Swizzling 改变OC方法的调度顺序

    首先加入一个小知识:

    SEL、Method、IMP的含义及区别

    在运行时,类(Class)维护了一个消息分发列表来解决消息的正确发送。每一个消息列表的入口是一个方法(Method),这个方法映射了一对键值对,其中键是这个方法的名字(SEL),值是指向这个方法实现的函数指针 implementation(IMP)。
    伪代码表示:

    Class {
          MethodList (
                      Method{
                          SEL:IMP;
                      }
                      Method{
                          SEL:IMP;
                      }
                      );
          };

    Method Swizzling就是改变类的消息分发列表来让消息解析时从一个选择器(SEL)对应到另外一个的实现(IMP),同时将原始的方法实现混淆到一个新的选择器(SEL)。

    对Swizzling方法封装

    //

    //  NSObject+Swizzling.h

    //  Swizzling

    //

    //  Created by peter.zhang on 2016/12/14.

    //  Copyright © 2016年 Peter. All rights reserved.

    //

    #import <Foundation/Foundation.h>

    #import <objc/runtime.h>

    @interface NSObject (Swizzling)

    /**

     * Adds a new method to a class with a given name and implementation.

     *

     * @param originalSelector 原来的方法

     * @param swizzledSelector 替换成的方法

     *

    */

     + (void)methodSwizzlingWithOriginalSelector:(SEL)originalSelector

                             bySwizzledSelector:(SEL)swizzledSelector;

    @end

    //

    //  NSObject+Swizzling.m

    //  Swizzling

    //

    //  Created by peter.zhang on 2016/12/14.

    //  Copyright © 2016年 Peter. All rights reserved.

    //

    #import "NSObject+Swizzling.h"

    @implementation NSObject (Swizzling)

    + (void)methodSwizzlingWithOriginalSelector:(SEL)originalSelector bySwizzledSelector:(SEL)swizzledSelector{

        Class class = [self class];

        //原有方法

        Method originalMethod = class_getInstanceMethod(class, originalSelector);

        //替换原有方法的新方法

        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

        //先尝试給源SEL添加IMP,这里是为了避免源SEL没有实现IMP的情况

        BOOL didAddMethod = class_addMethod(class,originalSelector,

                                            method_getImplementation(swizzledMethod),

                                            method_getTypeEncoding(swizzledMethod));

        if (didAddMethod) {//添加成功:说明源SEL没有实现IMP,将源SEL的IMP替换到交换SEL的IMP

            class_replaceMethod(class,swizzledSelector,

                                method_getImplementation(originalMethod),

                                method_getTypeEncoding(originalMethod));

        } else {//添加失败:说明源SEL已经有IMP,直接将两个SEL的IMP交换即可

            method_exchangeImplementations(originalMethod, swizzledMethod);

        }

    }

    @end

    -------------------------------以上是对Swizzling方法封装类别--------------------------------

    runtime有很多用途:改变ViewController的生命周期、app热更新、改变系统方法调度(解决获取索引、添加、删除元素越界崩溃问题)等。今天主要说数组或者字典的越界crash问题。

    啥都不是了,你把Swizzling方法封装类别添加到工程中:

    以可变数组为例子:

    //

    //  NSMutableArray+Security.h

    //  Swizzling

    //

    //  Created by peter.zhang on 2016/12/14.

    //  Copyright © 2016年 Peter. All rights reserved.

    //

    #import <Foundation/Foundation.h>

    @interface NSMutableArray (Security)

    @end

    //

    //  NSMutableArray+Security.m

    //  Swizzling

    //

    //  Created by peter.zhang on 2016/12/14.

    //  Copyright © 2016年 Peter. All rights reserved.

    //

    #import "NSMutableArray+Security.h"

    #import "NSObject+Swizzling.h"

    @implementation NSMutableArray (Security)

    + (void)load {

        static dispatch_once_t onceToken;

        dispatch_once(&onceToken, ^{

            [objc_getClass("__NSArrayM") methodSwizzlingWithOriginalSelector:@selector(removeObject:) bySwizzledSelector:@selector(safeRemoveObject:) ];

            [objc_getClass("__NSArrayM") methodSwizzlingWithOriginalSelector:@selector(addObject:) bySwizzledSelector:@selector(safeAddObject:)];

            [objc_getClass("__NSArrayM") methodSwizzlingWithOriginalSelector:@selector(removeObjectAtIndex:) bySwizzledSelector:@selector(safeRemoveObjectAtIndex:)];

            [objc_getClass("__NSArrayM") methodSwizzlingWithOriginalSelector:@selector(insertObject:atIndex:) bySwizzledSelector:@selector(safeInsertObject:atIndex:)];

            [objc_getClass("__NSArrayM") methodSwizzlingWithOriginalSelector:@selector(objectAtIndex:) bySwizzledSelector:@selector(safeObjectAtIndex:)];

        });

    }

    - (void)safeAddObject:(id)obj {

        if (obj == nil) {

            NSLog(@"%s can add nil object into NSMutableArray", __FUNCTION__);

        } else {

            [self safeAddObject:obj];

        }

    }

    - (void)safeRemoveObject:(id)obj {

        if (obj == nil) {

            NSLog(@"%s call -removeObject:, but argument obj is nil", __FUNCTION__);

            return;

        }

        [self safeRemoveObject:obj];

    }

    - (void)safeInsertObject:(id)anObject atIndex:(NSUInteger)index {

        if (anObject == nil) {

            NSLog(@"%s can't insert nil into NSMutableArray", __FUNCTION__);

        } else if (index > self.count) {

            NSLog(@"%s index is invalid", __FUNCTION__);

        } else {

            [self safeInsertObject:anObject atIndex:index];

        }

    }

    - (id)safeObjectAtIndex:(NSUInteger)index {

        if (self.count == 0) {

            NSLog(@"%s can't get any object from an empty array", __FUNCTION__);

            return nil;

        }

        if (index > self.count) {

            NSLog(@"%s index out of bounds in array", __FUNCTION__);

            return nil;

        }

        return [self safeObjectAtIndex:index];

    }

    - (void)safeRemoveObjectAtIndex:(NSUInteger)index {

        if (self.count <= 0) {

            NSLog(@"%s can't get any object from an empty array", __FUNCTION__);

            return;

        }

        if (index >= self.count) {

            NSLog(@"%s index out of bound", __FUNCTION__);

            return;

        }

        [self safeRemoveObjectAtIndex:index];

    }

    @end

    然后你在工程中用可变数组的增删改查都不会crash了。

  • 相关阅读:
    封装ajax---基于axios
    XHR的理解和作用
    params和 query区别
    HTTP请求交互的基本过程
    http3次握手
    ES6----import * as 用法
    微信小程序真机调试:Setting data field "XXX" to undefined is invalid
    webpack详解-----optimization
    node跨域
    shell 的 功能语句--1
  • 原文地址:https://www.cnblogs.com/PeterWolf/p/6187168.html
Copyright © 2011-2022 走看看