iOS小知识之挽救实例方法和类方法的“优化”方案

850 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

我们知道如果想挽救实例方法和类方法,需要在类中实现resolveInstanceMethodresolveClassMethod方法。如果想对每一个类方法都进行挽救处理,则需要在每一个类中都实现这两个方法。如此繁琐的操作,有没有更好的实现方式呢?
对于实例方法的查找流程,通过类对象、父类、最后找到根类。而类方法的查找流程,通过元类、根元类、最后同样找到根类。
在根类中,无论是实例方法还是类方法,找不到时都会调用resolveInstanceMethod。所以我们只需要在NSObject中,实现resolveInstanceMethod方法,就可以对所有类的实例方法及类方法都进行挽救处理。
创建NSObject+LG分类,写入以下代码:

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

@implementation NSObject (LG) 

-(void)say666{ 
    NSLog(@"666");
}

+ (BOOL)resolveInstanceMethod:(SEL)sel{ 
    
    if(sel==@selector(sayNB)){ 
        NSLog(@"resolveInstanceMethod:%@,%@", self, NSStringFromSelector(sel)); 
        
        IMP imp = class_getMethodImplementation(self, @selector(say666)); 
        Method methodSay666 = class_getInstanceMethod(self, @selector(say666)); 
        const char *type = method_getTypeEncoding(methodSay666); 
        return class_addMethod(self, @selector(sayNB), imp, type); 
    }
    return NO; 
} 
@end

在main函数中,调用LGPersonsayNB类方法,同时调用实例对象per的sayNB方法

int main(int argc, const char * argv[]) { 
    @autoreleasepool { 
        
        [LGPerson sayNB];
        
        LGPerson *per= [LGPerson alloc]; 
        [per sayNB]; 
    }
    return 0; 
} 

-------------------------

//输出结果: 
实例方法-say666
实例方法-say666

此方案的优点:

  • 任意一个类,只要继承自NSObject,它的所有方法都可以被监听到
  • 我们可以将自定义方法按指定策略进行命名,然后按照相同策略进行监听,只要遇到符合策略的方法无法找到时,可以将其上报服务端,让开发者在第一时间得到问题的反馈
  • NSObject分类中对所有方法统一监听,这种方式符合AOP面向切面的设计模式
    • 传统OOP面向对象设计模式,虽然每一个对象的分工都非常明确,但他们之间一些相同行为,会导致大量的冗余代码。如果我们将其提取,创建公共类进行继承,势必造成强依赖与高耦合
    • AOP的优势,对于原始的类与对象无侵入,只要维护好NSObject分类中的监听方法即可 缺点:
  • 在监听方法中写入大量判断条件,不利于查找与维护
  • 所有的方法都被监听,其中呢包含了大量的系统方法,造成性能消耗
  • NSObject分类中监听,导致系统提供的消息转发流程无法触发 对于容错处理,我们应该给开发者更大的容错空间。所以我们使用AOP设计模式,提供的“优化”方案,在这个场景下并不是一个真正的好方案。