五、属性(Property)的生成
属性Property的定义
typedef struct property_t *objc_property_t;
struct property_t{
const char *name;
const char *attributes;
};
Property
定义非常简单,只有名字name
和属性attributes
。Property
是在编译器生成好的,在methodizeClass(cls)
的时候从class_ro_t
复制到class_rw_t
中。
属性的生成
iOS中property
是由ivar+setter+getter
组成的,也就是说你声明了一个属性,编译器会自动帮你生成一个Ivars和两个Method。这过程都是在编译期间就完成的,我们来看一下。
首先我们新增一个类,定义了一个变量,三个属性和一个方法。
@interface TestProperty : NSObject{
NSInteger testIvar;
}
@property(nonatomic, assign) NSInteger testAssignInt;
@property(nonatomic, strong) NSString *testStrongProperty;
@property(nonatomic, copy) NSString *testCopyProperty;
- (void)testMethod;
@end
接着通过clang命令重写main.m文件
clang -rewrite-objc main.m
这时候我们来看下重写后的main.cpp文件,以下是几个关键点:
-
生成的方法
图一 - 变量Ivars
变量Ivars
可以看到这里有4个变量,分别是testIvar
、_testAssignInt
、_testStrongProperty
和_testCopyProperty
,后面三个是通过@property
自动生成的。 - 方法Method
方法Method
这里也和预期的一样,每个property
生成了对应的setter
和getter
方法,例如testStrongProperty
对应的分别是testStrongProperty
和setTestStrongProperty:
。 -
属性Property
属性Property
这里的属性共有3个,分别是:
testAssignInt : {"testAssignInt","Tq,N,V_testAssignInt"},
testStrongProperty : {"testStrongProperty","T@\"NSString\",&,N,V_testStrongProperty"},
testCopyProperty : {"testCopyProperty","T@\"NSString\",C,N,V_testCopyProperty"}
每一个属性都由两个字符串组成,例如第一个,testAssignInt
实际上就是property_t.name
,Tq,N,V_testAssignInt
就是property_t.attributes
,关于属性attributes
,例如N代表nonatomic,C代表copy,可以具体参考Declared Properties。
setter和getter
接下来看一下默认生成的setter
和getter
方法是怎样的,getter
方法比较简单,跟我们之前介绍的变量(Ivar)的结构与存放中的获取变量getIvars
的基本一致,通过变量的偏移量去获取。setter
的话基本类型和对象有些不一样:
// setTestAssignInt
static void _I_TestProperty_setTestAssignInt_(TestProperty * self, SEL _cmd, NSInteger testAssignInt) {
(*(NSInteger *)((char *)self + OBJC_IVAR_$_TestProperty$_testAssignInt)) = testAssignInt;
}
// setTestStrongProperty
static void _I_TestProperty_setTestStrongProperty_(TestProperty * self, SEL _cmd, NSString *testStrongProperty) {
(*(NSString **)((char *)self + OBJC_IVAR_$_TestProperty$_testStrongProperty)) = testStrongProperty;
}
// testCopyProperty
static void _I_TestProperty_setTestCopyProperty_(TestProperty * self, SEL _cmd, NSString *testCopyProperty) {
objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct TestProperty, _testCopyProperty), (id)testCopyProperty, 0, 1);
}
首先是基本类型的赋值,通过获取到变量之后直接赋值。然后是对象类型的,setTestStrongProperty
和基本类型一样,直接进行赋值,而setTestCopyProperty
调用了objc_setProperty
来进行赋值,来看下这个函数的实现:
void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, singed char shouldCopy){
bool copy = (shouldCopy && shouldCopy != MUTABLE_COPY);
bool mutableCopy = (shouldCopy == MUTABLE_COPY);
reallySetProperty(self, _cmd, newValue, offset, atomic, copy, mutableCopy);
}
void reallySetProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, bool copy, bool mutableCopy){
id oldValue;
id *slot = (id *)((char *)self + offset);
if(copy){
newValue = [newValue copyWithZone:nil];
}else if(mutableCopy){
newValue = [newValue mutableCopyWithZone:nil];
}else{
if (*slot == newValue) return;
newValue = objc_retain(newValue);
}
if(!atomic){
oldValue = *slot;
*slot = newValue;
}else{
spinlock_t& slotlock = PropertyLocks(slot);
slotlock.lock();
oldValue = *slot;
*slot = newValue;
slotlock.unlock();
}
objc_release(oldValue);
}
可见objc_setProperty主要是处理了atomic和copy这两个关键字,赋值完之后并对旧值进行了释放。
小结:
- property = ivar+setter+getter
- property都是在编译期确定好的。