iOS之runtime详解api(三)

栏目: IOS · 发布时间: 6年前

内容简介:第一篇我们讲了关于首先,我们还是先找到能打印出这个是通过传入对应的

第一篇我们讲了关于 ClassCategoryapi ,第二篇讲了关于 Methodapi ,这一篇来讲关于 IvarProperty

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_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 的用途最好是修改类型或者修饰符。`


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Design Accessible Web Sites

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》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

随机密码生成器
随机密码生成器

多种字符组合密码

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具