123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 |
- //
- // YYClassInfo.m
- // YYModel <https://github.com/ibireme/YYModel>
- //
- // Created by ibireme on 15/5/9.
- // Copyright (c) 2015 ibireme.
- //
- // This source code is licensed under the MIT-style license found in the
- // LICENSE file in the root directory of this source tree.
- //
- #import "YYClassInfo.h"
- #import <objc/runtime.h>
- YYEncodingType YYEncodingGetType(const char *typeEncoding) {
- char *type = (char *)typeEncoding;
- if (!type) return YYEncodingTypeUnknown;
- size_t len = strlen(type);
- if (len == 0) return YYEncodingTypeUnknown;
-
- YYEncodingType qualifier = 0;
- bool prefix = true;
- while (prefix) {
- switch (*type) {
- case 'r': {
- qualifier |= YYEncodingTypeQualifierConst;
- type++;
- } break;
- case 'n': {
- qualifier |= YYEncodingTypeQualifierIn;
- type++;
- } break;
- case 'N': {
- qualifier |= YYEncodingTypeQualifierInout;
- type++;
- } break;
- case 'o': {
- qualifier |= YYEncodingTypeQualifierOut;
- type++;
- } break;
- case 'O': {
- qualifier |= YYEncodingTypeQualifierBycopy;
- type++;
- } break;
- case 'R': {
- qualifier |= YYEncodingTypeQualifierByref;
- type++;
- } break;
- case 'V': {
- qualifier |= YYEncodingTypeQualifierOneway;
- type++;
- } break;
- default: { prefix = false; } break;
- }
- }
- len = strlen(type);
- if (len == 0) return YYEncodingTypeUnknown | qualifier;
- switch (*type) {
- case 'v': return YYEncodingTypeVoid | qualifier;
- case 'B': return YYEncodingTypeBool | qualifier;
- case 'c': return YYEncodingTypeInt8 | qualifier;
- case 'C': return YYEncodingTypeUInt8 | qualifier;
- case 's': return YYEncodingTypeInt16 | qualifier;
- case 'S': return YYEncodingTypeUInt16 | qualifier;
- case 'i': return YYEncodingTypeInt32 | qualifier;
- case 'I': return YYEncodingTypeUInt32 | qualifier;
- case 'l': return YYEncodingTypeInt32 | qualifier;
- case 'L': return YYEncodingTypeUInt32 | qualifier;
- case 'q': return YYEncodingTypeInt64 | qualifier;
- case 'Q': return YYEncodingTypeUInt64 | qualifier;
- case 'f': return YYEncodingTypeFloat | qualifier;
- case 'd': return YYEncodingTypeDouble | qualifier;
- case 'D': return YYEncodingTypeLongDouble | qualifier;
- case '#': return YYEncodingTypeClass | qualifier;
- case ':': return YYEncodingTypeSEL | qualifier;
- case '*': return YYEncodingTypeCString | qualifier;
- case '^': return YYEncodingTypePointer | qualifier;
- case '[': return YYEncodingTypeCArray | qualifier;
- case '(': return YYEncodingTypeUnion | qualifier;
- case '{': return YYEncodingTypeStruct | qualifier;
- case '@': {
- if (len == 2 && *(type + 1) == '?')
- return YYEncodingTypeBlock | qualifier;
- else
- return YYEncodingTypeObject | qualifier;
- }
- default: return YYEncodingTypeUnknown | qualifier;
- }
- }
- @implementation YYClassIvarInfo
- - (instancetype)initWithIvar:(Ivar)ivar {
- if (!ivar) return nil;
- self = [super init];
- _ivar = ivar;
- const char *name = ivar_getName(ivar);
- if (name) {
- _name = [NSString stringWithUTF8String:name];
- }
- _offset = ivar_getOffset(ivar);
- const char *typeEncoding = ivar_getTypeEncoding(ivar);
- if (typeEncoding) {
- _typeEncoding = [NSString stringWithUTF8String:typeEncoding];
- _type = YYEncodingGetType(typeEncoding);
- }
- return self;
- }
- @end
- @implementation YYClassMethodInfo
- - (instancetype)initWithMethod:(Method)method {
- if (!method) return nil;
- self = [super init];
- _method = method;
- _sel = method_getName(method);
- _imp = method_getImplementation(method);
- const char *name = sel_getName(_sel);
- if (name) {
- _name = [NSString stringWithUTF8String:name];
- }
- const char *typeEncoding = method_getTypeEncoding(method);
- if (typeEncoding) {
- _typeEncoding = [NSString stringWithUTF8String:typeEncoding];
- }
- char *returnType = method_copyReturnType(method);
- if (returnType) {
- _returnTypeEncoding = [NSString stringWithUTF8String:returnType];
- free(returnType);
- }
- unsigned int argumentCount = method_getNumberOfArguments(method);
- if (argumentCount > 0) {
- NSMutableArray *argumentTypes = [NSMutableArray new];
- for (unsigned int i = 0; i < argumentCount; i++) {
- char *argumentType = method_copyArgumentType(method, i);
- NSString *type = argumentType ? [NSString stringWithUTF8String:argumentType] : nil;
- [argumentTypes addObject:type ? type : @""];
- if (argumentType) free(argumentType);
- }
- _argumentTypeEncodings = argumentTypes;
- }
- return self;
- }
- @end
- @implementation YYClassPropertyInfo
- - (instancetype)initWithProperty:(objc_property_t)property {
- if (!property) return nil;
- self = [super init];
- _property = property;
- const char *name = property_getName(property);
- if (name) {
- _name = [NSString stringWithUTF8String:name];
- }
-
- YYEncodingType type = 0;
- unsigned int attrCount;
- objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount);
- for (unsigned int i = 0; i < attrCount; i++) {
- switch (attrs[i].name[0]) {
- case 'T': { // Type encoding
- if (attrs[i].value) {
- _typeEncoding = [NSString stringWithUTF8String:attrs[i].value];
- type = YYEncodingGetType(attrs[i].value);
-
- if ((type & YYEncodingTypeMask) == YYEncodingTypeObject && _typeEncoding.length) {
- NSScanner *scanner = [NSScanner scannerWithString:_typeEncoding];
- if (![scanner scanString:@"@\"" intoString:NULL]) continue;
-
- NSString *clsName = nil;
- if ([scanner scanUpToCharactersFromSet: [NSCharacterSet characterSetWithCharactersInString:@"\"<"] intoString:&clsName]) {
- if (clsName.length) _cls = objc_getClass(clsName.UTF8String);
- }
-
- NSMutableArray *protocols = nil;
- while ([scanner scanString:@"<" intoString:NULL]) {
- NSString* protocol = nil;
- if ([scanner scanUpToString:@">" intoString: &protocol]) {
- if (protocol.length) {
- if (!protocols) protocols = [NSMutableArray new];
- [protocols addObject:protocol];
- }
- }
- [scanner scanString:@">" intoString:NULL];
- }
- _protocols = protocols;
- }
- }
- } break;
- case 'V': { // Instance variable
- if (attrs[i].value) {
- _ivarName = [NSString stringWithUTF8String:attrs[i].value];
- }
- } break;
- case 'R': {
- type |= YYEncodingTypePropertyReadonly;
- } break;
- case 'C': {
- type |= YYEncodingTypePropertyCopy;
- } break;
- case '&': {
- type |= YYEncodingTypePropertyRetain;
- } break;
- case 'N': {
- type |= YYEncodingTypePropertyNonatomic;
- } break;
- case 'D': {
- type |= YYEncodingTypePropertyDynamic;
- } break;
- case 'W': {
- type |= YYEncodingTypePropertyWeak;
- } break;
- case 'G': {
- type |= YYEncodingTypePropertyCustomGetter;
- if (attrs[i].value) {
- _getter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);
- }
- } break;
- case 'S': {
- type |= YYEncodingTypePropertyCustomSetter;
- if (attrs[i].value) {
- _setter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);
- }
- } // break; commented for code coverage in next line
- default: break;
- }
- }
- if (attrs) {
- free(attrs);
- attrs = NULL;
- }
-
- _type = type;
- if (_name.length) {
- if (!_getter) {
- _getter = NSSelectorFromString(_name);
- }
- if (!_setter) {
- _setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:", [_name substringToIndex:1].uppercaseString, [_name substringFromIndex:1]]);
- }
- }
- return self;
- }
- @end
- @implementation YYClassInfo {
- BOOL _needUpdate;
- }
- - (instancetype)initWithClass:(Class)cls {
- if (!cls) return nil;
- self = [super init];
- _cls = cls;
- _superCls = class_getSuperclass(cls);
- _isMeta = class_isMetaClass(cls);
- if (!_isMeta) {
- _metaCls = objc_getMetaClass(class_getName(cls));
- }
- _name = NSStringFromClass(cls);
- [self _update];
- _superClassInfo = [self.class classInfoWithClass:_superCls];
- return self;
- }
- - (void)_update {
- _ivarInfos = nil;
- _methodInfos = nil;
- _propertyInfos = nil;
-
- Class cls = self.cls;
- unsigned int methodCount = 0;
- Method *methods = class_copyMethodList(cls, &methodCount);
- if (methods) {
- NSMutableDictionary *methodInfos = [NSMutableDictionary new];
- _methodInfos = methodInfos;
- for (unsigned int i = 0; i < methodCount; i++) {
- YYClassMethodInfo *info = [[YYClassMethodInfo alloc] initWithMethod:methods[i]];
- if (info.name) methodInfos[info.name] = info;
- }
- free(methods);
- }
- unsigned int propertyCount = 0;
- objc_property_t *properties = class_copyPropertyList(cls, &propertyCount);
- if (properties) {
- NSMutableDictionary *propertyInfos = [NSMutableDictionary new];
- _propertyInfos = propertyInfos;
- for (unsigned int i = 0; i < propertyCount; i++) {
- YYClassPropertyInfo *info = [[YYClassPropertyInfo alloc] initWithProperty:properties[i]];
- if (info.name) propertyInfos[info.name] = info;
- }
- free(properties);
- }
-
- unsigned int ivarCount = 0;
- Ivar *ivars = class_copyIvarList(cls, &ivarCount);
- if (ivars) {
- NSMutableDictionary *ivarInfos = [NSMutableDictionary new];
- _ivarInfos = ivarInfos;
- for (unsigned int i = 0; i < ivarCount; i++) {
- YYClassIvarInfo *info = [[YYClassIvarInfo alloc] initWithIvar:ivars[i]];
- if (info.name) ivarInfos[info.name] = info;
- }
- free(ivars);
- }
-
- if (!_ivarInfos) _ivarInfos = @{};
- if (!_methodInfos) _methodInfos = @{};
- if (!_propertyInfos) _propertyInfos = @{};
-
- _needUpdate = NO;
- }
- - (void)setNeedUpdate {
- _needUpdate = YES;
- }
- - (BOOL)needUpdate {
- return _needUpdate;
- }
- + (instancetype)classInfoWithClass:(Class)cls {
- if (!cls) return nil;
- static CFMutableDictionaryRef classCache;
- static CFMutableDictionaryRef metaCache;
- static dispatch_once_t onceToken;
- static dispatch_semaphore_t lock;
- dispatch_once(&onceToken, ^{
- classCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
- metaCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
- lock = dispatch_semaphore_create(1);
- });
- dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
- YYClassInfo *info = CFDictionaryGetValue(class_isMetaClass(cls) ? metaCache : classCache, (__bridge const void *)(cls));
- if (info && info->_needUpdate) {
- [info _update];
- }
- dispatch_semaphore_signal(lock);
- if (!info) {
- info = [[YYClassInfo alloc] initWithClass:cls];
- if (info) {
- dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
- CFDictionarySetValue(info.isMeta ? metaCache : classCache, (__bridge const void *)(cls), (__bridge const void *)(info));
- dispatch_semaphore_signal(lock);
- }
- }
- return info;
- }
- + (instancetype)classInfoWithClassName:(NSString *)className {
- Class cls = NSClassFromString(className);
- return [self classInfoWithClass:cls];
- }
- @end
|