HR's Blog

Swimming 🏊 in the sea🌊of code!

0%

Setter和Getter原理

.

当我们在@property上加上strong和copy关键字以后,编译器都为我们做了什么?

1. 实例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@interface HRObject : NSObject
@property (nonatomic, strong) NSString *strongName;
@property (nonatomic, copy) NSString *weakName;
@end

@implementation HRObject
@end

int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");

HRObject *object = [HRObject new];
object.strongName = @"strong";
object.weakName = @"weak";
}
return 0;
}
1
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o arm64.cpp

2. 编译后源码

1
2
3
4
5
6
7
8
9
10
11
12
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;

NSLog((NSString *)&__NSConstantStringImpl__var_folders_kw_5dr8tfr96kn9f2mf2nh0111r0000gp_T_main_c597e0_mi_0);

HRObject *object = ((HRObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("HRObject"), sel_registerName("new"));
((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)object, sel_registerName("setStrongName:"), (NSString *)&__NSConstantStringImpl__var_folders_kw_5dr8tfr96kn9f2mf2nh0111r0000gp_T_main_c597e0_mi_1);
((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)object, sel_registerName("setWeakName:"), (NSString *)&__NSConstantStringImpl__var_folders_kw_5dr8tfr96kn9f2mf2nh0111r0000gp_T_main_c597e0_mi_2);

}
return 0;
}

可以看到我们在使用点语法的时候,最终是生成了相应的set方法setStrongName:setWeakName:,找到这两个方法的实现,_INSTANCE_METHODS_HRObject给所有参数都申请了SetterGetter方法。而object.weakName = @"weak";最终调用的是_I_HRObject_setWeakName_方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[8];
} _OBJC_$_INSTANCE_METHODS_HRObject __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
8,
{{(struct objc_selector *)"strongName", "@16@0:8", (void *)_I_HRObject_strongName},
{(struct objc_selector *)"setStrongName:", "v24@0:8@16", (void *)_I_HRObject_setStrongName_},
{(struct objc_selector *)"weakName", "@16@0:8", (void *)_I_HRObject_weakName},
{(struct objc_selector *)"setWeakName:", "v24@0:8@16", (void *)_I_HRObject_setWeakName_},
{(struct objc_selector *)"strongName", "@16@0:8", (void *)_I_HRObject_strongName},
{(struct objc_selector *)"setStrongName:", "v24@0:8@16", (void *)_I_HRObject_setStrongName_},
{(struct objc_selector *)"weakName", "@16@0:8", (void *)_I_HRObject_weakName},
{(struct objc_selector *)"setWeakName:", "v24@0:8@16", (void *)_I_HRObject_setWeakName_}}
};

_I_HRObject_setWeakName_最终调用的是objc_setProperty

1
static void _I_HRObject_setWeakName_(HRObject * self, SEL _cmd, NSString *weakName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct HRObject, _weakName), (id)weakName, 0, 1); }

3.Runtime源码

申明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/* Properties */

//🏆Getter方法
// Read or write an object property. Not all object properties use these.
id _Nullable objc_getProperty(id _Nullable self, SEL _Nonnull _cmd,
ptrdiff_t offset, BOOL atomic);

//🏆Setter方法
void objc_setProperty(id _Nullable self, SEL _Nonnull _cmd, ptrdiff_t offset,
id _Nullable newValue, BOOL atomic, signed char shouldCopy);

void objc_setProperty_atomic(id _Nullable self, SEL _Nonnull _cmd,
id _Nullable newValue, ptrdiff_t offset);

void objc_setProperty_nonatomic(id _Nullable self, SEL _Nonnull _cmd,
id _Nullable newValue, ptrdiff_t offset);

void objc_setProperty_atomic_copy(id _Nullable self, SEL _Nonnull _cmd,
id _Nullable newValue, ptrdiff_t offset);

void objc_setProperty_nonatomic_copy(id _Nullable self, SEL _Nonnull _cmd,
id _Nullable newValue, ptrdiff_t offset);

// Read or write a non-object property. Not all uses are C structs,
// and not all C struct properties use this.
void objc_copyStruct(void * _Nonnull dest, const void * _Nonnull src,
ptrdiff_t size, BOOL atomic, BOOL hasStrong);

// Perform a copy of a C++ object using striped locks. Used by non-POD C++ typed atomic properties.
void objc_copyCppObjectAtomic(void * _Nonnull dest, const void * _Nonnull src,
void (* _Nonnull copyHelper)
(void * _Nonnull dest, const void * _Nonnull source))

Getter

Getter方法的逻辑比较简单,判断是否有加锁,没有的话,直接返回变量的值,有的话则先加锁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
if (offset == 0) {
return object_getClass(self);
}

// Retain release world
id *slot = (id*) ((char*)self + offset);
if (!atomic) return *slot;

// Atomic retain release world
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
id value = objc_retain(*slot);
slotlock.unlock();

// for performance, we (safely) issue the autorelease OUTSIDE of the spinlock.
return objc_autoreleaseReturnValue(value);
}

Setter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void objc_setProperty_atomic(id self, SEL _cmd, id newValue, ptrdiff_t offset) {
reallySetProperty(self, _cmd, newValue, offset, true, false, false);
}

void objc_setProperty_nonatomic(id self, SEL _cmd, id newValue, ptrdiff_t offset) {
reallySetProperty(self, _cmd, newValue, offset, false, false, false);
}

void objc_setProperty_atomic_copy(id self, SEL _cmd, id newValue, ptrdiff_t offset) {
reallySetProperty(self, _cmd, newValue, offset, true, true, false);
}

void objc_setProperty_nonatomic_copy(id self, SEL _cmd, id newValue, ptrdiff_t offset) {
reallySetProperty(self, _cmd, newValue, offset, false, true, false);
}
1
2
3
4
5
void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy) {
bool copy = (shouldCopy && shouldCopy != MUTABLE_COPY);
bool mutableCopy = (shouldCopy == MUTABLE_COPY);
reallySetProperty(self, _cmd, newValue, offset, atomic, copy, mutableCopy);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy) {
if (offset == 0) {
object_setClass(self, newValue);
return;
}

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);
}

4. 成员变量的Offset是怎么计算的?

The sequence of operations can be illustrated in the following way:

1
2
3
4
5
1. 0
2. ((TYPE *)0)
3. ( ((TYPE *)0)->MEMBER )
4. &( ((TYPE *)0)->MEMBER )
5. ( (size_t) &( ((TYPE *)0)->MEMBER )
  1. 定义数字0。
  2. 将数字0转换成指向struct Type的指针。也就是把Type的起始位置转变成0。
  3. 获取结构体Type的变量MEMBER
  4. 但是并没有使用MEMBER的值,使用的是MEMBER的内存地址。
  5. 最终又把地址转换成了size_t的类型。

因为这个结构体的起始位置被指定为0,所以MEMBER的地址转换成数字的时候,就是MEMBER基于struct的偏移量。

这个特定代码无害的原因是没有写入,甚至没有访问过内存位置。 一切都只涉及指向这些位置(而不是它们的内容)和数字的指针。 所有这些都保存在机器寄存器或通常的本地堆栈中。

How to understand “((size_t) &((TYPE *)0)->MEMBER)”?