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
  • 相关阅读:
    vuejs开发环境搭建
    贝塞尔曲线(cubic bezier)
    解决安装mysql的”A Windows service with the name MySQL already exists.“问题
    display:inline-block的间隙问题和解决办法
    限制两行显示,超出部分省略号
    border-radius四个值的问题
    PHP环境搭建
    CSS3属性box-sizing
    -webkit-tap-highlight-color
    gdb命令
  • 原文地址:https://www.cnblogs.com/madaha/p/9850966.html
Copyright © 2011-2022 走看看