zoukankan      html  css  js  c++  java
  • ios 黑魔法Swizzling的应用---分解ZFPlayer

    ios黑魔法实际上就是方法之间的调换

    Method_Swizzling是发生在运行时的,主要用于在运行时将两个Method进行交换,我们可以将Method Swizzle代码写到任何地方,但是只有在Method_Swizzling这段Method Swizzle代码执行完毕之后互换才起作用。Method_Swizzling交换时机:尽可能在+load方法中实现

    SEL 和 IMP 扩展

    SEL: 类成员方法的指针,不同于C的函数指针,函数指针保存着对应方法的内存地址,而SEL仅仅是一个方法对应编号

    IMP:函数指针,保存着对应方法的内存地址

    他们的关系是:每一个继承NSObject的类都可以获得runtime的支持,class类的本质是一个结构体,该结构体中有一个isa的指针(指向对象的类),这个结构体是编译器在类编译时为其创建的,这个结构体中还包含着一个Dispatch table(是一张SEL和IM对应的表),so,通过SEL的方法变化在Dispatch table找到编号对应的IMP,并执行对应的方法。

    Swizzling就可以利用了SEL编号可以对应不同的IMP来实现其功能。

    //
    //  NSArray+Swizzling.m
    //  MDHSwizzling
    //
    //  Created by Apple on 2018/10/25.
    //  Copyright © 2018年 马大哈. All rights reserved.
    //
    
    #import "NSArray+Swizzling.h"
    #import <objc/runtime.h>
    #import <UIKit/UIKit.h>
    
    @implementation NSArray (Swizzling)
    
    // load类方法: 创建分类的时候系统会自动调用
    + (void)load {
        
       
        // dispatch_once 用于保证swizzling效果只执行一次,反之函数多次调换(防止调换回来的情况)就无法起到应用作用。
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            
            /*
             NSArray                 __NSArrayI & __NSCFArray
             NSMutableArray          __NSArrayM
             NSDictionary            __NSDictionaryI
             NSMutableDictionary     __NSDictionaryM
             */
            
            // OC在编译时,会根据每个方法的名字,参数序列,生成一个唯一的整型标识(int类型的地址),这个标识就是SEL
            SEL originalSelector = @selector(objectAtIndex:);
            SEL swizzledSelector = NSSelectorFromString(@"safe_ObjectAtIndex:");
            // 获取实例方法 (类方法用 class_getClassMethod)
            Method originalMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), originalSelector);
            Method swizzledMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), swizzledSelector);
            
            SEL originalSelector1 = @selector(objectAtIndexedSubscript:);
            SEL swizzledSelector1 = NSSelectorFromString(@"safe_objectAtIndexedSubscript:");
    
            Method originalMethod1 = class_getInstanceMethod(objc_getClass("__NSArrayI"), originalSelector1);
            Method swizzledMethod1 = class_getInstanceMethod(objc_getClass("__NSArrayI"), swizzledSelector1);
    
            /*
             这里为什么不能 两个需要替换的方法全都使用 swizzledMethod 同一个呢 ?
             method_exchangeImplementations(originalMethod1, swizzledMethod);
             这回造成 objectAtIndexedSubscript 指向 objectAtIndex,而无法起到应用的作用
            
             原因如下:
             第一次交换,发送objectAtIndex消息却执行safe_ObjectAtIndex方法,发送safe_ObjectAtIndex消息却执行objectAtIndex
             第二次交换,objectAtIndexedSubscript发送执行safe_ObjectAtIndex,但是第一次的时候safe_ObjectAtIndex已经执行了objectAtIndex就会有问题
            
             所以,必须另起炉灶
             
             method_exchangeImplementations: 向array发送objectAtIndex执行的是safe_ObjectAtIndex替代函数进行异常处理,crash规避。
             */
            method_exchangeImplementations(originalMethod, swizzledMethod);
            method_exchangeImplementations(originalMethod1, swizzledMethod1);
            
            
            if (([UIDevice currentDevice].systemVersion.floatValue < 10.0f)){
                // ios10 以后解析的网络数组数据由 __NSCFArray 变成了 __NSArrayI
                Method oldObjectAtIndex3 = class_getInstanceMethod(objc_getClass("__NSCFArray"), @selector(objectAtIndex:));
                Method newObjectAtIndex3 = class_getInstanceMethod(objc_getClass("__NSCFArray"), @selector(safe_ObjectAtIndex:));
                method_exchangeImplementations(oldObjectAtIndex3, newObjectAtIndex3);
            }
        });
    }
    
    - (id)safe_ObjectAtIndex:(NSInteger)index {
        
        if (index > self.count - 1 || index < 0) {
            // 异常处理
            @try {
                return [self safe_ObjectAtIndex:index];
            }
            @catch (NSException *exception) {
                // 在崩溃后会打印崩溃信息
                NSLog(@"---------- %s Crash Because Method %s  ----------
     %@", class_getName(self.class), __func__,[exception callStackSymbols]);
                return @"";
            }
            @finally {}
        } else {
            return [self safe_ObjectAtIndex:index];
        }
    
    }
    
    - (id)safe_objectAtIndexedSubscript:(NSInteger)index {
        
        if (index > self.count - 1 || index < 0) {
            // 异常处理
            @try {
                return [self safe_ObjectAtIndex:index];
            }
            @catch (NSException *exception) {
                // 在崩溃后会打印崩溃信息
                NSLog(@"---------- %s Crash Because Method %s  ----------
     %@", class_getName(self.class), __func__,[exception callStackSymbols]);
                return @"";
            }
            @finally {}
        } else {
            return [self safe_ObjectAtIndex:index];
        }
        
    }
    
    
    
    @end
  • 相关阅读:
    OpenCV -- Mat 转 QImage 函数
    Qt--checkbox
    QT 发布release版本
    JS_0014:JS刷新页面
    JS_0013:JS获取文件后缀名
    JS_0012:JS从一个有规则的字符串中随机选择一个字符再循环生成一个新的无规则的字符串
    JQuery0016:JQuery等待页面全部加载完后执行代码块
    JQuery0015:JQuery查找指定元素并修改其属性
    JS_0011:通过JS给div添加html标签内容
    JS_0010:获取url中指定的参数
  • 原文地址:https://www.cnblogs.com/madaha/p/9850966.html
Copyright © 2011-2022 走看看