iOS RunTime

2019-12-04  本文已影响0人  lczalh

1.方法交换

我们可以将Method Swizzling功能封装为类方法,作为NSObject的类别,这样我们后续调用也会方便些。

NSObject+Swizzling.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface NSObject (Swizzling)

/**
 方法交换

 @param originalSelector 源方法
 @param swizzledSelector 新方法
*/
+ (void)methodSwizzlingWithOriginalSelector:(SEL)originalSelector
                        andSwizzledSelector:(SEL)swizzledSelector;
                        
@end

NS_ASSUME_NONNULL_END

NSObject+Swizzling.m

#import "NSObject+Swizzling.h"
#import<objc/runtime.h>

@implementation NSObject (Swizzling)

+ (void)methodSwizzlingWithOriginalSelector:(SEL)originalSelector
                        andSwizzledSelector:(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

ViewController.m

#import "ViewController.h"
#import "NSObject+Swizzling.h"

@implementation ViewController (Swizzling)

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 交换对象方法
        [self methodSwizzlingWithOriginalSelector:@selector(viewWillAppear:) andSwizzledSelector:@selector(lczViewWillAppear:)];
        
        // 交换类方法
        [object_getClass([LibDevModel class]) methodSwizzlingWithOriginalSelector:@selector(onScanOver:) andSwizzledSelector:@selector(lczOnScanOver:)];
    });
}

2.消息转发

2.1 转发对象方法

LibDevModel+Swizzling.h

#import <DoorMasterSDK/DoorMasterSDK.h>

NS_ASSUME_NONNULL_BEGIN

@interface LibDevModel (Swizzling)
- (void)lczTimerCancel;
@end

NS_ASSUME_NONNULL_END

LibDevModel+Swizzling.m

#import "LibDevModel+Swizzling.h"
#import "GLGuardViewController.h"

@implementation LibDevModel (Swizzling)

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    // 获取 aSelector 的方法签名
    NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
    if (!signature) { // 如果无法获取
        // 判断 ViewController 是否能够响应 aSelector
        if ([ViewController instancesRespondToSelector:aSelector]) {
            // 获取 ViewController 中 aSelector 的方法签名
            signature = [ViewController instanceMethodSignatureForSelector:aSelector];
        }
    }
    return signature;
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    if ([ViewController instancesRespondToSelector:anInvocation.selector]) {
        [anInvocation invokeWithTarget:[ViewController new]];
    }
}

GLGuardViewController.m

#import "GLGuardViewController.h"


@interface GLGuardViewController ()

@end

@implementation GLGuardViewController

- (void)lczTimerCancel {
    NSLog(@"调用了");
}

2.2 转发类方法

LibDevModel+Swizzling.h

#import <DoorMasterSDK/DoorMasterSDK.h>

NS_ASSUME_NONNULL_BEGIN

@interface LibDevModel (Swizzling)
+ (void)lczTimerCancel;
@end

NS_ASSUME_NONNULL_END

LibDevModel+Swizzling.m

+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    // 获取 aSelector 的方法签名
    NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
    if (!signature) { // 如果无法获取
        // 判断 ViewController 是否能够响应 aSelector
        if ([GLGuardViewController respondsToSelector:aSelector]) {
            // 获取 ViewController 中 aSelector 的方法签名
            signature = [GLGuardViewController methodSignatureForSelector:aSelector];
        }
    }
    return signature;
}

+ (void)forwardInvocation:(NSInvocation *)anInvocation {
    if ([GLGuardViewController respondsToSelector:anInvocation.selector]) {
        [anInvocation invokeWithTarget:[GLGuardViewController class]];
    }
}

GLGuardViewController.m

#import "GLGuardViewController.h"


@interface GLGuardViewController ()

@end

@implementation GLGuardViewController

+ (void)lczTimerCancel {
    NSLog(@"调用了");
}
上一篇下一篇

猜你喜欢

热点阅读