iOS之runtime詳解api(三)

第一篇咱們講了關於ClassCategoryapi,第二篇講了關於Methodapi,這一篇來說關於IvarPropertyapi

4.objc_ivar or Ivar

首先,咱們仍是先找到能打印出Ivar信息的函數:bash

const char * _Nullable
ivar_getName(Ivar _Nonnull v)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
複製代碼

這個是經過傳入對應的Ivar,得到Ivar的名字。 咱們寫到一個方法裏面,以便於調用: -(void)logIvarName:(Ivar)ivar { if (ivar) { const char* name = ivar_getName(ivar); NSLog(@"name = %s",name); } else { NSLog(@"ivar爲null"); } } 那麼知道了如何得到名字,那麼怎麼得到Ivar呢?函數

Ivar _Nullable
class_getInstanceVariable(Class _Nullable cls, const char * _Nonnull name)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);

Ivar _Nullable
class_getClassVariable(Class _Nullable cls, const char * _Nonnull name)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
複製代碼

class_getInstanceVariable是在cls類裏,名字爲name的實例變量。 class_getClassVariable是在cls類裏,名字爲name的類變量,因爲在OC語法裏面,並不存在類變量這個概念,因此,這個方法並無什麼用,那咱們就驗證class_getInstanceVariable這個方法。 咱們新建一個Cat類,添加一個成員變量int _age和一個屬性@property(nonatomic,copy)NSString* name,衆所周知,屬性會自動生成一個前面帶_的成員變量(name生成_name)。佈局

-(void)getIvar {
    Ivar ivar = class_getInstanceVariable(objc_getClass("Cat"), "_name");
    Ivar ivar1 = class_getInstanceVariable(objc_getClass("Cat"), "_age");
    [self logIvarName:ivar];
    [self logIvarName:ivar1];
}
複製代碼

運行結果:測試

2019-02-26 11:42:38.646792+0800 Runtime-Demo[59730:4976606] name = _name
2019-02-26 11:42:38.646845+0800 Runtime-Demo[59730:4976606] name = _age
複製代碼

打印出來了,也確實是成員變量。 那麼如何得到一個類的全部成員變量呢?就用下面這個方法:ui

Ivar _Nonnull * _Nullable
class_copyIvarList(Class _Nullable cls, unsigned int * _Nullable outCount)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
複製代碼

爲了增長可靠性,咱們在Cat.m文件裏面加一個成員變量BOOL _sex@property(nonatomic, strong)Person* master,下面咱們把Car類裏面全部的成員變量打印下:atom

-(void)copyIvarList {
    unsigned int count;
    Ivar* ivars =class_copyIvarList(objc_getClass("Cat"), &count);
    for (unsigned int i = 0; i < count; i++) {
        Ivar ivar = ivars[i];
        [self logIvarName:ivar];
    }
    free(ivars);
}
複製代碼

運行結果:spa

2019-02-26 11:50:51.090761+0800 Runtime-Demo[59875:4979802] name = _age
2019-02-26 11:50:51.090799+0800 Runtime-Demo[59875:4979802] name = _sex
2019-02-26 11:50:51.090809+0800 Runtime-Demo[59875:4979802] name = _name
2019-02-26 11:50:51.090817+0800 Runtime-Demo[59875:4979802] name = _master
複製代碼

若是你要得到成員變量的類型,就能夠用下面這個方法:指針

const char * _Nullable
ivar_getTypeEncoding(Ivar _Nonnull v)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
複製代碼

咱們試着得到下_name的類型:code

-(void)getTypeEncoding {
    Ivar ivar = class_getInstanceVariable(objc_getClass("Cat"), "_name");
    const char* type = ivar_getTypeEncoding(ivar);
    NSLog(@"type = %s",type);
}
複製代碼

運行結果:

type = @"NSString"
複製代碼

name確實是NSString類型的。 下面咱們看的三個方法是給ivar賦值或者取值。

id _Nullable
object_getIvar(id _Nullable obj, Ivar _Nonnull ivar)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

void
object_setIvar(id _Nullable obj, Ivar _Nonnull ivar, id _Nullable value)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

void
object_setIvarWithStrongDefault(id _Nullable obj, Ivar _Nonnull ivar,
                                id _Nullable value)
OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0, 2.0);
複製代碼

object_getIvar這個方法是給ivar取值的函數。咱們測試下:

-(void)getIvarValue {
    Cat* cat = [Cat new];
    Ivar ivar = class_getInstanceVariable(objc_getClass("Cat"), "_name");
    NSString* name = object_getIvar(cat, ivar);
    NSLog(@"賦值前:%@",name);
    cat.name = @"jack";
    NSString* name2 = object_getIvar(cat, ivar);
    NSLog(@"賦值後:%@",name2);
}
複製代碼

運行結果:

2019-02-26 15:44:11.758498+0800 Runtime-Demo[63973:5079569] 賦值前:(null)
2019-02-26 15:44:11.758541+0800 Runtime-Demo[63973:5079569] 賦值後:jack
複製代碼

後面我就要仔細說說object_setIvarobject_setIvarWithStrongDefault,這兩個函數都和內存管理有關係。先說下它們的共同點,若是內存管理屬於已知的內存管理方式(成員變量或屬性屬於ARCstrong或者weak),它們都沒有區別。不一樣點就是若是是屬於未知的內存管理方式,object_setIvar會把該實例變量被分配爲unsafe_unretain,而object_setIvarWithStrongDefault會把該實例變量被分配爲strong。 首先咱們要清楚3個概念,strong,weakunsafe_unretainstrong是強引用指向並擁有那個對象,根據retainCount是否爲0來肯定是否釋放內存 weak是弱引用指向但並不擁有那個對象。釋放空間時會自動將指針設置成nilunsafe_unretainweak相似,只是釋放空間時不會將指針設置成nil,因此會有野指針的危害。 因此,在ARC下,這兩個方法的做用幾乎如出一轍。 新增2個屬性,@property(nonatomic, copy)NSString* style@property(nonatomic, copy)NSString *breed

-(void)setIvar {
    Cat* cat = [Cat new];
    Ivar ivar = class_getInstanceVariable(objc_getClass("Cat"), "_breed");
    Ivar ivar2 = class_getInstanceVariable(objc_getClass("Cat"), "_style");
    object_setIvar(cat, ivar,@"英短");
    object_setIvar(cat, ivar2,@"活潑");
    NSLog(@"breed = %@",cat.breed);
    NSLog(@"style = %@",cat.style);
}
複製代碼

運行結果:

2019-02-26 17:53:10.013361+0800 Runtime-Demo[66371:5132652] breed = 英短
2019-02-26 17:53:10.013430+0800 Runtime-Demo[66371:5132652] style = 活潑
複製代碼

賦值功能徹底好用。 下面這個方法是得到實例變量的偏移量,也就是內存的偏移位置,咱們就能夠看到變量的內存地址。

ptrdiff_t
ivar_getOffset(Ivar _Nonnull v)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
複製代碼

咱們測試下Cat類,先看下Cat類的屬性和變量分佈:

Cat.h
@interface Cat : NSObject
{
    @public
    int _age;

}
@property(nonatomic, copy)NSString* name;

@property(nonatomic, copy)NSString *breed;

@property(nonatomic, copy)NSString* style;

@end

Cat.m
@interface Cat()
{
    BOOL _sex;
}
@property(nonatomic, strong)Person* master;
@end
@implementation Cat

@end

複製代碼

咱們看到Cat類裏面有4個屬性,2個成員變量,如今咱們經過獲取變量列表,逐個打印每一個變量的ptrdiff_t

-(void)getOffset {
    unsigned int count;
    Ivar* ivars =class_copyIvarList(objc_getClass("Cat"), &count);
    for (unsigned int i = 0; i < count; i++) {
        Ivar ivar = ivars[i];
        ptrdiff_t offset = ivar_getOffset(ivar);
        NSLog(@"%s = %td",ivar_getName(ivar),offset);
    }
    free(ivars);
    NSLog(@"Cat總字節 = %lu",class_getInstanceSize(objc_getClass("Cat")));
}
複製代碼

運行結果:

