前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >iOS runtime探究(三): 从runtime开始理解OC的属性property你要知道的runtime都在这里

iOS runtime探究(三): 从runtime开始理解OC的属性property你要知道的runtime都在这里

作者头像
WWWWDotPNG
发布2018-04-10 11:44:14
1.1K0
发布2018-04-10 11:44:14
举报
文章被收录于专栏:iOS技术杂谈

你要知道的runtime都在这里

转载请注明出处 https://cloud.tencent.com/developer/user/1605429

本文主要讲解runtime相关知识,从原理到实践,由于包含内容过多分为以下五篇文章详细讲解,可自行选择需要了解的方向:

  • 从runtime开始: 理解面向对象的类到面向过程的结构体
  • 从runtime开始: 深入理解OC消息转发机制
  • 从runtime开始: 理解OC的属性property
  • 从runtime开始: 实践Category添加属性与黑魔法method swizzling
  • 从runtime开始: 深入weak实现机理

本文是系列文章的第三篇文章从runtime开始: 理解OC的属性property,主要从runtime出发讲解属性property相关的底层实现和相关方法,由于之前的博客已经详细讲解了property的底层实现,所以本文不再赘述,如有需要可以查看相关文章:iOS @property探究(一): 基础详解该文主要讲解property的基础以及修饰符详解,iOS @property探究(二): 深入理解该文主要深入代码理解property的底层实现,由于与本文的内容由很大的重复,因此本文不再赘述上述相关内容。

本文将会讲解一些runtime操作属性的相关方法。

首先回顾一下相关代码以及与property底层实现相关的两个结构体:

代码语言:javascript
复制
//OC自定义类的定义
@interface Person : NSObject

@property (nonatomic, copy) NSString* cjmName;
@property (nonatomic, assign) NSUInteger cjmAge;

@end

@implementation Person

@synthesize cjmName = _cjmName;
@synthesize cjmAge = _cjmAge;

@end


//clang转写为.cpp的相关代码
struct _prop_t {
        const char *name;
        const char *attributes;
};

static struct /*_prop_list_t*/ {
        unsigned int entsize;  // sizeof(struct _prop_t)
        unsigned int count_of_properties;
        struct _prop_t prop_list[2];
} _OBJC_$_PROP_LIST_Person __attribute__ ((used, section ("__DATA,__objc_const"))) = {
        sizeof(_prop_t),
        2,
        {{"cjmName","T@\"NSString\",C,N,V_cjmName"},
        {"cjmAge","TQ,N,V_cjmAge"}}
};

通过上述代码其实我们可以看出,一个@property属性在底层就是一个结构体描述,那么我们如何获取这个结构体呢?可以通过如下代码获取:

代码语言:javascript
复制
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person* p = [[Person alloc] init];
        p.cjmName = @"Jiaming Chen";
        
        unsigned int propertyCount = 0;
        objc_property_t *propertyList = class_copyPropertyList([p class], &propertyCount);
        for (int i = 0; i < propertyCount; i++) {
            const char* name = property_getName(propertyList[i]);
            const char* attributes = property_getAttributes(propertyList[i]);
            NSLog(@"%s %s", name, attributes);
        }
    }
    return 0;
}

首先看一下objc_property_t是什么,在objc/runtime.h中可以找到相关定义:

代码语言:javascript
复制
typedef struct objc_property *objc_property_t;

它是一个指向结构体struct objc_property的指针,这里的结构体struct objc_property其实就是前文中.cpp文件中的struct _prop_t结构体,通过class_copyPropertyList方法就可以获取到相关类的所有属性,具体函数声明如下:

代码语言:javascript
复制
/** 
 * Describes the properties declared by a class.
 * 
 * @param cls The class you want to inspect.
 * @param outCount On return, contains the length of the returned array. 
 *  If \e outCount is \c NULL, the length is not returned.        
 * 
 * @return An array of pointers of type \c objc_property_t describing the properties 
 *  declared by the class. Any properties declared by superclasses are not included. 
 *  The array contains \c *outCount pointers followed by a \c NULL terminator. You must free the array with \c free().
 * 
 *  If \e cls declares no properties, or \e cls is \c Nil, returns \c NULL and \c *outCount is \c 0.
 */
OBJC_EXPORT objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);

通过注释可以看出,第一个参数是相关类的类对象(如有疑问可以查阅本系列文章的前两篇文章),第二个参数是一个指向unsigned int的指针,用于指明property的数量,通过该方法就能够获取到所有的属性,接下来可以通过property_getNameproperty_getAttributes方法获取该属性描述的nameattributes值,输出的结果如下:

代码语言:javascript
复制
2017-03-27 09:59:20.914487 OCTest[2467:460742] cjmName T@"NSString",C,N,V_cjmName
2017-03-27 09:59:20.915321 OCTest[2467:460742] cjmAge TQ,N,V_cjmAge

name很好理解,后面的attributes通过对比不难发现其规律,感兴趣的读者也可以多设置几个不同类型、不同修饰符的property看一下输出。

除此之外哈有一下几个方法用于根据属性名获取一个属性描述结构体、添加属性、替换属性等方法。

代码语言:javascript
复制
/** 
 * Returns a property with a given name of a given class.
 * 
 * @param cls The class you want to inspect.
 * @param name The name of the property you want to inspect.
 * 
 * @return A pointer of type \c objc_property_t describing the property, or
 *  \c NULL if the class does not declare a property with that name, 
 *  or \c NULL if \e cls is \c Nil.
 */
OBJC_EXPORT objc_property_t class_getProperty(Class cls, const char *name)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
    
/** 
 * Adds a property to a class.
 * 
 * @param cls The class to modify.
 * @param name The name of the property.
 * @param attributes An array of property attributes.
 * @param attributeCount The number of attributes in \e attributes.
 * 
 * @return \c YES if the property was added successfully, otherwise \c NO
 *  (for example, the class already has that property).
 */
OBJC_EXPORT BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount)
    OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0);

/** 
 * Replace a property of a class. 
 * 
 * @param cls The class to modify.
 * @param name The name of the property.
 * @param attributes An array of property attributes.
 * @param attributeCount The number of attributes in \e attributes. 
 */
OBJC_EXPORT void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount)
    OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0);

举个简单的栗子:

代码语言:javascript
复制
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person* p = [[Person alloc] init];
        p.cjmAge = 20;
        p.cjmName = @"Jiaming Chen";
        
        unsigned int propertyCount = 0;
        objc_property_t *propertyList = class_copyPropertyList([p class], &propertyCount);
        for (int i = 0; i < propertyCount; i++) {
            const char* name = property_getName(propertyList[i]);
            const char* attributes = property_getAttributes(propertyList[i]);
            NSLog(@"%s %s", name, attributes);
        }
        objc_property_attribute_t attributes = {
            "T@\"NSString\",C,N,V_studentIdentifier",
            "",
        };
        class_addProperty([p class], "studentIdentifier", &attributes, 1);
        objc_property_t property = class_getProperty([p class], "studentIdentifier");
        NSLog(@"%s %s", property_getName(property), property_getAttributes(property));
    }
    return 0;
}

通过上述方法就能添加一个属性,由于本人水平有限实际开发中没有用过上述方法,具体实际例子也举不出来所以不再过多赘述。

备注

由于作者水平有限,难免出现纰漏,如有问题还请不吝赐教。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017.03.27 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 你要知道的runtime都在这里
    • 备注
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档