属性的setter和getter方法
2020-11-19 本文已影响0人
forping
函数调用
简单声明四个属性
@property (nonatomic, strong) NSMutableArray *arr;
@property (strong) NSMutableArray *arr1;
@property (nonatomic, copy) NSMutableArray *arr2;
@property (nonatomic, retain) NSMutableArray *arr3;
arr 转换成arm64汇编的方法,去除汇编指令,并加上简单的注释
"-[A arr]": ; @"\01-[A arr]"
sub sp, sp, #16 ; =16 栈寄存器存储的值 -= 16
.cfi_def_cfa_offset 16 ; .cfi_def_cfa_offset 16 和 .cfi_offset %rbp, -16 会输出一些堆栈和调试信息,确保调试器要使用这些信息时能够找到
str x0, [sp, #8] ; 第一个参数self的地址 存放到 sp + 8 的地址值
str x1, [sp] ; 第二个参数 SELF 存放到sp
.loc 1 15 47 prologue_end ; 测试/A.h:15:47
ldr x8, [sp, #8] ; sp+8 也就是 self的地址 存放到x8
ldr x0, [x8, #8] ; self的地址+8; 也就是 arr的地址 存放到x0 也就是返回值
add sp, sp, #16 ; =16 恢复sp
ret ;返回arr的地址值
"-[A setArr:]": ; @"\01-[A setArr:]"
sub sp, sp, #48 ; =48 ; 栈寄存器存储的值 - 48
stp x29, x30, [sp, #32] ; 16-byte Folded Spill ; x29 X30(函数的返回值)的值放到 sp + 32的 内存中, 两个64位寄存器 需要16个字节,刚好 48 -32 = 16
add x29, sp, #32 ; =32 sp+32的地址值存储到x29
stur x0, [x29, #-8] ; 第一个参数self的地址 存放到 X29 - 8(sp+24) 的地址值,
str x1, [sp, #16] ; 第二个参数的地址SEL存放到sp + 16 的地址值
str x2, [sp, #8] ; ; 第二个参数的地址newValue存放到sp + 8 的地址值
ldr x1, [sp, #16] ; SEL 存储到x1
ldur x0, [x29, #-8] ; self的地址 存放到X0
ldr x2, [sp, #8] ;newValue存放到X2
mov x3, #8 ; 8 赋值给X3
bl _objc_setProperty_nonatomic ; 调用 _objc_setProperty_nonatomic 函数 (bl:将下一条指令(ldp x29, x30, [sp, #32] ; 16-byte Folded Reload)的地址存储到lr(x30)寄存器里)
ldp x29, x30, [sp, #32] ; 16-byte Folded Reload 将sp+32地址存储的值 赋给 x29 和 X30寄存器
add sp, sp, #48 ; =48 还原栈
ret ; 返回
arr1
"-[A arr1]": ; @"\01-[A arr1]"
sub sp, sp, #16 ; =16
.cfi_def_cfa_offset 16
str x0, [sp, #8]
str x1, [sp]
ldr x1, [sp]
ldr x0, [sp, #8]
mov x2, #16
mov w8, #1
and w3, w8, ;0x1 // w8 和 1 进行逻辑与运算,然后运算结果存储到w3,第三个参数
add sp, sp, #16 ; =16
b _objc_getProperty ; 用的b指令,证明不会返回到当前函数了. 执行完_objc_getProperty就跳转到当前函数的调用地址的下一条指令了
"-[A setArr1:]": ; @"\01-[A setArr1:]"
sub sp, sp, #48 ; =48
stp x29, x30, [sp, #32] ; 16-byte Folded Spill
add x29, sp, #32 ; =32
stur x0, [x29, #-8]
str x1, [sp, #16]
str x2, [sp, #8]
ldr x1, [sp, #16]
ldur x0, [x29, #-8]
ldr x2, [sp, #8]
mov x3, #16
bl _objc_setProperty_atomic
ldp x29, x30, [sp, #32] ; 16-byte Folded Reload
add sp, sp, #48 ; =48
ret
arr2
"-[A arr2]": ; @"\01-[A arr2]"
sub sp, sp, #16 ; =16
str x0, [sp, #8]
str x1, [sp]
ldr x1, [sp]
ldr x0, [sp, #8]
mov x2, #24
mov w8, #0
and w3, w8, #0x1 ; 0和1 进行逻辑于运算,结果存储到 第三个参数
add sp, sp, #16 ; =16
b _objc_getProperty
"-[A setArr2:]": ; @"\01-[A setArr2:]"
sub sp, sp, #48 ; =48
stp x29, x30, [sp, #32] ; 16-byte Folded Spill
add x29, sp, #32 ; =32
stur x0, [x29, #-8]
str x1, [sp, #16]
str x2, [sp, #8]
ldr x1, [sp, #16]
ldur x0, [x29, #-8]
ldr x2, [sp, #8]
mov x3, #24
bl _objc_setProperty_nonatomic_copy
ldp x29, x30, [sp, #32] ; 16-byte Folded Reload
add sp, sp, #48 ; =48
ret
arr3
"-[A arr3]": ; @"\01-[A arr3]"
sub sp, sp, #16 ; =16
str x0, [sp, #8]
str x1, [sp]
ldr x8, [sp, #8]
ldr x0, [x8, #32]
add sp, sp, #16 ; =16
ret
"-[A setArr3:]": ; @"\01-[A setArr3:]"
sub sp, sp, #48 ; =48
stp x29, x30, [sp, #32] ; 16-byte Folded Spill
add x29, sp, #32 ; =32
stur x0, [x29, #-8]
str x1, [sp, #16]
str x2, [sp, #8]
ldr x1, [sp, #16]
ldur x0, [x29, #-8]
ldr x2, [sp, #8]
mov x3, #32
bl _objc_setProperty_nonatomic
ldp x29, x30, [sp, #32] ; 16-byte Folded Reload
add sp, sp, #48 ; =48
ret
getter 函数里第一个和第四个属性的汇编码基本一样,根据地址得到属性的值,第二个和第三个最后跳转到了_objc_getProperty然后获得值:
setter函数跳转 _objc_setProperty_nonatomic, _objc_setProperty_atomic, _objc_setProperty_nonatomic_copy三个函数,我们从runtime源码中来找到这些函数
setter
//底层都是调用了 reallySetProperty函数
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(id self, SEL _cmd, id newValue, ptrdiff_t offset)
{
reallySetProperty(self, _cmd, newValue, offset, true, false, false);
}
void objc_setProperty_nonatomic_copy(id self, SEL _cmd, id newValue, ptrdiff_t offset)
{
reallySetProperty(self, _cmd, newValue, offset, false, true, false);
}
reallySetProperty
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
if (offset == 0) { // offset 表示赋值的是isa
object_setClass(self, newValue);
return;
}
id oldValue;
id *slot = (id*) ((char*)self + offset); // self 保存的地址值加上偏移量得到旧值的地址值, slot 是一个指针,存储的是 旧值的地址值 ,旧值如果是对象,那么旧值也会是个指针,因此用id * 来表示
// 针对策略处理新的value值
if (copy) {
newValue = [newValue copyWithZone:nil];
} else if (mutableCopy) {
newValue = [newValue mutableCopyWithZone:nil];
} else {// 如果不进行copy操作的话
if (*slot == newValue) return; // 如果两个值的地址是一样的,直接返回
newValue = objc_retain(newValue); retain新值
}
if (!atomic) { // 如果不需要加锁
oldValue = *slot; // 旧值 赋给oldValue
*slot = newValue; // self + offset 的这个地址保存newValue的地址值
} else {
// extern StripedMap<spinlock_t> PropertyLocks;
// using spinlock_t = mutex_tt<LOCKDEBUG>;
// typedef mutex_t spinlock_t;
// using mutex_t = mutex_tt<LOCKDEBUG>;
// class mutex_tt : nocopy_t {
//os_unfair_lock mLock;
//...
//}
// & 是引用符号,引用是C++对C的一个重要补充,在声明一个引用时,必须同时使之初始化,即声明它代表哪一个变量。请注意:由于引用不是独立的变量,编译系统不给它单独分配存储单元,因此在建立引用时只有声明没有定义,只是声明它与原有的某一变量的关系。在声明一个变量的引用后,在本函数执行期间,该引用一直与其代表的变量相联系,不能再作为其他变量的别名。
// 怎样区分&是引用还是取地址符呢?方法是:判断&a这样的形式前是否有类型符即int &a=b;如果有类型符(int)则是引用,否则是取地址运算符。
// 所以 spinlock_t& 就是 spinlock_t类型,只不过引用了 PropertyLocks[slot]; 的返回值,不占用存储单元
spinlock_t& slotlock = PropertyLocks[slot]; // spinlock_t 是os_unfair_lock互斥锁 ,因为OSSpinLock自旋锁已经不安全了,会有优先级反转的问题
slotlock.lock(); // 加锁
oldValue = *slot;
*slot = newValue;
slotlock.unlock(); // 解锁
}
objc_release(oldValue); // 旧值的引用计数-1
}
看代码的时候对id *slot = (id*) ((char*)self + offset);
有所疑惑,画了个图就明白了.
getter方法
id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
if (offset == 0) { // 判断偏移量 如果是0,根据isa指针获得类对象
return object_getClass(self);
}
// Retain release world
id *slot = (id*) ((char*)self + offset); // 根据偏移量获得 获得成员变量的地址值. 而成员变量是一个id类型的 因此用 id *引用
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); // 返回value
}
id
objc_autoreleaseReturnValue(id obj)
{
if (prepareOptimizedReturn(ReturnAtPlus1)) return obj;
return objc_autorelease(obj);
}
id objc_autorelease(id obj) { return [obj autorelease]; }
如果不加锁,获得值直接返回,如果加锁,直接返回或加入自动释放池返回