2019-02-26 20:09:16.296160+0800 Runtime-Demo[17275:490666] _age = 8
2019-02-26 20:09:16.296274+0800 Runtime-Demo[17275:490666] _sex = 12
2019-02-26 20:09:16.296364+0800 Runtime-Demo[17275:490666] _name = 16
2019-02-26 20:09:16.296452+0800 Runtime-Demo[17275:490666] _breed = 24
2019-02-26 20:09:16.296525+0800 Runtime-Demo[17275:490666] _style = 32
2019-02-26 20:09:16.296666+0800 Runtime-Demo[17275:490666] _master = 40
2019-02-26 20:09:16.296765+0800 Runtime-Demo[17275:490666] Cat總字節 = 48
複製代碼

看下地址和大小,Cat總共48字節,_age從第8字節開始,佔4個字節,而後第12字節開始是_sex,佔4個字節,到第16位是_name,佔8個字節,到24字節是_breed,佔8個字節,到32字節是_style,佔8個字節,到40字節是_master,佔8個字節。它們所佔內存是由自己類型內存對齊共同決定的。

下面這個函數是爲動態類增長變量的,什麼是動態類呢?咱們在第一篇的時候講了,動態建立類能夠用objc_allocateClassPair函數去建立,而class_addIvar函數就必需要在objc_allocateClassPairobjc_registerClassPair前去新增變量。

BOOL
class_addIvar(Class _Nullable cls, const char * _Nonnull name, size_t size,
              uint8_t alignment, const char * _Nullable types)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
複製代碼

咱們來看看參數,cls是你要加實例變量的類,size是所佔內存的字節數,types是實例變量的類型,alignment指的是對齊,官方文檔有個公式log2(sizeof(pointer_type))。下面咱們測試下:

-(void)addIvar {
    Class class = objc_allocateClassPair(objc_getClass("NSObject"), "Dog", 0);
    float alignment = log2f(sizeof(int));
    class_addIvar(class, "age", sizeof(int), alignment, "int");
    objc_registerClassPair(class);
    Ivar ivar = class_getInstanceVariable(class, "age");
    NSLog(@"name = %s",ivar_getName(ivar));
    NSLog(@"size = %zu",class_getInstanceSize(objc_getClass("Dog")));
}
複製代碼

運行結果:

2019-02-26 20:44:46.198155+0800 Runtime-Demo[19229:519808] name = age
2019-02-26 20:44:46.198295+0800 Runtime-Demo[19229:519808] size = 16
複製代碼

能打印出來新建類的實例變量。

下面四個方法和變量佈局有關係,這是我感受最難理解的方法。IvarLayout這個概念在runtime.h裏面並無進行說明。

const uint8_t * _Nullable
class_getIvarLayout(Class _Nullable cls)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

const uint8_t * _Nullable
class_getWeakIvarLayout(Class _Nullable cls)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

void
class_setIvarLayout(Class _Nullable cls, const uint8_t * _Nullable layout)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

void
class_setWeakIvarLayout(Class _Nullable cls, const uint8_t * _Nullable layout)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
複製代碼

若是想深刻研究layout的含義能夠看這一篇《runtime之ivar內存佈局篇》。這裏我就不一一贅述了。

4.objc_property or objc_property_t

屬性應該是咱們最熟悉的了,至關於給實例變量加了修飾符,自動生成setget方法,用起來很方便。 runtime裏面關於屬性的結構體是objc_property或者objc_property_t,這個咱們並不知道里面的結構,可是官方告訴咱們另一個:

typedef struct {
    const char * _Nonnull name;           /**< The name of the attribute */
    const char * _Nonnull value;          /**< The value of the attribute (usually empty) */
} objc_property_attribute_t;
複製代碼

咱們能夠經過objc_property_attribute_t來間接得到關於屬性的一些信息。 而這個方法property_copyAttributeList方法就是經過傳入objc_property_t來得到objc_property_attribute_t

objc_property_attribute_t * _Nullable
property_copyAttributeList(objc_property_t _Nonnull property,
                           unsigned int * _Nullable outCount)
OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0, 2.0);
複製代碼

咱們寫個方法來封裝下這個方法:

