当前位置:   article > 正文

ios 黑魔法Swizzling的应用---分解ZFPlayer

ios magic swizzle

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  ----------\n %@", 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  ----------\n %@", class_getName(self.class), __func__,[exception callStackSymbols]);
            return @"";
        }
        @finally {}
    } else {
        return [self safe_ObjectAtIndex:index];
    }
    
}



@end

 

转载于:https://www.cnblogs.com/madaha/p/9850966.html

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/531543
推荐阅读
  

闽ICP备14008679号