上次讨论了
Class
作为 NSMutableDictionary
的 key
的情况,这次来看看
Protocol
作为NSMutableDictionary
的key
,会怎样。Protocol *protocol = @protocol(UIScrollViewDelegate); NSLog(@"class %@", NSStringFromClass([(id)protocol class])); NSLog(@"isKindOfClass %d", [(id)protocol isKindOfClass:[NSObject class]]); NSLog(@"conformsToProtocol %d", [(id)protocol conformsToProtocol:@protocol(NSCopying)]); NSMutableDictionary *dict = [NSMutableDictionary dictionary]; [dict setObject:@"" forKey:protocol];
很遗憾,尽管
Protocol *
也是个NSObject
对象,但并不遵循NSCopying
协议。所以就发生了崩溃。class Protocol isKindOfClass 1 conformsToProtocol 0 -[Protocol copyWithZone:]: unrecognized selector sent to instance 0x7fff8a490608
当然了,通过
NSStringFromProtocol
,我们还是可以达到目的。iOS 使用 Protocol 作为组件化解耦与通信的方式,大部分用的就是这种
NSMutableDictionary
+NSStringFromProtocol
的方案。比如BeeHive。问题也不大,只是对于这种底层框架,还是需要尽可能优化性能。
于是改用
CoreFoudation
的CFDictionary
。写个简单DEMO,对比一下性能。自定义3个
Protocol
。@protocol DWJProtocol0 <NSObject> @end @protocol DWJProtocol1 <NSObject> @end @protocol DWJProtocol2 <NSObject> @end
对
NSMutableDictionary
进入3次写入,并读取30万次,统计读取耗时。- (void)testNSDicionary { NSMutableDictionary *dict = [NSMutableDictionary dictionary]; [dict setObject:@"" forKey:NSStringFromProtocol(@protocol(DWJProtocol0))]; [dict setObject:@"" forKey:NSStringFromProtocol(@protocol(DWJProtocol1))]; [dict setObject:@"" forKey:NSStringFromProtocol(@protocol(DWJProtocol2))]; CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent(); for (int i = 0; i < 100000; i++) { NSString *object0 = [dict objectForKey:NSStringFromProtocol(@protocol(DWJProtocol0))]; NSString *object1 = [dict objectForKey:NSStringFromProtocol(@protocol(DWJProtocol1))]; NSString *object2 = [dict objectForKey:NSStringFromProtocol(@protocol(DWJProtocol2))]; } CFAbsoluteTime endTime = CFAbsoluteTimeGetCurrent(); NSLog(@"%@", @((endTime - startTime) * 1000)); }
同样对
CFMutableDictionaryRef
进入3次写入,并读取30万次,统计读取耗时。- (void)testCFDictionary { CFMutableDictionaryRef dict = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, NULL, &kCFTypeDictionaryValueCallBacks); CFDictionarySetValue(dict, (__bridge const void *)(@protocol(DWJProtocol0)), (__bridge const void *)(@"")); CFDictionarySetValue(dict, (__bridge const void *)(@protocol(DWJProtocol1)), (__bridge const void *)(@"")); CFDictionarySetValue(dict, (__bridge const void *)(@protocol(DWJProtocol2)), (__bridge const void *)(@"")); CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent(); for (int i = 0; i < 100000; i++) { NSString *object0 = CFDictionaryGetValue(dict, (__bridge const void *)(@protocol(DWJProtocol0))); NSString *object1 = CFDictionaryGetValue(dict, (__bridge const void *)(@protocol(DWJProtocol1))); NSString *object2 = CFDictionaryGetValue(dict, (__bridge const void *)(@protocol(DWJProtocol2))); } CFAbsoluteTime endTime = CFAbsoluteTimeGetCurrent(); NSLog(@"%@", @((endTime - startTime) * 1000)); CFRelease(dict); }
直接跑
Time Profiler
,使用Debug
模式运行在iOS 14.4.2
的iPhone 8
,对比耗时,以及观察调用堆栈。总耗时
CFMutableDictionaryRef
大概是NSMutableDictionary
的1/25,效果可观。原因如下- 不需要
NSString
的转换字符串操作
- 不需要对
key
的copy
操作,甚至没有对key
的retain
操作(keyCallBacks
为NULL
)
当然了,这里有个前提是,这个
Protocol *
的对象,在运行时里是唯一生成不变的。所以没必要转换为NSString。
Loading Comments...