-(void)logProperty:(objc_property_t)property {
    NSLog(@"-------------------");
    unsigned int count;
    objc_property_attribute_t* attributeList = property_copyAttributeList(property, &count);
    for (unsigned int i = 0; i < count; i++) {
       objc_property_attribute_t attribute = attributeList[i];
        NSLog(@"name = %s",attribute.name);
        NSLog(@"value = %s",attribute.value);
    }
}
複製代碼

後面咱們就用這個方法來打印屬性相關的信息。那怎麼得到objc_property_t呢?

objc_property_t _Nullable
class_getProperty(Class _Nullable cls, const char * _Nonnull name)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
複製代碼

咱們仍是以Cat類爲例,咱們從上面可知有4個屬性@property(nonatomic, copy)NSString* name@property(nonatomic, copy)NSString *breed@property(nonatomic, copy)NSString* style@property(nonatomic, strong)Person* master。 下面咱們分別獲取name這個屬性。

-(void)getProperty {
    objc_property_t property = class_getProperty(objc_getClass("Cat"), "name");
    [self logProperty:property];
}
複製代碼

打印結果:

2019-02-27 09:37:17.172874+0800 Runtime-Demo[72525:5355290] name = T
2019-02-27 09:37:17.172916+0800 Runtime-Demo[72525:5355290] value = @"NSString"
2019-02-27 09:37:17.172929+0800 Runtime-Demo[72525:5355290] name = C
2019-02-27 09:37:17.172950+0800 Runtime-Demo[72525:5355290] value =
2019-02-27 09:37:17.172965+0800 Runtime-Demo[72525:5355290] name = N
2019-02-27 09:37:17.172975+0800 Runtime-Demo[72525:5355290] value =
2019-02-27 09:37:17.172985+0800 Runtime-Demo[72525:5355290] name = V
2019-02-27 09:37:17.172995+0800 Runtime-Demo[72525:5355290] value = _name
複製代碼

咱們能夠看到有value是的nameTV,T表明type,屬性的類型,V表明ivar,表明屬性的ivar的是_name。其餘沒有值的表明,那些修飾符,C表明copyN表明nonatomic。由此咱們能夠總結出來:

name value 含義
T 屬性的類型
V 屬性所生成的實例變量的名稱
C copy
N nonatomic
W weak
& 對象類型處於默認狀態是用&,比方strong和readwrite
R readonly

注:若是沒有N,就說明是atomic

一樣也能夠得到一個類的屬性列表。爲了打印方便,咱們此次只打印屬性的名字,就要用到property_getName這個方法:

const char * _Nonnull
property_getName(objc_property_t _Nonnull property)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
複製代碼

下面咱們打印下列表的名字:

objc_property_t _Nonnull * _Nullable
class_copyPropertyList(Class _Nullable cls, unsigned int * _Nullable outCount)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
複製代碼

仍是以Cat爲例:

-(void)copyPropertyList {
    unsigned int count;
    objc_property_t* propertyList = class_copyPropertyList(objc_getClass("Cat"), &count);
    for (unsigned int i = 0; i < count; i++) {
        objc_property_t property = propertyList[i];
        NSLog(@"name = %s",property_getName(property));
    }
    free(propertyList);
}
複製代碼

運行結果:

2019-02-27 10:30:33.006299+0800 Runtime-Demo[73443:5379227] name = master
2019-02-27 10:30:33.006338+0800 Runtime-Demo[73443:5379227] name = name
2019-02-27 10:30:33.006348+0800 Runtime-Demo[73443:5379227] name = breed
2019-02-27 10:30:33.006357+0800 Runtime-Demo[73443:5379227] name = style
複製代碼

把屬性名字都打印出來了,這裏要和ivar區分一下,若是經過已知屬性去找ivar,那麼找到的是帶有下劃線的。 以前咱們能夠打印出一個property的全部屬性,系統還提供了2個方法:

const char * _Nullable
property_getAttributes(objc_property_t _Nonnull property)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

char * _Nullable
property_copyAttributeValue(objc_property_t _Nonnull property,
                            const char * _Nonnull attributeName)
OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0, 2.0);
複製代碼

咱們先測試property_getAttributes這個函數

-(void)getAttributes {
    objc_property_t property = class_getProperty(objc_getClass("Cat"), "name");
    const char* attributes = property_getAttributes(property);
    NSLog(@"attributes = %s",attributes);
}
複製代碼

