forked from mikeash/MAFuture
-
Notifications
You must be signed in to change notification settings - Fork 0
/
MAMethodSignatureCache.m
130 lines (110 loc) · 3.29 KB
/
MAMethodSignatureCache.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#import <objc/runtime.h>
#import "MAMethodSignatureCache.h"
@interface NSRecursiveLock (BlockAdditions)
- (void)ma_do: (dispatch_block_t)block;
@end
@implementation NSRecursiveLock (BlockAdditions)
- (void)ma_do: (dispatch_block_t)block
{
[self lock];
block();
[self unlock];
}
@end
@implementation MAMethodSignatureCache
+ (MAMethodSignatureCache *)sharedCache
{
static MAMethodSignatureCache *cache;
static dispatch_once_t pred;
dispatch_once(&pred, ^{ cache = [[self alloc] init]; });
return cache;
}
- (id)init
{
if((self = [super init]))
{
#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
_cache = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
#else
_cache = [[NSMapTable alloc]
initWithKeyOptions: NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality
valueOptions: NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPersonality
capacity: 0];
#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
_lock = [[NSRecursiveLock alloc] init];
[[NSNotificationCenter defaultCenter]
addObserver: self
selector: @selector( _clearCache )
name: NSBundleDidLoadNotification
object: nil];
}
return self;
}
- (void)_clearCache
{
[_lock ma_do: ^{
#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
CFDictionaryRemoveAllValues(_cache);
#else
[_cache removeAllObjects];
#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
}];
}
- (NSMethodSignature *)_searchAllClassesForSignature: (SEL)sel
{
int count = objc_getClassList(NULL, 0);
Class *classes = malloc(sizeof(*classes) * count);
objc_getClassList(classes, count);
NSMethodSignature *sig = nil;
for(int i = 0; i < count; i++)
{
Class c = classes[i];
if(class_getClassMethod(c, @selector(methodSignatureForSelector:)) && class_getClassMethod(c, @selector(instanceMethodSignatureForSelector:)))
{
NSMethodSignature *thisSig = [c methodSignatureForSelector: sel];
if(!sig)
sig = thisSig;
else if(sig && thisSig && ![sig isEqual: thisSig])
{
sig = nil;
break;
}
thisSig = [c instanceMethodSignatureForSelector: sel];
if(!sig)
sig = thisSig;
else if(sig && thisSig && ![sig isEqual: thisSig])
{
sig = nil;
break;
}
}
}
free(classes);
return sig;
}
- (NSMethodSignature *)cachedMethodSignatureForSelector: (SEL)sel
{
__block NSMethodSignature *sig = nil;
[_lock ma_do: ^{
#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
sig = CFDictionaryGetValue(_cache, sel);
#else
sig = [_cache objectForKey: (id)sel];
#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
if(!sig)
{
sig = [self _searchAllClassesForSignature: sel];
if(!sig)
sig = (id)[NSNull null];
#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
CFDictionarySetValue(_cache, sel, sig);
#else
[_cache setObject: sig forKey: (id)sel];
#endif //__IPHONE_OS_VERSION_MAX_ALLOWED
}
}];
if(sig == (id)[NSNull null])
sig = nil;
return sig;
}
@end