GCDMulticastDelegate源代码阅读

最早注意到这个类是之前搞XMPP的时候,使用了一个开源库:https://github.com/robbiehanson/XMPPFramework,在研究其中的插件机制的时候,发现其中的核心就是这个类,是一个一对多的代理机制。之前读过一次源代码,但是一直比较忙,没有来得及总结。这两天抽出来时间读源代码,细看了一下这个类的实现,既简单又巧妙,写的非常好。还有一个类似的实现,MultiDelegate,已经封装为pods:https://github.com/aleph7/MultiDelegate

这个类实现的技术基础是OC的消息动态解析与转发,通过这些机制进行封装,很巧妙实现了一对多的delegate机制,避免了有这种需求的时候,都需要自己去维护这种逻辑的问题。代码不长,阅读难度也不是很大。调用的时候,核心是methodSignatureForSelector: -> forwardInvocation: -> doesNotRecognizeSelector逻辑的重写,关于这部分,可以参考我之前关于OC消息动态解析与转发的一篇博客,http://blog.csdn.net/colorapp/article/details/43951709。这里需要注意的一点是,GCDMulticastDelegate不能使用respondToSelector进行检测,但是其内部已经对不能实现调用的方法进行了处理,不会引发Exception。

总体而言,是其中的代码实现思路很有意思,可以作为参考。代码内部有几个需要注意的地方:
1. methodSignatureForSelector: 这个方法必须实现,否则在forwardInvocation之前会引发crash,这里的实现方式是获取其中包含其他的类的method的实现,可以借鉴一下。
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
	for (GCDMulticastDelegateNode *node in delegateNodes)
	{
		id nodeDelegate = node.delegate;
		#if __has_feature(objc_arc_weak) && !TARGET_OS_IPHONE
		if (nodeDelegate == [NSNull null])
			nodeDelegate = node.unsafeDelegate;
		#endif
		
		NSMethodSignature *result = [nodeDelegate methodSignatureForSelector:aSelector];
		
		if (result != nil)
		{
			return result;
		}
	}
	
	// This causes a crash...
	// return [super methodSignatureForSelector:aSelector];
	
	// This also causes a crash...
	// return nil;
	
	return [[self class] instanceMethodSignatureForSelector:@selector(doNothing)];
}



2. duplicateInvocation: 这个方法的实现比较有意思,可以作为参考,后续可以用到。
- (NSInvocation *)duplicateInvocation:(NSInvocation *)origInvocation
{
	NSMethodSignature *methodSignature = [origInvocation methodSignature];
	
	NSInvocation *dupInvocation = [NSInvocation invocationWithMethodSignature:methodSignature];
	[dupInvocation setSelector:[origInvocation selector]];
	
	NSUInteger i, count = [methodSignature numberOfArguments];
	for (i = 2; i < count; i++)
	{
		const char *type = [methodSignature getArgumentTypeAtIndex:i];
		
		if (*type == *@encode(BOOL))
		{
			BOOL value;
			[origInvocation getArgument:&value atIndex:i];
			[dupInvocation setArgument:&value atIndex:i];
		}
		else if (*type == *@encode(char) || *type == *@encode(unsigned char))
		{
			char value;
			[origInvocation getArgument:&value atIndex:i];
			[dupInvocation setArgument:&value atIndex:i];
		}
		else if (*type == *@encode(short) || *type == *@encode(unsigned short))
		{
			short value;
			[origInvocation getArgument:&value atIndex:i];
			[dupInvocation setArgument:&value atIndex:i];
		}
		else if (*type == *@encode(int) || *type == *@encode(unsigned int))
		{
			int value;
			[origInvocation getArgument:&value atIndex:i];
			[dupInvocation setArgument:&value atIndex:i];
		}
		else if (*type == *@encode(long) || *type == *@encode(unsigned long))
		{
			long value;
			[origInvocation getArgument:&value atIndex:i];
			[dupInvocation setArgument:&value atIndex:i];
		}
		else if (*type == *@encode(long long) || *type == *@encode(unsigned long long))
		{
			long long value;
			[origInvocation getArgument:&value atIndex:i];
			[dupInvocation setArgument:&value atIndex:i];
		}
		else if (*type == *@encode(double))
		{
			double value;
			[origInvocation getArgument:&value atIndex:i];
			[dupInvocation setArgument:&value atIndex:i];
		}
		else if (*type == *@encode(float))
		{
			float value;
			[origInvocation getArgument:&value atIndex:i];
			[dupInvocation setArgument:&value atIndex:i];
		}
		else if (*type == '@')
		{
			void *value;
			[origInvocation getArgument:&value atIndex:i];
			[dupInvocation setArgument:&value atIndex:i];
		}
		else if (*type == '^')
		{
			void *block;
			[origInvocation getArgument:&block atIndex:i];
			[dupInvocation setArgument:&block atIndex:i];
		}
		else
		{
			NSString *selectorStr = NSStringFromSelector([origInvocation selector]);
			
			NSString *format = @"Argument %lu to method %@ - Type(%c) not supported";
			NSString *reason = [NSString stringWithFormat:format, (unsigned long)(i - 2), selectorStr, *type];
			
			[[NSException exceptionWithName:NSInvalidArgumentException reason:reason userInfo:nil] raise];
		}
	}
	
	[dupInvocation retainArguments];
	
	return dupInvocation;
}



你可能感兴趣的:(GCDMulticastDelegate源代码阅读)