運行結果:

attributes = T@"NSString",C,N,V_name
複製代碼

打印的結果和以前是同樣的,此次是以字符串的形式打印。 再看下property_copyAttributeValue這個方法,這是經過attributeName得到單獨的value。

-(void)copyAttributeValue {
    objc_property_t property = class_getProperty(objc_getClass("Cat"), "name");
    //V咱們已知是屬性所表明的ivar的名字,看打印是不是ivar
    char* value = property_copyAttributeValue(property,"V");
    NSLog(@"value = %s",value);
}
複製代碼

運行結果:

value = _name
複製代碼

從以前打印結果,這個打印結果是正確的。 下面這兩個方法是動態添加或者替換屬性

BOOL
class_addProperty(Class _Nullable cls, const char * _Nonnull name,
                  const objc_property_attribute_t * _Nullable attributes,
                  unsigned int attributeCount)
OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0, 2.0);

void
class_replaceProperty(Class _Nullable cls, const char * _Nonnull name,
                      const objc_property_attribute_t * _Nullable attributes,
                      unsigned int attributeCount)
OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0, 2.0);
複製代碼

咱們仍是以Cat爲例,爲他增長Property,目標:增長一個@property(nonatomic, copy,readonly)NSString* mood形式的屬性。 傳參須要傳objc_property_attribute_t的列表,分析一下,TV是必有的,TvalueNSStringVvalue_mood,而後nonatomic表明有Ncopy表明有Creadonly表明有R,因此咱們能夠獲知attributeT,V,C,N,R。好了,咱們寫代碼吧!

-(void)addProperty {
    unsigned int count = 5;
    objc_property_attribute_t attributeList[count];
    objc_property_attribute_t attribute1 ;
    attribute1.name = "T";
    attribute1.value = "NSString";
    objc_property_attribute_t attribute2 ;
    attribute2.name = "V";
    attribute2.value = "_mood";
    objc_property_attribute_t attribute3 ;
    attribute3.name = "N";
    attribute3.value = "";
    objc_property_attribute_t attribute4 ;
    attribute4.name = "C";
    attribute4.value = "";
    objc_property_attribute_t attribute5 ;
    attribute5.name = "R";
    attribute5.value = "";
    attributeList[0] = attribute1;
    attributeList[1] = attribute2;
    attributeList[2] = attribute3;
    attributeList[3] = attribute4;
    attributeList[4] = attribute5;
    
    BOOL isSuccess = class_addProperty(objc_getClass("Cat"), "mood", (const objc_property_attribute_t *)&attributeList, count);
    NSLog(@"新增%@",isSuccess?@"成功":@"失敗");
    
    [self copyPropertyList];
    
    objc_property_t property = class_getProperty(objc_getClass("Cat"), "mood");
    const char* attributes = property_getAttributes(property);
    NSLog(@"attributes = %s",attributes);
}
複製代碼

運行結果:

2019-02-27 11:52:49.325561+0800 Runtime-Demo[74832:5417422] 新增成功
2019-02-27 11:52:49.325614+0800 Runtime-Demo[74832:5417422] name = mood
2019-02-27 11:52:49.325632+0800 Runtime-Demo[74832:5417422] name = master
2019-02-27 11:52:49.325650+0800 Runtime-Demo[74832:5417422] name = name
2019-02-27 11:52:49.325662+0800 Runtime-Demo[74832:5417422] name = breed
2019-02-27 11:52:49.325674+0800 Runtime-Demo[74832:5417422] name = style
2019-02-27 11:52:49.325709+0800 Runtime-Demo[74832:5417422] attributes = TNSString,V_mood,N,C,R
複製代碼

新增成功,而且打印的屬性列表也有mood。打印出來的attributes也是沒問題的。 再看看class_replaceProperty我打算把name這個屬性的屬性名改爲catName。 一樣咱們仍是先分析下objc_property_attribute_t的列表,name的屬性是@property(nonatomic, copy)NSString* name,只改變名字的話,T,C,N都不變,變得是VVvalue變成_catName。因此代碼就是:

