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
{@publicint _age;}
@property(nonatomic, copy)NSString* name;@property(nonatomic, copy)NSString *breed;@property(nonatomic, copy)NSString* style;@endCat.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的名字,看打印是否是ivarchar* 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 replacereturn NO;} else if (prop) {// replace existingrwlock_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 existingrwlock_writer_t lock(runtimeLock);try_free(prop->attributes);prop->attributes = copyPropertyAttributeString(attrs, count);return YES;}
复制代码
从这一段我们可以看到这一部分只改变了 prop->attributes
。也没有改变 prop->name
。所以,我们打印属性的name
自然没有改变。那么,class_replaceProperty
的用途最好是修改类型或者修饰符。`
iOS之runtime详解api(三)相关推荐
- iOS开发-Runtime详解(简书)
简介 Runtime 又叫运行时,是一套底层的 C 语言 API,其为 iOS 内部的核心之一,我们平时编写的 OC 代码,底层都是基于它来实现的.比如: [receiver message]; // ...
- 李洪强iOS经典面试题156 - Runtime详解(面试必备)
李洪强iOS经典面试题156 - Runtime详解(面试必备) 一.runtime简介 RunTime简称运行时.OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的是消息机制. 对于C ...
- IOS 多线程04-GCD详解 底层并发 API
IOS 多线程04-GCD详解 底层并发 API 注:本人是翻译过来,并且加上本人的一点见解. 前言 想要揭示出表面之下深层次的一些可利用的方面.这些底层的 API 提供了大量的灵活性,随之而来的是大 ...
- Android高效率编码-第三方SDK详解系列(三)——JPush推送牵扯出来的江湖恩怨,XMPP实现推送,自定义客户端推送
Android高效率编码-第三方SDK详解系列(三)--JPush推送牵扯出来的江湖恩怨,XMPP实现推送,自定义客户端推送 很久没有更新第三方SDK这个系列了,所以更新一下这几天工作中使用到的推送, ...
- iOS 2D绘图详解(Quartz 2D)之路径(点,直线,虚线,曲线,圆弧,椭圆,矩形)
前言:一个路径可以包含由一个或者多个shape以及子路径subpath,quartz提供了很多方便的shape可以直接调用.例如:point,line,Arc(圆弧),Curves(曲线),Ellip ...
- 【java8新特性】——Optional详解(三)
一.简介 Optional类是Java8为了解决null值判断问题,借鉴google guava类库的Optional类而引入的一个同名Optional类,使用Optional类可以避免显式的null ...
- NTFS文件系统详解(三)NTFS元文件解析
NTFS文件系统详解(三)NTFS元文件解析 一. 分析$Boot文件 二.分析文件记录 1. MFT偏移地址计算 2. 文件记录的结构 3. 属性的属性头分析 4. 属性的属性体分析 NTFS文件系 ...
- XXE漏洞详解(三)——XXE漏洞实际运用
今天继续给大家介绍渗透测试相关知识,本文主要内容是XXE漏洞详解(三)--XXE漏洞实际运用. 免责声明: 本文所介绍的内容仅做学习交流使用,严禁利用文中技术进行非法行为,否则造成一切严重后果自负! ...
- Python之ruamel.yaml模块详解(三)| ruamel.yaml与pyyaml的区别
Python之ruamel.yaml模块详解(三)| ruamel.yaml与pyyaml的区别 12 默认支持Yaml1.2 13 Py2和Py3重新整合 14 修复 15 测试 16 API 接前 ...
最新文章
- mp4格式解析、分割
- golang中的strings.HasSuffix
- 安全测试的目的,发现哪些问题
- 【GAN优化】如何选好正则项让你的GAN收敛
- 1792 关于数论中的互质数的最大不能组合数
- 分区表学习一:分区表介绍
- oracle命令行查看编码,Oracle数据库查看编码和修改编码
- MySQL服务迁移到opt_mysql文件*.opt *.frm *.MYI *.MYD的迁移
- 常见Java开发过程中遇到的问题及其解决办法
- 终端编译opengl程序编译运行_ubuntu – 通过SSH编写opengl代码,通过机器显示运行程序...
- 数字IC后端设计实现流程之initial design
- [转载] Python3 使用 SQLite3 数据库的操作笔记:批量插入
- zabbix通过UNIXODBC连接数据库
- c语言旋转bmp图片程序,C语言实现BMP图像处理(任意角度旋转)
- 离散数学杜忠复版答案_离散数学(第二版)课后习题答案详解(完整版)
- Proxmark3 Easy破解门禁卡(转载 珍贵知识防止掉失)
- 马里兰帕克分校计算机科学,马里兰大学帕克分校管理信息系统(MIS)专业详解...
- 常用计算机字体的后缀名是,自考计算机应用基础试题及参考答案
- 【成为架构师课程系列】架构师的核心能力地图
- YOLO系列 --- YOLOV7算法(二):YOLO V7算法detect.py代码解析
热门文章
- Shell脚本基础介绍
- 在Ubuntu11.10中安装配置OpenCV2.3.1和CodeBlocks
- 【ubuntu】ubuntu14.04、16.04、18.04 LTS版本支持时间
- 实现了html实现网页无限下滑,10行代码实现页面无限滚动
- oracle bl编译,使用 PL/SQL 条件编译
- 三星a5009Android6.0,三星A5009原版系统刷机包_三星A5009最新升级包线刷包和root
- postgres默认安装后有哪些表_Greenplum 行存、列存,堆表、AO表的原理和选择
- ctypealpha php_php ctype函数中文翻译和示例
- 商淘多b2b2c商城系统怎么在个人电脑上安装_社交电商系统开发是否有价值?
- python123第k序元素查找_Python实现折半查找并用matplotlib实现动态过程可视化