内容简介:第一篇我们讲了关于首先,我们还是先找到能打印出这个是通过传入对应的
第一篇我们讲了关于 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协议详解
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Design Accessible Web Sites
Jeremy Sydik / Pragmatic Bookshelf / 2007-11-05 / USD 34.95
It's not a one-browser web anymore. You need to reach audiences that use cell phones, PDAs, game consoles, or other "alternative" browsers, as well as users with disabilities. Legal requirements for a......一起来看看 《Design Accessible Web Sites》 这本书的介绍吧!