-(void)replaceProperty {
    unsigned int count = 4;
    objc_property_attribute_t attributeList[count];
    objc_property_attribute_t attribute1 ;
    attribute1.name = "T";
    attribute1.value = "NSString";
    objc_property_attribute_t attribute2 ;
    attribute2.name = "V";
    attribute2.value = "_mood";
    objc_property_attribute_t attribute3 ;
    attribute3.name = "N";
    attribute3.value = "";
    objc_property_attribute_t attribute4 ;
    attribute4.name = "C";
    attribute4.value = "";
    attributeList[0] = attribute1;
    attributeList[1] = attribute2;
    attributeList[2] = attribute3;
    attributeList[3] = attribute4;

    class_replaceProperty(objc_getClass("Cat"), "name", (const objc_property_attribute_t*)&attributeList, count);
    [self copyPropertyList];

    objc_property_t property = class_getProperty(objc_getClass("Cat"), "name");
    const char* attributes = property_getAttributes(property);
    NSLog(@"attributes = %s",attributes);

}
複製代碼

運行結果:

2019-02-27 11:58:46.341930+0800 Runtime-Demo[74939:5421075] name = master
2019-02-27 11:58:46.341970+0800 Runtime-Demo[74939:5421075] name = name
2019-02-27 11:58:46.341980+0800 Runtime-Demo[74939:5421075] name = breed
2019-02-27 11:58:46.341988+0800 Runtime-Demo[74939:5421075] name = style
2019-02-27 11:58:46.342016+0800 Runtime-Demo[74939:5421075] attributes = TNSString,V_mood,N,C
複製代碼

打印結果徹底出乎個人意料,打印出來的屬性徹底沒有catName,可是打印attributes倒是改變的attributes。爲何呢?咱們要從源碼看起來了:

struct property_t {
    const char *name;
    const char *attributes;
};
複製代碼

property_t的結構體分爲nameattributes

BOOL 
class_addProperty(Class cls, const char *name, 
                  const objc_property_attribute_t *attrs, unsigned int n)
{
    return _class_addProperty(cls, name, attrs, n, NO);
}

void 
class_replaceProperty(Class cls, const char *name, 
                      const objc_property_attribute_t *attrs, unsigned int n)
{
    _class_addProperty(cls, name, attrs, n, YES);
}
複製代碼

class_addPropertyclass_replaceProperty的底層都調用了_class_addProperty方法,只是裏面的布爾值傳的不同。咱們再看下_class_addProperty這個方法,

static bool 
_class_addProperty(Class cls, const char *name, 
                   const objc_property_attribute_t *attrs, unsigned int count, 
                   bool replace)
{
    if (!cls) return NO;
    if (!name) return NO;

    property_t *prop = class_getProperty(cls, name);
    if (prop  &&  !replace) {
        // already exists, refuse to replace
        return NO;
    } 
    else if (prop) {
        // replace existing
        rwlock_writer_t lock(runtimeLock);
        try_free(prop->attributes);
        prop->attributes = copyPropertyAttributeString(attrs, count);
        return YES;
    }
    else {
        rwlock_writer_t lock(runtimeLock);
        
        assert(cls->isRealized());
        
        property_list_t *proplist = (property_list_t *)
            malloc(sizeof(*proplist));
        proplist->count = 1;
        proplist->entsizeAndFlags = sizeof(proplist->first);
        proplist->first.name = strdupIfMutable(name);
        proplist->first.attributes = copyPropertyAttributeString(attrs, count);
        
        cls->data()->properties.attachLists(&proplist, 1);
        
        return YES;
    }
}
複製代碼

裏面這一句property_t *prop = class_getProperty(cls, name);是取出要替換的屬性,接着後面就是一系列判斷,由於prop存在,而且replaceYES,因此會走到下面這一段:

else if (prop) {
        // replace existing
        rwlock_writer_t lock(runtimeLock);
        try_free(prop->attributes);
        prop->attributes = copyPropertyAttributeString(attrs, count);
        return YES;
    }
複製代碼

從這一段咱們能夠看到這一部分只改變了 prop->attributes。也沒有改變 prop->name。因此,咱們打印屬性的name天然沒有改變。那麼,class_replaceProperty的用途最好是修改類型或者修飾符。`

相關文章
相關標籤/搜索