酷店

YYClassInfo.m 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. //
  2. // YYClassInfo.m
  3. // YYModel <https://github.com/ibireme/YYModel>
  4. //
  5. // Created by ibireme on 15/5/9.
  6. // Copyright (c) 2015 ibireme.
  7. //
  8. // This source code is licensed under the MIT-style license found in the
  9. // LICENSE file in the root directory of this source tree.
  10. //
  11. #import "YYClassInfo.h"
  12. #import <objc/runtime.h>
  13. YYEncodingType YYEncodingGetType(const char *typeEncoding) {
  14. char *type = (char *)typeEncoding;
  15. if (!type) return YYEncodingTypeUnknown;
  16. size_t len = strlen(type);
  17. if (len == 0) return YYEncodingTypeUnknown;
  18. YYEncodingType qualifier = 0;
  19. bool prefix = true;
  20. while (prefix) {
  21. switch (*type) {
  22. case 'r': {
  23. qualifier |= YYEncodingTypeQualifierConst;
  24. type++;
  25. } break;
  26. case 'n': {
  27. qualifier |= YYEncodingTypeQualifierIn;
  28. type++;
  29. } break;
  30. case 'N': {
  31. qualifier |= YYEncodingTypeQualifierInout;
  32. type++;
  33. } break;
  34. case 'o': {
  35. qualifier |= YYEncodingTypeQualifierOut;
  36. type++;
  37. } break;
  38. case 'O': {
  39. qualifier |= YYEncodingTypeQualifierBycopy;
  40. type++;
  41. } break;
  42. case 'R': {
  43. qualifier |= YYEncodingTypeQualifierByref;
  44. type++;
  45. } break;
  46. case 'V': {
  47. qualifier |= YYEncodingTypeQualifierOneway;
  48. type++;
  49. } break;
  50. default: { prefix = false; } break;
  51. }
  52. }
  53. len = strlen(type);
  54. if (len == 0) return YYEncodingTypeUnknown | qualifier;
  55. switch (*type) {
  56. case 'v': return YYEncodingTypeVoid | qualifier;
  57. case 'B': return YYEncodingTypeBool | qualifier;
  58. case 'c': return YYEncodingTypeInt8 | qualifier;
  59. case 'C': return YYEncodingTypeUInt8 | qualifier;
  60. case 's': return YYEncodingTypeInt16 | qualifier;
  61. case 'S': return YYEncodingTypeUInt16 | qualifier;
  62. case 'i': return YYEncodingTypeInt32 | qualifier;
  63. case 'I': return YYEncodingTypeUInt32 | qualifier;
  64. case 'l': return YYEncodingTypeInt32 | qualifier;
  65. case 'L': return YYEncodingTypeUInt32 | qualifier;
  66. case 'q': return YYEncodingTypeInt64 | qualifier;
  67. case 'Q': return YYEncodingTypeUInt64 | qualifier;
  68. case 'f': return YYEncodingTypeFloat | qualifier;
  69. case 'd': return YYEncodingTypeDouble | qualifier;
  70. case 'D': return YYEncodingTypeLongDouble | qualifier;
  71. case '#': return YYEncodingTypeClass | qualifier;
  72. case ':': return YYEncodingTypeSEL | qualifier;
  73. case '*': return YYEncodingTypeCString | qualifier;
  74. case '^': return YYEncodingTypePointer | qualifier;
  75. case '[': return YYEncodingTypeCArray | qualifier;
  76. case '(': return YYEncodingTypeUnion | qualifier;
  77. case '{': return YYEncodingTypeStruct | qualifier;
  78. case '@': {
  79. if (len == 2 && *(type + 1) == '?')
  80. return YYEncodingTypeBlock | qualifier;
  81. else
  82. return YYEncodingTypeObject | qualifier;
  83. }
  84. default: return YYEncodingTypeUnknown | qualifier;
  85. }
  86. }
  87. @implementation YYClassIvarInfo
  88. - (instancetype)initWithIvar:(Ivar)ivar {
  89. if (!ivar) return nil;
  90. self = [super init];
  91. _ivar = ivar;
  92. const char *name = ivar_getName(ivar);
  93. if (name) {
  94. _name = [NSString stringWithUTF8String:name];
  95. }
  96. _offset = ivar_getOffset(ivar);
  97. const char *typeEncoding = ivar_getTypeEncoding(ivar);
  98. if (typeEncoding) {
  99. _typeEncoding = [NSString stringWithUTF8String:typeEncoding];
  100. _type = YYEncodingGetType(typeEncoding);
  101. }
  102. return self;
  103. }
  104. @end
  105. @implementation YYClassMethodInfo
  106. - (instancetype)initWithMethod:(Method)method {
  107. if (!method) return nil;
  108. self = [super init];
  109. _method = method;
  110. _sel = method_getName(method);
  111. _imp = method_getImplementation(method);
  112. const char *name = sel_getName(_sel);
  113. if (name) {
  114. _name = [NSString stringWithUTF8String:name];
  115. }
  116. const char *typeEncoding = method_getTypeEncoding(method);
  117. if (typeEncoding) {
  118. _typeEncoding = [NSString stringWithUTF8String:typeEncoding];
  119. }
  120. char *returnType = method_copyReturnType(method);
  121. if (returnType) {
  122. _returnTypeEncoding = [NSString stringWithUTF8String:returnType];
  123. free(returnType);
  124. }
  125. unsigned int argumentCount = method_getNumberOfArguments(method);
  126. if (argumentCount > 0) {
  127. NSMutableArray *argumentTypes = [NSMutableArray new];
  128. for (unsigned int i = 0; i < argumentCount; i++) {
  129. char *argumentType = method_copyArgumentType(method, i);
  130. NSString *type = argumentType ? [NSString stringWithUTF8String:argumentType] : nil;
  131. [argumentTypes addObject:type ? type : @""];
  132. if (argumentType) free(argumentType);
  133. }
  134. _argumentTypeEncodings = argumentTypes;
  135. }
  136. return self;
  137. }
  138. @end
  139. @implementation YYClassPropertyInfo
  140. - (instancetype)initWithProperty:(objc_property_t)property {
  141. if (!property) return nil;
  142. self = [super init];
  143. _property = property;
  144. const char *name = property_getName(property);
  145. if (name) {
  146. _name = [NSString stringWithUTF8String:name];
  147. }
  148. YYEncodingType type = 0;
  149. unsigned int attrCount;
  150. objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount);
  151. for (unsigned int i = 0; i < attrCount; i++) {
  152. switch (attrs[i].name[0]) {
  153. case 'T': { // Type encoding
  154. if (attrs[i].value) {
  155. _typeEncoding = [NSString stringWithUTF8String:attrs[i].value];
  156. type = YYEncodingGetType(attrs[i].value);
  157. if ((type & YYEncodingTypeMask) == YYEncodingTypeObject && _typeEncoding.length) {
  158. NSScanner *scanner = [NSScanner scannerWithString:_typeEncoding];
  159. if (![scanner scanString:@"@\"" intoString:NULL]) continue;
  160. NSString *clsName = nil;
  161. if ([scanner scanUpToCharactersFromSet: [NSCharacterSet characterSetWithCharactersInString:@"\"<"] intoString:&clsName]) {
  162. if (clsName.length) _cls = objc_getClass(clsName.UTF8String);
  163. }
  164. NSMutableArray *protocols = nil;
  165. while ([scanner scanString:@"<" intoString:NULL]) {
  166. NSString* protocol = nil;
  167. if ([scanner scanUpToString:@">" intoString: &protocol]) {
  168. if (protocol.length) {
  169. if (!protocols) protocols = [NSMutableArray new];
  170. [protocols addObject:protocol];
  171. }
  172. }
  173. [scanner scanString:@">" intoString:NULL];
  174. }
  175. _protocols = protocols;
  176. }
  177. }
  178. } break;
  179. case 'V': { // Instance variable
  180. if (attrs[i].value) {
  181. _ivarName = [NSString stringWithUTF8String:attrs[i].value];
  182. }
  183. } break;
  184. case 'R': {
  185. type |= YYEncodingTypePropertyReadonly;
  186. } break;
  187. case 'C': {
  188. type |= YYEncodingTypePropertyCopy;
  189. } break;
  190. case '&': {
  191. type |= YYEncodingTypePropertyRetain;
  192. } break;
  193. case 'N': {
  194. type |= YYEncodingTypePropertyNonatomic;
  195. } break;
  196. case 'D': {
  197. type |= YYEncodingTypePropertyDynamic;
  198. } break;
  199. case 'W': {
  200. type |= YYEncodingTypePropertyWeak;
  201. } break;
  202. case 'G': {
  203. type |= YYEncodingTypePropertyCustomGetter;
  204. if (attrs[i].value) {
  205. _getter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);
  206. }
  207. } break;
  208. case 'S': {
  209. type |= YYEncodingTypePropertyCustomSetter;
  210. if (attrs[i].value) {
  211. _setter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);
  212. }
  213. } // break; commented for code coverage in next line
  214. default: break;
  215. }
  216. }
  217. if (attrs) {
  218. free(attrs);
  219. attrs = NULL;
  220. }
  221. _type = type;
  222. if (_name.length) {
  223. if (!_getter) {
  224. _getter = NSSelectorFromString(_name);
  225. }
  226. if (!_setter) {
  227. _setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:", [_name substringToIndex:1].uppercaseString, [_name substringFromIndex:1]]);
  228. }
  229. }
  230. return self;
  231. }
  232. @end
  233. @implementation YYClassInfo {
  234. BOOL _needUpdate;
  235. }
  236. - (instancetype)initWithClass:(Class)cls {
  237. if (!cls) return nil;
  238. self = [super init];
  239. _cls = cls;
  240. _superCls = class_getSuperclass(cls);
  241. _isMeta = class_isMetaClass(cls);
  242. if (!_isMeta) {
  243. _metaCls = objc_getMetaClass(class_getName(cls));
  244. }
  245. _name = NSStringFromClass(cls);
  246. [self _update];
  247. _superClassInfo = [self.class classInfoWithClass:_superCls];
  248. return self;
  249. }
  250. - (void)_update {
  251. _ivarInfos = nil;
  252. _methodInfos = nil;
  253. _propertyInfos = nil;
  254. Class cls = self.cls;
  255. unsigned int methodCount = 0;
  256. Method *methods = class_copyMethodList(cls, &methodCount);
  257. if (methods) {
  258. NSMutableDictionary *methodInfos = [NSMutableDictionary new];
  259. _methodInfos = methodInfos;
  260. for (unsigned int i = 0; i < methodCount; i++) {
  261. YYClassMethodInfo *info = [[YYClassMethodInfo alloc] initWithMethod:methods[i]];
  262. if (info.name) methodInfos[info.name] = info;
  263. }
  264. free(methods);
  265. }
  266. unsigned int propertyCount = 0;
  267. objc_property_t *properties = class_copyPropertyList(cls, &propertyCount);
  268. if (properties) {
  269. NSMutableDictionary *propertyInfos = [NSMutableDictionary new];
  270. _propertyInfos = propertyInfos;
  271. for (unsigned int i = 0; i < propertyCount; i++) {
  272. YYClassPropertyInfo *info = [[YYClassPropertyInfo alloc] initWithProperty:properties[i]];
  273. if (info.name) propertyInfos[info.name] = info;
  274. }
  275. free(properties);
  276. }
  277. unsigned int ivarCount = 0;
  278. Ivar *ivars = class_copyIvarList(cls, &ivarCount);
  279. if (ivars) {
  280. NSMutableDictionary *ivarInfos = [NSMutableDictionary new];
  281. _ivarInfos = ivarInfos;
  282. for (unsigned int i = 0; i < ivarCount; i++) {
  283. YYClassIvarInfo *info = [[YYClassIvarInfo alloc] initWithIvar:ivars[i]];
  284. if (info.name) ivarInfos[info.name] = info;
  285. }
  286. free(ivars);
  287. }
  288. if (!_ivarInfos) _ivarInfos = @{};
  289. if (!_methodInfos) _methodInfos = @{};
  290. if (!_propertyInfos) _propertyInfos = @{};
  291. _needUpdate = NO;
  292. }
  293. - (void)setNeedUpdate {
  294. _needUpdate = YES;
  295. }
  296. - (BOOL)needUpdate {
  297. return _needUpdate;
  298. }
  299. + (instancetype)classInfoWithClass:(Class)cls {
  300. if (!cls) return nil;
  301. static CFMutableDictionaryRef classCache;
  302. static CFMutableDictionaryRef metaCache;
  303. static dispatch_once_t onceToken;
  304. static dispatch_semaphore_t lock;
  305. dispatch_once(&onceToken, ^{
  306. classCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
  307. metaCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
  308. lock = dispatch_semaphore_create(1);
  309. });
  310. dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
  311. YYClassInfo *info = CFDictionaryGetValue(class_isMetaClass(cls) ? metaCache : classCache, (__bridge const void *)(cls));
  312. if (info && info->_needUpdate) {
  313. [info _update];
  314. }
  315. dispatch_semaphore_signal(lock);
  316. if (!info) {
  317. info = [[YYClassInfo alloc] initWithClass:cls];
  318. if (info) {
  319. dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
  320. CFDictionarySetValue(info.isMeta ? metaCache : classCache, (__bridge const void *)(cls), (__bridge const void *)(info));
  321. dispatch_semaphore_signal(lock);
  322. }
  323. }
  324. return info;
  325. }
  326. + (instancetype)classInfoWithClassName:(NSString *)className {
  327. Class cls = NSClassFromString(className);
  328. return [self classInfoWithClass:cls];
  329. }
  330. @end