iOS 代理实现一对多的通知

1,549 阅读1分钟

由于通知的一对多实现会产生内存泄漏、耦合关系太离散等一系列问题,有了想用代理实现一对多的方式通知。

需求:当多个类都服从了某个协议并且这些类实现了协议中同一个方法时,触发其中一个代理,所有类都将触发该代理方法。

思路:创建该代理的管理类,用于记录实现该类的target和实现消息转发。

// 把目标类添加到管理类里面
- (void)addTarget:(id)target protcol:(id)protocol {
    
    if (![self.refTargets.allKeys containsObject:protocol]) {
        NSPointerArray *targets = [NSPointerArray weakObjectsPointerArray];
        [targets addPointer:(__bridge void * _Nullable)(target)];
        [self.refTargets setObject:targets forKey:protocol];
    }
    else {
        NSPointerArray *targets = [self.refTargets objectForKey:protocol];
        if ([targets.allObjects containsObject:target]) {
            return;
        }
        [targets addPointer:(__bridge void * _Nullable)(target)];
    }
}


// 方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    NSMethodSignature *sig = [super methodSignatureForSelector:aSelector];
    if (!sig) {
        for (id key in self.refTargets.allKeys) {
            NSPointerArray *targets = [self.refTargets objectForKey:key];
            for (id obj in targets) {
                if ((sig = [obj methodSignatureForSelector:aSelector])) {
                    break;
                }
            }
        }
    }
    return sig;
}

// 方法转发
- (void)forwardInvocation:(NSInvocation *)anInvocation {
    for (NSString* key in self.refTargets.allKeys) {
        NSPointerArray *targets = [self.refTargets objectForKey:key];
        const char *name = [key UTF8String];
        Protocol *protocol = objc_getProtocol(name);
        NSAssert(protocol!=nil, @"不存在的协议");
        struct objc_method_description protocol_method_description =  protocol_getMethodDescription(protocol, anInvocation.selector, YES, YES);
        if (protocol_method_description.name!=nil) {
            for (id obj in targets) {
                if ([obj respondsToSelector:anInvocation.selector]) {
                    [anInvocation invokeWithTarget:obj];
                }
            }
        }
    }
}


// 移除某个目标类
- (void)removeProtocol:(id)protocol{
    if ([self.refTargets.allKeys containsObject:protocol]) {
        [_refTargets removeObjectForKey:protocol];
    }
}

// 移除所有目标类
- (void)removeAll{
    [self.refTargets removeAllObjects];
}

// 判段目标类否是实现该协议
- (BOOL)respondsToSelector:(SEL)aSelector {
    if ([super respondsToSelector:aSelector]) {
        return YES;
    }
    
    for (id key in self.refTargets.allKeys) {
        NSPointerArray *targets = [self.refTargets objectForKey:key];
        for (id obj  in targets) {
            if ([obj respondsToSelector:aSelector]) {
                return YES;
            }
        }
    }
    return NO;
}