DKClassInfo.m 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. //
  2. // DKClassInfo.m
  3. // DKModel
  4. //
  5. // Created by dongke on 17/5/9.
  6. // Copyright (c) 2017 dongke.
  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 "DKClassInfo.h"
  12. #import <objc/runtime.h>
  13. DKEncodingType DKEncodingGetType(const char *typeEncoding) {
  14. char *type = (char *)typeEncoding;
  15. if (!type) return DKEncodingTypeUnknown;
  16. size_t len = strlen(type);
  17. if (len == 0) return DKEncodingTypeUnknown;
  18. DKEncodingType qualifier = 0;
  19. bool prefix = true;
  20. while (prefix) {
  21. switch (*type) {
  22. case 'r': {
  23. qualifier |= DKEncodingTypeQualifierConst;
  24. type++;
  25. } break;
  26. case 'n': {
  27. qualifier |= DKEncodingTypeQualifierIn;
  28. type++;
  29. } break;
  30. case 'N': {
  31. qualifier |= DKEncodingTypeQualifierInout;
  32. type++;
  33. } break;
  34. case 'o': {
  35. qualifier |= DKEncodingTypeQualifierOut;
  36. type++;
  37. } break;
  38. case 'O': {
  39. qualifier |= DKEncodingTypeQualifierBycopy;
  40. type++;
  41. } break;
  42. case 'R': {
  43. qualifier |= DKEncodingTypeQualifierByref;
  44. type++;
  45. } break;
  46. case 'V': {
  47. qualifier |= DKEncodingTypeQualifierOneway;
  48. type++;
  49. } break;
  50. default: { prefix = false; } break;
  51. }
  52. }
  53. len = strlen(type);
  54. if (len == 0) return DKEncodingTypeUnknown | qualifier;
  55. switch (*type) {
  56. case 'v': return DKEncodingTypeVoid | qualifier;
  57. case 'B': return DKEncodingTypeBool | qualifier;
  58. case 'c': return DKEncodingTypeInt8 | qualifier;
  59. case 'C': return DKEncodingTypeUInt8 | qualifier;
  60. case 's': return DKEncodingTypeInt16 | qualifier;
  61. case 'S': return DKEncodingTypeUInt16 | qualifier;
  62. case 'i': return DKEncodingTypeInt32 | qualifier;
  63. case 'I': return DKEncodingTypeUInt32 | qualifier;
  64. case 'l': return DKEncodingTypeInt32 | qualifier;
  65. case 'L': return DKEncodingTypeUInt32 | qualifier;
  66. case 'q': return DKEncodingTypeInt64 | qualifier;
  67. case 'Q': return DKEncodingTypeUInt64 | qualifier;
  68. case 'f': return DKEncodingTypeFloat | qualifier;
  69. case 'd': return DKEncodingTypeDouble | qualifier;
  70. case 'D': return DKEncodingTypeLongDouble | qualifier;
  71. case '#': return DKEncodingTypeClass | qualifier;
  72. case ':': return DKEncodingTypeSEL | qualifier;
  73. case '*': return DKEncodingTypeCString | qualifier;
  74. case '^': return DKEncodingTypePointer | qualifier;
  75. case '[': return DKEncodingTypeCArray | qualifier;
  76. case '(': return DKEncodingTypeUnion | qualifier;
  77. case '{': return DKEncodingTypeStruct | qualifier;
  78. case '@': {
  79. if (len == 2 && *(type + 1) == '?')
  80. return DKEncodingTypeBlock | qualifier;
  81. else
  82. return DKEncodingTypeObject | qualifier;
  83. }
  84. default: return DKEncodingTypeUnknown | qualifier;
  85. }
  86. }
  87. @implementation DKClassIvarInfo
  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 = DKEncodingGetType(typeEncoding);
  101. }
  102. return self;
  103. }
  104. @end
  105. @implementation DKClassMethodInfo
  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 alloc]init];
  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 DKClassPropertyInfo
  140. - (instancetype)initWithProperty:(objc_property_t)property {
  141. if (!property) return nil;
  142. self = [self init];
  143. _property = property;
  144. const char *name = property_getName(property);
  145. if (name) {
  146. _name = [NSString stringWithUTF8String:name];
  147. }
  148. DKEncodingType 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 = DKEncodingGetType(attrs[i].value);
  157. if ((type & DKEncodingTypeMask) == DKEncodingTypeObject) {
  158. size_t len = strlen(attrs[i].value);
  159. if (len > 3) {
  160. char name[len - 2];
  161. name[len - 3] = '\0';
  162. memcpy(name, attrs[i].value + 2, len - 3);
  163. _cls = objc_getClass(name);
  164. }
  165. }
  166. }
  167. } break;
  168. case 'V': { // Instance variable
  169. if (attrs[i].value) {
  170. _ivarName = [NSString stringWithUTF8String:attrs[i].value];
  171. }
  172. } break;
  173. case 'R': {
  174. type |= DKEncodingTypePropertyReadonly;
  175. } break;
  176. case 'C': {
  177. type |= DKEncodingTypePropertyCopy;
  178. } break;
  179. case '&': {
  180. type |= DKEncodingTypePropertyRetain;
  181. } break;
  182. case 'N': {
  183. type |= DKEncodingTypePropertyNonatomic;
  184. } break;
  185. case 'D': {
  186. type |= DKEncodingTypePropertyDynamic;
  187. } break;
  188. case 'W': {
  189. type |= DKEncodingTypePropertyWeak;
  190. } break;
  191. case 'G': {
  192. type |= DKEncodingTypePropertyCustomGetter;
  193. if (attrs[i].value) {
  194. _getter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);
  195. }
  196. } break;
  197. case 'S': {
  198. type |= DKEncodingTypePropertyCustomSetter;
  199. if (attrs[i].value) {
  200. _setter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);
  201. }
  202. } // break; commented for code coverage in next line
  203. default: break;
  204. }
  205. }
  206. if (attrs) {
  207. free(attrs);
  208. attrs = NULL;
  209. }
  210. _type = type;
  211. if (_name.length) {
  212. if (!_getter) {
  213. _getter = NSSelectorFromString(_name);
  214. }
  215. if (!_setter) {
  216. _setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:", [_name substringToIndex:1].uppercaseString, [_name substringFromIndex:1]]);
  217. }
  218. }
  219. return self;
  220. }
  221. @end
  222. @implementation DKClassInfo {
  223. BOOL _needUpdate;
  224. }
  225. - (instancetype)initWithClass:(Class)cls {
  226. if (!cls) return nil;
  227. self = [super init];
  228. _cls = cls;
  229. _superCls = class_getSuperclass(cls);
  230. _isMeta = class_isMetaClass(cls);
  231. if (!_isMeta) {
  232. _metaCls = objc_getMetaClass(class_getName(cls));
  233. }
  234. _name = NSStringFromClass(cls);
  235. [self _update];
  236. _superClassInfo = [self.class classInfoWithClass:_superCls];
  237. return self;
  238. }
  239. - (void)_update {
  240. _ivarInfos = nil;
  241. _methodInfos = nil;
  242. _propertyInfos = nil;
  243. Class cls = self.cls;
  244. unsigned int methodCount = 0;
  245. Method *methods = class_copyMethodList(cls, &methodCount);
  246. if (methods) {
  247. NSMutableDictionary *methodInfos = [NSMutableDictionary new];
  248. _methodInfos = methodInfos;
  249. for (unsigned int i = 0; i < methodCount; i++) {
  250. DKClassMethodInfo *info = [[DKClassMethodInfo alloc] initWithMethod:methods[i]];
  251. if (info.name) methodInfos[info.name] = info;
  252. }
  253. free(methods);
  254. }
  255. unsigned int propertyCount = 0;
  256. objc_property_t *properties = class_copyPropertyList(cls, &propertyCount);
  257. if (properties) {
  258. NSMutableDictionary *propertyInfos = [NSMutableDictionary new];
  259. _propertyInfos = propertyInfos;
  260. for (unsigned int i = 0; i < propertyCount; i++) {
  261. DKClassPropertyInfo *info = [[DKClassPropertyInfo alloc] initWithProperty:properties[i]];
  262. if (info.name) propertyInfos[info.name] = info;
  263. }
  264. free(properties);
  265. }
  266. unsigned int ivarCount = 0;
  267. Ivar *ivars = class_copyIvarList(cls, &ivarCount);
  268. if (ivars) {
  269. NSMutableDictionary *ivarInfos = [NSMutableDictionary new];
  270. _ivarInfos = ivarInfos;
  271. for (unsigned int i = 0; i < ivarCount; i++) {
  272. DKClassIvarInfo *info = [[DKClassIvarInfo alloc] initWithIvar:ivars[i]];
  273. if (info.name) ivarInfos[info.name] = info;
  274. }
  275. free(ivars);
  276. }
  277. if (!_ivarInfos) _ivarInfos = @{};
  278. if (!_methodInfos) _methodInfos = @{};
  279. if (!_propertyInfos) _propertyInfos = @{};
  280. _needUpdate = NO;
  281. }
  282. - (void)setNeedUpdate {
  283. _needUpdate = YES;
  284. }
  285. - (BOOL)needUpdate {
  286. return _needUpdate;
  287. }
  288. + (instancetype)classInfoWithClass:(Class)cls {
  289. if (!cls) return nil;
  290. static CFMutableDictionaryRef classCache;
  291. static CFMutableDictionaryRef metaCache;
  292. static dispatch_once_t onceToken;
  293. static dispatch_semaphore_t lock;
  294. dispatch_once(&onceToken, ^{
  295. classCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
  296. metaCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
  297. lock = dispatch_semaphore_create(1);
  298. });
  299. dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
  300. DKClassInfo *info = CFDictionaryGetValue(class_isMetaClass(cls) ? metaCache : classCache, (__bridge const void *)(cls));
  301. if (info && info->_needUpdate) {
  302. [info _update];
  303. }
  304. dispatch_semaphore_signal(lock);
  305. if (!info) {
  306. info = [[DKClassInfo alloc] initWithClass:cls];
  307. if (info) {
  308. dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
  309. CFDictionarySetValue(info.isMeta ? metaCache : classCache, (__bridge const void *)(cls), (__bridge const void *)(info));
  310. dispatch_semaphore_signal(lock);
  311. }
  312. }
  313. return info;
  314. }
  315. + (instancetype)classInfoWithClassName:(NSString *)className {
  316. Class cls = NSClassFromString(className);
  317. return [self classInfoWithClass:cls];
  318. }
  319. @end