Skip to content

说一下 Method Swizzling? 说一下在实际开发中你在什么场景下使用过? #55

@tbfungeek

Description

@tbfungeek

Method Swizzling 在实际开发中用得还是蛮多的:

  • 在开源库AFNetworking就使用了Method Swizzling将
    将 NSURLSessionTask 的 resume 和 suspend 方法做了替换。替换的目的就是在这两个方法调用的时候发送暂停恢复的通知。
  • Aspect 和JSPatch 里面会通过Method Swizzling 来替换系统的 forwardInvocation 在这里重新修改消息分发的规则。

在实际开发中可以用于:

  • 防止数组取值时越界crash
#import "NSArray+Safe.h"
#import <objc/runtime.h>

@implementation NSArray (Safe)
+ (void)load {
    // objectAtIndex: 方式取元素
    Method originMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(objectAtIndex:));
    Method replacedMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(safeObjectAtIndex:));
    method_exchangeImplementations(originMethod, replacedMethod);
    //......
    // 直接通过数组下标的方式取元素
    Method originMethodBySub = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(objectAtIndexedSubscript:));
    Method replacedMethodBySub = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(safeObjectAtIndexedSubscript:));
    method_exchangeImplementations(originMethodBySub, replacedMethodBySub);
    //...
}

- (id)safeObjectAtIndex:(NSUInteger)index {
    if (self.count > index && self.count) {
        return [self safeObjectAtIndex:index];
    }
    return nil;
}

-(id)safeObjectAtIndexedSubscript:(NSUInteger)index {
    if (self.count > index && self.count) {
        return [self safeObjectAtIndexedSubscript:index];
    }
    return nil;
}
@end
  • 统计UIViewController加载次数
  • 统计UIButton点击次数
  • 统计自定义方法的执行
  • 统计UITableView的Cell点击事件
  • 处理按钮重复点击
+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Method originMethod =   class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));
        Method replacedMethod = class_getInstanceMethod(self, @selector(idl_SendAction:to:forEvent:));
        method_exchangeImplementations(originMethod, replacedMethod);
    });
}

- (void)idl_SendAction:(nonnull SEL)action to:(id)target forEvent:(UIEvent *)event {
    if (self.delayTime > 0) {
        if (self.userInteractionEnabled) {
            [self idl_SendAction:action to:target forEvent:event];
        }
        self.userInteractionEnabled = NO;
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
                                     (int64_t)(self.delayTime * NSEC_PER_SEC)),
                                     dispatch_get_main_queue(), ^{
                                         self.userInteractionEnabled = YES;
                                     });
    }else{
        [self idl_SendAction:action to:target forEvent:event];
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions