zoukankan      html  css  js  c++  java
  • YYModel 源码解读(二)之YYClassInfo.h (2)

     1 /**
     2  Instance variable information.
     3  */
     4 @interface YYClassIvarInfo : NSObject
     5 @property (nonatomic, assign, readonly) Ivar ivar;              ///< ivar opaque struct
     6 @property (nonatomic, strong, readonly) NSString *name;         ///< Ivar's name
     7 @property (nonatomic, assign, readonly) ptrdiff_t offset;       ///< Ivar's offset
     8 @property (nonatomic, strong, readonly) NSString *typeEncoding; ///< Ivar's type encoding
     9 @property (nonatomic, assign, readonly) YYEncodingType type;    ///< Ivar's type
    10 
    11 /**
    12  Creates and returns an ivar info object.
    13  
    14  @param ivar ivar opaque struct
    15  @return A new object, or nil if an error occurs.
    16  */
    17 - (instancetype)initWithIvar:(Ivar)ivar;
    18 @end

    上边代码通过创建Ivar(成员变量)的 抽象类, 返回我们需要的关于Ivar 的信息,

    通过一个初始化方法创建,接下来我们看看该方法的具体实现

     1 - (instancetype)initWithIvar:(Ivar)ivar {
     2     // 初始化判空 如果为空 就返回nil
     3     if (!ivar) return nil;
     4     self = [super init];
     5     _ivar = ivar;
     6     
     7     // 获取成员变量的名称
     8     const char *name = ivar_getName(ivar);
     9     if (name) {
    10         
    11         // 把c的字符串转化成oc的字符串
    12         _name = [NSString stringWithUTF8String:name];
    13     }
    14     
    15     _offset = ivar_getOffset(ivar);
    16     
    17     // 获取类型编码
    18     const char *typeEncoding = ivar_getTypeEncoding(ivar);
    19     if (typeEncoding) {
    20         
    21         // 转为oc的字符穿
    22         _typeEncoding = [NSString stringWithUTF8String:typeEncoding];
    23         
    24         // 转成枚举值
    25         _type = YYEncodingGetType(typeEncoding);
    26    
    27     }
    28     return self;
    29 }
    ivar_getName
    ivar_getTypeEncoding
    ivar_getOffset

    这三个方法都是运行时方法,分别用来获取 名称 , 类型编码 , 偏移量 尤其要之处的是
    ivar_getOffset方法: 官方文档中的描述是这样的
    1 Returns the offset of an instance variable.
    2 
    3 Declaration
    4 ptrdiff_t ivar_getOffset( Ivar ivar)
    5 Discussion
    6 For instance variables of type id or other object types, call object_getIvar and object_setIvar instead of using this offset to access the instance variable data directly.

    ivar_getOffset函数,对于类型id或其它对象类型的实例变量,可以调用object_getIvar和object_setIvar来直接访问成员变量,而不使用偏移量。

    接下来我们看看Method(方法)的 抽象类

     1 /**
     2  Method information.
     3  */
     4 @interface YYClassMethodInfo : NSObject
     5 @property (nonatomic, assign, readonly) Method method;                  ///< method opaque struct
     6 @property (nonatomic, strong, readonly) NSString *name;                 ///< method name
     7 @property (nonatomic, assign, readonly) SEL sel;                        ///< method's selector
     8 @property (nonatomic, assign, readonly) IMP imp;                        ///< method's implementation
     9 @property (nonatomic, strong, readonly) NSString *typeEncoding;         ///< method's parameter and return types
    10 @property (nonatomic, strong, readonly) NSString *returnTypeEncoding;   ///< return value's type
    11 @property (nullable, nonatomic, strong, readonly) NSArray<NSString *> *argumentTypeEncodings; ///< array of arguments' type
    12 
    13 /**
    14  Creates and returns a method info object.
    15  
    16  @param method method opaque struct
    17  @return A new object, or nil if an error occurs.
    18  */
    19 - (instancetype)initWithMethod:(Method)method;
    20 @end

    这这段代码中 比较陌生的是Method 和 IMP

    Method 是一个结构体: 

    struct objc_method
    {
      SEL method_name;
      char * method_types;
      IMP method_imp;
    };

    1.方法名:方法名为此方法的方法签名,相同函数名和参数的方法名是一样的
    2.方法类型: 描述方法的参数类型
    3. 方法真实实现代码块的地址指针,可像C 一样直接调用
     1 - (instancetype)initWithMethod:(Method)method {
     2     if (!method) return nil;
     3     self = [super init];
     4     _method = method;
     5     
     6     // Method获取方法的名称
     7     _sel = method_getName(method);
     8     
     9     // 方法的实现地址
    10     _imp = method_getImplementation(method);
    11     
    12     // SEL 获取方法名
    13     const char *name = sel_getName(_sel);
    14     if (name) {
    15         _name = [NSString stringWithUTF8String:name];
    16     }
    17     
    18     // 获取类型
    19     const char *typeEncoding = method_getTypeEncoding(method);
    20     if (typeEncoding) {
    21         _typeEncoding = [NSString stringWithUTF8String:typeEncoding];
    22     }
    23     
    24     // 获取返回值类型
    25     char *returnType = method_copyReturnType(method);
    26     if (returnType) {
    27         _returnTypeEncoding = [NSString stringWithUTF8String:returnType];
    28         
    29         // 但凡 通过copy retain alloc 系统方法得到的内存,必须使用relea() 或 free() 进行释放
    30         free(returnType);
    31     }
    32     
    33     // 获取参数列表
    34     unsigned int argumentCount = method_getNumberOfArguments(method);
    35     if (argumentCount > 0) {
    36         
    37         NSMutableArray *argumentTypes = [NSMutableArray new];
    38         
    39         for (unsigned int i = 0; i < argumentCount; i++) {
    40             
    41             // 获取参数中的某一个参数
    42             char *argumentType = method_copyArgumentType(method, i);
    43             
    44             NSString *type = argumentType ? [NSString stringWithUTF8String:argumentType] : nil;
    45             [argumentTypes addObject:type ? type : @""];
    46             if (argumentType) free(argumentType);
    47         }
    48         _argumentTypeEncodings = argumentTypes;
    49     }
    50     return self;
    51 }

    上边的代码使用了运行时中 关于Method 的一些方法,再次不做介绍,但值得注意的是

     但凡 通过copy retain alloc 系统方法得到的内存,必须使用relea() 或 free() 进行释放

     1 /**
     2  Property information.
     3  */
     4 @interface YYClassPropertyInfo : NSObject
     5 @property (nonatomic, assign, readonly) objc_property_t property; ///< property's opaque struct
     6 @property (nonatomic, strong, readonly) NSString *name;           ///< property's name
     7 @property (nonatomic, assign, readonly) YYEncodingType type;      ///< property's type
     8 @property (nonatomic, strong, readonly) NSString *typeEncoding;   ///< property's encoding value
     9 @property (nonatomic, strong, readonly) NSString *ivarName;       ///< property's ivar name
    10 @property (nullable, nonatomic, assign, readonly) Class cls;      ///< may be nil
    11 @property (nonatomic, assign, readonly) SEL getter;               ///< getter (nonnull)
    12 @property (nonatomic, assign, readonly) SEL setter;               ///< setter (nonnull)
    13 
    14 /**
    15  Creates and returns a property info object.
    16  
    17  @param property property opaque struct
    18  @return A new object, or nil if an error occurs.
    19  */
    20 - (instancetype)initWithProperty:(objc_property_t)property;

    上边的类是对属性的抽象类,让我们通过下边的代码 了解下属性编码的知识

     1     objc_property_t property = class_getProperty([YYWeiboStatus class], "user");
     2     
     3     unsigned int num;
     4     objc_property_attribute_t *attr = property_copyAttributeList(property, &num);
     5     for (unsigned int i = 0; i < num; i++) {
     6         
     7         objc_property_attribute_t att = attr[i];
     8         fprintf(stdout, "name = %s , value = %s 
    ",att.name , att.value);
     9     }
    10     
    11     const char *chars = property_getAttributes(property);
    12     fprintf(stdout, "%s 
    ",chars);

    打印的输出结果为

    1 name = T , value = @"YYWeiboUser" 
    2 name = & , value =  
    3 name = N , value =  
    4 name = V , value = _user 
    5 T@"YYWeiboUser",&,N,V_user 

    可以看出,比较重要的是属性的编码都是以T开头 标示属性的类型  以V开头 标示属性的变量名

    
    
      1 - (instancetype)initWithProperty:(objc_property_t)property {
      2     if (!property) return nil;
      3     self = [self init];
      4     
      5     _property = property;
      6     
      7     // 1. 获取属性名称
      8     const char *name = property_getName(property);
      9     if (name) {
     10         _name = [NSString stringWithUTF8String:name];
     11     }
     12     
     13     // 2.获取每一个属性的编码字符串
     14     YYEncodingType type = 0;
     15     unsigned int attrCount;
     16     objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount);
     17     
     18     // 3. 编译每一个属性的 objc_property_attribute_t
     19     for (unsigned int i = 0; i < attrCount; i++) {
     20         
     21         // 3.1 根据objc_property_attribute_t 中的name 做一些事
     22         switch (attrs[i].name[0]) {
     23                 
     24                 // T 代码属性的类型编码
     25             case 'T': { // Type encoding
     26                 if (attrs[i].value) {
     27                     _typeEncoding = [NSString stringWithUTF8String:attrs[i].value];
     28                     type = YYEncodingGetType(attrs[i].value);
     29                     
     30                     // 计算属性的实体类型 比如:@"User"
     31                     if ((type & YYEncodingTypeMask) == YYEncodingTypeObject) {
     32                         
     33                         size_t len = strlen(attrs[i].value); // len = 7
     34                         if (len > 3) {
     35                             char name[len - 2]; // 新建一个长度 = len - 2 的name字符数组 长度为5
     36                             name[len - 3] = '';   // 设置最后一个字符为
     37                             memcpy(name, attrs[i].value + 2, len - 3); // copy USer 到name 中,
     38                            
     39                             // 获取name 的真实实体类型
     40                             _cls = objc_getClass(name);
     41                         }
     42                     }
     43                 }
     44             } break;
     45             case 'V': { // Instance variable
     46                 if (attrs[i].value) {
     47                     _ivarName = [NSString stringWithUTF8String:attrs[i].value];
     48                 }
     49             } break;
     50             case 'R': {
     51                 type |= YYEncodingTypePropertyReadonly;
     52             } break;
     53             case 'C': {
     54                 type |= YYEncodingTypePropertyCopy;
     55             } break;
     56             case '&': {
     57                 type |= YYEncodingTypePropertyRetain;
     58             } break;
     59             case 'N': {
     60                 type |= YYEncodingTypePropertyNonatomic;
     61             } break;
     62             case 'D': {
     63                 type |= YYEncodingTypePropertyDynamic;
     64             } break;
     65             case 'W': {
     66                 type |= YYEncodingTypePropertyWeak;
     67             } break;
     68             case 'G': { // getter 方法
     69                 type |= YYEncodingTypePropertyCustomGetter;
     70                 if (attrs[i].value) {
     71                     _getter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);
     72                 }
     73             } break;
     74             case 'S': { // setter 方法
     75                 type |= YYEncodingTypePropertyCustomSetter;
     76                 if (attrs[i].value) {
     77                     _setter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);
     78                 }
     79             } // break; commented for code coverage in next line
     80             default: break;
     81         }
     82     }
     83     if (attrs) {
     84         free(attrs);
     85         attrs = NULL;
     86     }
     87     
     88     _type = type;
     89     
     90     // 获取setter 和 getter 方法
     91     if (_name.length) {
     92         if (!_getter) {
     93             _getter = NSSelectorFromString(_name);
     94         }
     95         if (!_setter) {
     96             _setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:", [_name substringToIndex:1].uppercaseString, [_name substringFromIndex:1]]);
     97         }
     98     }
     99     return self;
    100 }
    
    

    最后让我来看看打印结果

    1  objc_property_t property = class_getProperty([YYWeiboStatus class], "user");
    2     
    3     YYClassPropertyInfo *propertyInfo = [[YYClassPropertyInfo alloc] initWithProperty:property];
    4 
    5     NSLog(@"%@",propertyInfo.typeEncoding);
    1 2016-05-23 16:00:12.134 ModelBenchmark[12626:245778] @"YYWeiboUser"
    1 typedef struct example {
    2     int* aPint;
    3     double  aDouble;
    4     char *aString;
    5     int  anInt;
    6     BOOL isMan;
    7     struct example *next;
    8 } Example;
    1 @property (nonatomic, assign) Example example;
    1 2016-05-23 16:04:59.493 ModelBenchmark[12684:250528] {example=^id*iB^{example}}
    1 objc_property_t property = class_getProperty([YYWeiboStatus class], "statusID");
    2     
    3     YYClassPropertyInfo *propertyInfo = [[YYClassPropertyInfo alloc] initWithProperty:property];
    4 
    5     NSLog(@"%@",propertyInfo.typeEncoding);
    1 2016-05-23 16:06:39.747 ModelBenchmark[12696:252148] Q
    到此 关于 Ivar Method Property  的抽象类已经介绍完毕,在后面的使用中 直接使用这些抽象类来进行编码的

  • 相关阅读:
    Python并行编程(七):线程同步之事件
    Python并行编程(六):线程同步之条件
    Python并行编程(五):线程同步之信号量
    Python并行编程(四):线程同步之RLock
    Python并行编程(三):线程同步之Lock
    UML关系总结——画uml图、流程图、软件结构图、类图、顺序图的方法
    2020,你好!
    字符串和多维数组
    排序算法
    查找技术
  • 原文地址:https://www.cnblogs.com/machao/p/5520367.html
Copyright © 2011-2022 走看看