内容简介:第一篇我们讲了关于首先,我们还是先找到能打印出这个是通过传入对应的
第一篇我们讲了关于 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
的用途最好是修改类型或者修饰符。`
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Flutter 完整开发实战详解(十六、详解自定义布局实战)
- 数据结构 1 线性表详解 链表、 栈 、 队列 结合JAVA 详解
- 详解Openstack环境准备
- Java泛型详解
- iOS RunLoop 详解
- Raft协议详解
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Effective Ruby:改善Ruby程序的48条建议
Peter J. Jones / 杨政权、秦五一、孟樊超 / 机械工业出版社 / 2016-1 / 49
如果你是经验丰富的Rub程序员,本书能帮助你发挥Ruby的全部力量来编写更稳健、高效、可维护和易执行的代码。Peter J.Jones凭借其近十年的Ruby开发经验,总结出48条Ruby的最佳实践、专家建议和捷径,并辅以可执行的代码实例。 Jones在Ruby开发的每个主要领域都给出了实用的建议,从模块、内存到元编程。他对鲜为人知的Ruby方言、怪癖、误区和强力影响代码行为与性能的复杂性的揭......一起来看看 《Effective Ruby:改善Ruby程序的48条建议》 这本书的介绍吧!