• YYModel 源码解读(二)之YYClassInfo.h (3)


    前边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 }

    源码在这里

  • 相关阅读:
    mongodb入门安装与配置
    mssql export db
    初识django
    git
    水晶报表的使用经验和资料总结
    SQL中CONVERT转化函数的用法▲
    生活
    SQL中的临时表和表变量
    Convert Datetime to String in Sql Server
    转:探讨SQL Server 2005的安全策略
  • 原文地址:https://www.cnblogs.com/machao/p/5558672.html
Copyright © 2020-2023  润新知