iOS runtime实际使用场景汇总

69 阅读2分钟

1. 方法交换

+ (void)load {
    Method originalMethod = class_getInstanceMethod(self, @selector(setText:));
    Method customMethod = class_getInstanceMethod(self, @selector(customSetText:));
    IMP originalImp = method_getImplementation(originalMethod);
    IMP customImp = method_getImplementation(customMethod);
    
    //直接添加setText:方法,注意用的是customSetText:方法的实现,如果成功了,就只需要替换customSetText:为setText:的实现就行,这样就实现了两者的交换
    //如果添加失败了,说明该方法已经重写或者当前操作是在分类中进行,这个时候需要交换两者的实现
    BOOL addResult = class_addMethod(self, @selector(setText:), customImp, "v@:@");
    if (addResult) {
        class_replaceMethod(self, @selector(customSetText:), originalImp, "v@:@");
    } else {
        method_exchangeImplementations(originalMethod, customMethod);
    }
}

- (void)customSetText:(NSString *)text {
    [self customSetText:text];
}

2. 动态添加方法

// 官方代码
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(resolveThisMethodDynamically)) {
        class_addMethod(self, sel, (IMP)dynamicMethodIMP, "v@:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

// OC方法本质上是默认带有self和_cmd的C方法
void dynamicMethodIMP(id self, SEL _cmd)
{
    NSLog(@"%s", __func__);
}

3. 利用消息转发解决NSTimer循环引用(参考YYWeakProxy

1. 核心代码
- (instancetype)initWithTarget:(id)target {
    _target = target;
    return self;
}

+ (instancetype)proxyWithTarget:(id)target {
    return [[YYWeakProxy alloc] initWithTarget:target];
}

- (id)forwardingTargetForSelector:(SEL)selector {
    return _target;
}
2. 使用
// 发送给YYWeakProxy对象的消息都会转发给self处理
_timer = [NSTimer timerWithTimeInterval:1 target:[YYWeakProxy proxyWithTarget:self] selector:@selector(timerAction) userInfo:nil repeats:YES];
[NSRunLoop.currentRunLoop addTimer:_timer forMode:NSRunLoopCommonModes];
[NSRunLoop.currentRunLoop run];

4. 获取类的所有属性,用于字典和模型映射

Class cls = [Person class];
do {
    NSLog(@"class: %@", NSStringFromClass(cls));
    unsigned int outCount = 0;
    objc_property_t *propertyList = class_copyPropertyList(cls, &outCount);
    for (int i = 0; i < outCount; i++) {
        NSString *name = @(property_getName(propertyList[i]));
        NSLog(@"propertyName: %@", name);
    }
    cls = class_getSuperclass(cls); // 沿着继承链遍历
} while (![ViewController isClassFromFoundation:cls]); // 只需要遍历自定义的类

// 判断某个类是否来自Foundation
+ (BOOL)isClassFromFoundation:(Class)c
{
    if (c == [NSObject class]) return YES;
    
    static NSSet *foundationClasses;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 集合中没有NSObject,因为几乎所有的类都是继承自NSObject,具体是不是NSObject需要特殊判断
        foundationClasses = [NSSet setWithObjects:
                             [NSURL class],
                             [NSDate class],
                             [NSValue class],
                             [NSData class],
                             [NSError class],
                             [NSArray class],
                             [NSDictionary class],
                             [NSString class],
                             [NSAttributedString class], nil];
    });
    
    __block BOOL result = NO;
    [foundationClasses enumerateObjectsUsingBlock:^(Class foundationClass, BOOL *stop) {
        if ([c isSubclassOfClass:foundationClass]) {
            result = YES;
            *stop = YES;
        }
    }];
    return result;
}

未完待续。。。