NSProxy 实现“多继承”

之前有在YYWeakProxy看到过 NSProxy 使用,可以解决NSTimer 对 target 是强引用的问题。主要使用 NSProxy 对 target 进行消息转发,并不会对 target 造成引用关系。那么 NSProxy 能对多个对象方法进行转发,就是 NSProxy 具有了多个对象的方法,可以实现“多继承”。

NSProxy是什么?

根据官方文档的解释,NSProxy是一个抽象超类,主要用于将一个信息,转发到一个真实对象中。

消息转发

在NSProxy中有这么一种转发方法:方法签名转发

- (void)forwardInvocation:(NSInvocation *)invocation;
- (nullable NSMethodSignature *)methodSignatureForSelector:(SEL)sel NS_SWIFT_UNAVAILABLE("NSInvocation and related APIs not available");

可以通过消息转发的方式,让抽象类将消息转发的真实类中。不过很快发现了一个问题,这些方法并不属于 Proxy ,不能直接调用。他们之间需要有一个桥梁来沟通,可以使用协议来约定,这样就可以直接进行调用操作。下面看下具体的实现:

实现“多继承”

Person.h

#import 

@protocol PersonPtotocol 

@property (nonatomic, copy) NSString *name;

- (NSString *)getPersonInfo;

@end

@interface Person : NSObject

@end

Person.m

#import "Person.h"

@implementation Person

@synthesize name = _name;

- (NSString *)getPersonInfo
{
    return [NSString stringWithFormat:@"person name: %@", _name];
}

@end

由与当前类中并没有 NSString *name 和 getPersonInfo方法,所以需要在实现文件中实现,这样Person才真正具有这两个方法。

BDExtends.m

Proxy中并不包含该方法,会直接进行方法签名,但是缺少签名的对象,所以就需要提前获取target,当然可以直接通过方法名称来确定target,不过比较麻烦,我这里就使用字典来表示他们之间的关系。

- (instancetype)initExtends:(NSArray *)extends
{
    NSMutableDictionary *tempMethodList = @{}.mutableCopy;
    
    [extends enumerateObjectsUsingBlock:^(NSString *_Nonnull objString, NSUInteger idx, BOOL * _Nonnull stop) {
        
        Class objClass = NSClassFromString(objString);
    
        NSString *des = [NSString stringWithFormat:@"can not find the class of %@", objString];
        NSAssert(objString != nil, des);
        
        
        id obj = [[objClass alloc] init];
        
        unsigned int numberOfMethods = 0;

        Method *methodList = class_copyMethodList(objClass, &numberOfMethods);
        
        for (int i = 0 ; i < numberOfMethods; i++) {
            
            Method tempMethod = methodList[i];
            
            SEL tempSel = method_getName(tempMethod);
            
            const char *tempMethodName = sel_getName(tempSel);
            
            [tempMethodList setObject:obj forKey:[NSString stringWithUTF8String:tempMethodName]];
    
        }
        
    }];
    
    self.methodList = [[NSDictionary alloc] initWithDictionary:tempMethodList copyItems:NO];
    
    return self;
}

获取target 和 act ,使用 class_copyMethodList 获取所有的方法列表,然后关联到自己创建的target上去,后面也可以使用方法名称直接获取对应的target,也是很不错的选择。


- (nullable NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
 NSString *methodName = NSStringFromSelector(sel);
    
    id target = [self.methodList valueForKey:methodName];
    
    if (target && [target respondsToSelector:sel])
    {
        return [target methodSignatureForSelector:sel];
    }
}

通过使用 methodSignatureForSelector ,获取了NSMethodSignature,方法也就进行了重新签名,下面就是转发实现:


- (void)forwardInvocation:(NSInvocation *)invocation
{
    SEL sel = invocation.selector;
    
    NSString *methodName = NSStringFromSelector(sel);
    
    id target = [self.methodList valueForKey:methodName];
    
    if (target && [target respondsToSelector:sel]) {
        
        [invocation invokeWithTarget:target];
    }

}

主要是通过 NSInvocation 给真实对象转发消息,获取target来进行方法转发。

最后看下demo吧

你可能感兴趣的:(NSProxy 实现“多继承”)