前边3篇介绍了YYClassinfo 文件的组成单元,算是功能的分割,按照业务的设计思想来说,方向应该是相反的
由此引申出我们在设计api的思想其实和项目管理是很类似的----- 一些题外话
1.目的
回到代码,首先应该明确写这个类的目的是什么? 按照正常逻辑,我们需要一个类来获取我们所需要的所有和此类相关的信息
包括(类名,父类,成员变量,方法,属性...)
2.技术调研
调研我们所需要的结果是否能够通过技术手段实现
3.目标分隔,也就是任务分解
需要把整体目标分解成小目标,在本代码中则分割成 三个部分
①获取IVar -----> 具体的实现
②获取Method -----> 具体的实现
③获取Property ------> 具体的实现
好了大概的思想就是上边这些了 ,接下来让我们看看暴露出来的属性是怎么样的,也就是我们所需要的信息是什么
1 /** 2 Class information for a class. 3 */ 4 @interface YYClassInfo : NSObject 5 @property (nonatomic, assign, readonly) Class cls; ///< class object 6 @property (nullable, nonatomic, assign, readonly) Class superCls; ///< super class object 7 @property (nullable, nonatomic, assign, readonly) Class metaCls; ///< class's meta class object 8 @property (nonatomic, readonly) BOOL isMeta; ///< whether this class is meta class 9 @property (nonatomic, strong, readonly) NSString *name; ///< class name 10 @property (nullable, nonatomic, strong, readonly) YYClassInfo *superClassInfo; ///< super class's class info 11 @property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassIvarInfo *> *ivarInfos; ///< ivars 12 @property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassMethodInfo *> *methodInfos; ///< methods 13 @property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassPropertyInfo *> *propertyInfos; ///< properties 14 15 /** 16 If the class is changed (for example: you add a method to this class with 17 'class_addMethod()'), you should call this method to refresh the class info cache. 18 19 After called this method, `needUpdate` will returns `YES`, and you should call 20 'classInfoWithClass' or 'classInfoWithClassName' to get the updated class info. 21 */ 22 - (void)setNeedUpdate; 23 24 /** 25 If this method returns `YES`, you should stop using this instance and call 26 `classInfoWithClass` or `classInfoWithClassName` to get the updated class info. 27 28 @return Whether this class info need update. 29 */ 30 - (BOOL)needUpdate; 31 32 /** 33 Get the class info of a specified Class. 34 35 @discussion This method will cache the class info and super-class info 36 at the first access to the Class. This method is thread-safe. 37 38 @param cls A class. 39 @return A class info, or nil if an error occurs. 40 */ 41 + (nullable instancetype)classInfoWithClass:(Class)cls; 42 43 /** 44 Get the class info of a specified Class. 45 46 @discussion This method will cache the class info and super-class info 47 at the first access to the Class. This method is thread-safe. 48 49 @param className A class name. 50 @return A class info, or nil if an error occurs. 51 */ 52 + (nullable instancetype)classInfoWithClassName:(NSString *)className;
这里需要注意的是两个方法
1 /** 2 If the class is changed (for example: you add a method to this class with 3 'class_addMethod()'), you should call this method to refresh the class info cache. 4 5 After called this method, `needUpdate` will returns `YES`, and you should call 6 'classInfoWithClass' or 'classInfoWithClassName' to get the updated class info. 7 */ 8 - (void)setNeedUpdate;
当使用运行时对本类 操作的时候 ,需要调用方法更新本类的信息,这个时候
1 needUpdate
将返回为yes 让后调用
`classInfoWithClass` or `classInfoWithClassName`
具体的作用后边还会详细的解释,接下来看看方法的具体实现
1 + (instancetype)classInfoWithClass:(Class)cls { 2 3 // 判空 4 if (!cls) return nil; 5 /** 6 * 在此使用了coreFoundation 的 CFMutableDictionaryRef 7 * 单例模式 8 */ 9 10 static CFMutableDictionaryRef classCache; 11 static CFMutableDictionaryRef metaCache; 12 static dispatch_once_t onceToken; 13 /** 14 * GCD 信号 15 */ 16 static dispatch_semaphore_t lock; 17 dispatch_once(&onceToken, ^{ 18 classCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 19 metaCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 20 lock = dispatch_semaphore_create(1); 21 }); 22 23 // 加锁 24 dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); 25 26 /** 27 * 首先在缓存中取值,分为两种,metaCache , classCache 28 */ 29 YYClassInfo *info = CFDictionaryGetValue(class_isMetaClass(cls) ? metaCache : classCache, (__bridge const void *)(cls)); 30 /** 31 * 如果存在且需要更新,就更新 32 */ 33 if (info && info->_needUpdate) { 34 [info _update]; 35 } 36 dispatch_semaphore_signal(lock); 37 38 // 不存在,就要创建 39 if (!info) { 40 info = [[YYClassInfo alloc] initWithClass:cls]; 41 if (info) { 42 dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); 43 CFDictionarySetValue(info.isMeta ? metaCache : classCache, (__bridge const void *)(cls), (__bridge const void *)(info)); 44 dispatch_semaphore_signal(lock); 45 } 46 } 47 return info; 48 }
上边的代码用到了几个我们平时可能不太熟悉的东西,
总体来说,但凡是需要反复使用的东西,尽量做缓存操作,这应该算是一种思想吧
dispatch_semaphore_t
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); 如果semaphore计数大于等于1.计数-1,返回,程序继续运行。
如果计数为0,则等待。
这里设置的等待时间是一直等待。dispatch_semaphore_signal(semaphore);计数+1
.在这两句代码中间的执行代码,每次只会允许一个线程进入,这样就有效的保证了在多线程环境下,只能有一个线程进入
这样就可以当做同步锁来使用了 。
CoreFoundation 框架的简单介绍
Core Foundation 是由oc base foundation 概念上衍生出来的一个库,只提供有限的基于C语言的oc的部分对象,
优点是 可以在frameworks and libraries 共享数据,使用coreFoundation 可以osX ios 运行
在平时使用中,我们可能更加关心的是
Toll-Free Bridged
也就是CF 和 NS 的转换问题
Objective-C 的ARC 内存管理 只管理 oc 的对象,对core foundation 是无效的,core foundation 必须通过 CFRelease来释放
对于MRC 可以直接进行转换,下面我们讨论 ARC 下的转换情况
1 id obj = [[NSObject alloc] init]; 2 void *p = obj;
系统会直接报错,错误信息为
1 Implicit conversion of Objective-C pointer type 'id' to C pointer type 'void *' requires a bridged cast
很明显 ,需要一个bridged 来辅助转换
1 id obj = [[NSObject alloc] init]; 2 void *p = (__bridge void *)obj;
ok,成功了 ,其实与这个bridged 相关的还有另外两个
__bridge_retained
__bridge_transfer
__bridge
只是单纯地执行了类型转换,没有进行所有权的转移,也就是说,当string对象被释放的时候,cfString也不能被使用了。
__bridge_retained 可以通过转换目标处(cfString)的 retain 处理,来使所有权转移。即使 string 变量被释放,cfString 还是可以使用具体的对象。只是有一点,由于Core Foundation的对象不属于ARC的管理范畴,所以需要自己release。
__bridge_transfer
所有权被转移的同时,被转换变量将失去对象的所有权。当Core Foundation对象类型向Objective-C对象类型转换的时候,会经常用到 __bridge_transfer 关键字。
CFStringRef cfString = CFStringCreate...(); NSString *string = (__bridge_transfer NSString *)cfString; // CFRelease(cfString); 因为已经用 __bridge_transfer 转移了对象的所有权,所以不需要调用 release
CFTypeRef CFBridgingRetain(id X) { return (__bridge_retained CFTypeRef)X; } id CFBridgingRelease(CFTypeRef X) { return (__bridge_transfer id)X; }
其他两个初始化方法
1 - (instancetype)initWithClass:(Class)cls { 2 if (!cls) return nil; 3 self = [super init]; 4 _cls = cls; 5 _superCls = class_getSuperclass(cls); 6 _isMeta = class_isMetaClass(cls); 7 if (!_isMeta) { 8 _metaCls = objc_getMetaClass(class_getName(cls)); 9 } 10 _name = NSStringFromClass(cls); 11 [self _update]; 12 13 _superClassInfo = [self.class classInfoWithClass:_superCls]; 14 return self; 15 }
1 + (instancetype)classInfoWithClassName:(NSString *)className { 2 Class cls = NSClassFromString(className); 3 return [self classInfoWithClass:cls]; 4 }
与needUpdate 想关
1 - (void)setNeedUpdate { 2 _needUpdate = YES; 3 } 4 5 - (BOOL)needUpdate { 6 return _needUpdate; 7 }
1 - (void)_update { 2 _ivarInfos = nil; 3 _methodInfos = nil; 4 _propertyInfos = nil; 5 6 Class cls = self.cls; 7 unsigned int methodCount = 0; 8 Method *methods = class_copyMethodList(cls, &methodCount); 9 if (methods) { 10 NSMutableDictionary *methodInfos = [NSMutableDictionary new]; 11 _methodInfos = methodInfos; 12 for (unsigned int i = 0; i < methodCount; i++) { 13 YYClassMethodInfo *info = [[YYClassMethodInfo alloc] initWithMethod:methods[i]]; 14 if (info.name) methodInfos[info.name] = info; 15 } 16 free(methods); 17 } 18 unsigned int propertyCount = 0; 19 objc_property_t *properties = class_copyPropertyList(cls, &propertyCount); 20 if (properties) { 21 NSMutableDictionary *propertyInfos = [NSMutableDictionary new]; 22 _propertyInfos = propertyInfos; 23 for (unsigned int i = 0; i < propertyCount; i++) { 24 YYClassPropertyInfo *info = [[YYClassPropertyInfo alloc] initWithProperty:properties[i]]; 25 if (info.name) propertyInfos[info.name] = info; 26 } 27 free(properties); 28 } 29 30 unsigned int ivarCount = 0; 31 Ivar *ivars = class_copyIvarList(cls, &ivarCount); 32 if (ivars) { 33 NSMutableDictionary *ivarInfos = [NSMutableDictionary new]; 34 _ivarInfos = ivarInfos; 35 for (unsigned int i = 0; i < ivarCount; i++) { 36 YYClassIvarInfo *info = [[YYClassIvarInfo alloc] initWithIvar:ivars[i]]; 37 if (info.name) ivarInfos[info.name] = info; 38 } 39 free(ivars); 40 } 41 42 if (!_ivarInfos) _ivarInfos = @{}; 43 if (!_methodInfos) _methodInfos = @{}; 44 if (!_propertyInfos) _propertyInfos = @{}; 45 46 _needUpdate = NO; 47 }