iOS之runtime詳解api(三)
第一篇我們講了關於Class
和Category
的api
,第二篇講了關於Method
的api
,這一篇來講關於Ivar
和Property
。
4.objc_ivar or Ivar
首先,我們還是先找到能打印出Ivar
資訊的函式:
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 複製程式碼
打印出來了,也確實是成員變數。 那麼如何獲得一個類的所有成員變數呢?就用下面這個方法:
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
類裡面所有的成員變數列印下:
-(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); } 複製程式碼
執行結果:
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
的型別:
-(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_setIvar
和object_setIvarWithStrongDefault
,這兩個函式都和記憶體管理有關係。先說下它們的共同點,如果記憶體管理屬於已知的記憶體管理方式(成員變數或屬性屬於ARC
,strong
或者weak
),它們都沒有區別。不同點就是如果是屬於未知的記憶體管理方式,object_setIvar
會把該例項變數被分配為unsafe_unretain
,而object_setIvarWithStrongDefault
會把該例項變數被分配為strong
。
首先我們要清楚3個概念,strong
,weak
和unsafe_unretain
。strong
是強引用指向並擁有那個物件,根據retainCount
是否為0來確定是否釋放記憶體weak
是弱引用指向但並不擁有那個物件。釋放空間時會自動將指標設定成nil
。unsafe_unretain
和weak
類似,只是釋放空間時不會將指標設定成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_allocateClassPair
後objc_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
屬性應該是我們最熟悉的了,相當於給例項變數加了修飾符,自動生成set
和get
方法,用起來很方便。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
是的name
為T
和V
,T代表type
,屬性的型別,V
代表ivar
,代表屬性的ivar
的是_name
。其他沒有值的代表,那些修飾符,C
代表copy
,N
代表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
的列表,分析一下,T
和V
是必有的,T
的value
是NSString
,V
的value
是_mood
,然後nonatomic
代表有N
,copy
代表有C
,readonly
代表有R,所以我們可以獲知attribute
有T
,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
都不變,變得是V
,V
的value
變成_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
的結構體分為name
和attributes
。
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_addProperty
和class_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
存在,並且replace
為YES
,所以會走到下面這一段:
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
的用途最好是修改型別或者修飾符。`