美文网首页A原理/底层iOS - Runtime
iOS runtime探究(三): 从runtime开始理解OC

iOS runtime探究(三): 从runtime开始理解OC

作者: WWWWDotPNG | 来源:发表于2017-03-27 10:25 被阅读1366次

    你要知道的runtime都在这里

    转载请注明出处 http://www.jianshu.com/p/0623addb6b74

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

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

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

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

    //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属性在底层就是一个结构体描述,那么我们如何获取这个结构体呢?可以通过如下代码获取:

    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中可以找到相关定义:

    typedef struct objc_property *objc_property_t;
    

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

    /** 
     * 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值,输出的结果如下:

    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看一下输出。

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

    /** 
     * 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);
    

    举个简单的栗子:

    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;
    }
    

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

    备注

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

    相关文章

      网友评论

      本文标题:iOS runtime探究(三): 从runtime开始理解OC

      本文链接:https://www.haomeiwen.com/subject/gaqqottx.html