Penelitian tentang implementasi yang mendasari pengubah salinan iOS
Lihatlah implementasi yang mendasari pengubah salin, kuat, pertahankan, atmoik, dan nonatomik
Mengenai fungsi dan perbedaan atribut modifier @property yang umum digunakan seperti copy, strong, hold, dll, banyak sekali diskusi online, namun saya belum menemukan satupun yang menjelaskan dengan jelas. Bahkan ada yang salah, atau ada pula yang sudah memperjelas, namun saya kurang memahaminya dengan baik. Seperti kata pepatah, 源码面前,了无秘密, lihat saja kode sumbernya untuk melihat cara penerapannya. Setelah Anda memahami kode sumbernya, Anda tidak akan pernah menyimpang dari makna aslinya. Di sini kita terutama melihat implementasi penyalinan.
1. Lihatlah kode sumber c++ yang kuat, simpan, salin, atomik, nonatomik
@interface propertyTest : NSObject
@property (nonatomic, strong) NSString *nsstring___StrongTest;
@property (nonatomic, retain) NSString *nsstring___RetainTest;
@property (nonatomic, copy) NSString *nsstring___CopyTest;
@property (atomic, copy) NSString *nsstring___AtomicCopyTest;
@end
Gunakan clang -rewrite-objc propertyTest.m untuk menghasilkan kode sumber c++ seperti yang ditunjukkan di bawah ini. Komentarnya sangat jelas.
// @implementation propertyTest
//nsstring___StrongTest get方法
static NSString * _I_propertyTest_nsstring___StrongTest(propertyTest * self, SEL _cmd)
{
//strong get 根据地址偏移找到对应的实例变量 直接返回
return (*(NSString **)((char *)self + OBJC_IVAR_$_propertyTest$_nsstring___StrongTest));
}
//nsstring___StrongTest set方法
static void _I_propertyTest_setNsstring___StrongTest_(propertyTest * self, SEL _cmd, NSString *nsstring___StrongTest)
{
//strong set 根据地址偏移找到对应的实例变量 直接赋值
(*(NSString **)((char *)self + OBJC_IVAR_$_propertyTest$_nsstring___StrongTest)) = nsstring___StrongTest;
}
//////===============
//nsstring___RetainTest get方法
static NSString * _I_propertyTest_nsstring___RetainTest(propertyTest * self, SEL _cmd)
{
//retain get 根据地址偏移找到对应的实例变量 直接返回
return (*(NSString **)((char *)self + OBJC_IVAR_$_propertyTest$_nsstring___RetainTest));
}
//由于下面要用objc_setProperty,声明objc_setProperty在外部文件定义
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
static void _I_propertyTest_setNsstring___RetainTest_(propertyTest * self, SEL _cmd, NSString *nsstring___RetainTest)
{
//retain set方法 ,和strong不一样,这里用到了objc_setProperty
objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct propertyTest, _nsstring___RetainTest), (id)nsstring___RetainTest, 0, 0);
}
//////===============
//nsstring___CopyTest get方法
static NSString * _I_propertyTest_nsstring___CopyTest(propertyTest * self, SEL _cmd)
{
//copy get 根据地址偏移找到对应的实例变量 直接返回
return (*(NSString **)((char *)self + OBJC_IVAR_$_propertyTest$_nsstring___CopyTest));
}
//nsstring___CopyTest set方法
static void _I_propertyTest_setNsstring___CopyTest_(propertyTest * self, SEL _cmd, NSString *nsstring___CopyTest)
{
//copy set方法 ,和strong不一样,和retain一样, 用到了objc_setProperty
objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct propertyTest, _nsstring___CopyTest), (id)nsstring___CopyTest, 0, 1);
}
//////===============
//由于下面要用objc_getProperty,声明objc_getProperty在外部文件定义
extern "C" __declspec(dllimport) id objc_getProperty(id, SEL, long, bool);
//nsstring___AtomicCopyTest get方法
static NSString * _I_propertyTest_nsstring___AtomicCopyTest(propertyTest * self, SEL _cmd)
{
//atomic get方法 用到了objc_getProperty
typedef NSString * _TYPE;
return (_TYPE)objc_getProperty(self, _cmd, __OFFSETOFIVAR__(struct propertyTest, _nsstring___AtomicCopyTest), 1);
}
//nsstring___AtomicCopyTest set方法
static void _I_propertyTest_setNsstring___AtomicCopyTest_(propertyTest * self, SEL _cmd, NSString *nsstring___AtomicCopyTest)
{
//atomic set方法 同样用到了 objc_setProperty
objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct propertyTest, _nsstring___AtomicCopyTest), (id)nsstring___AtomicCopyTest, 1, 1);
}
// @end
####Ringkasan: Metode get yang kuat, salin, dan simpan menemukan variabel instan yang sesuai berdasarkan offset alamat dan mengembalikannya secara langsung. Metode get atom menggunakan metode objc_getProperty
set kuat menemukan variabel instan yang sesuai berdasarkan offset alamat dan menetapkannya secara langsung Metode penyimpanan, penyalinan, dan kumpulan atom menggunakan objc_setProperty
2. Bagaimana objc_getProperty dan objc_setProperty diimplementasikan?
objc_getProperty dan objc_setProperty tidak ditemukan di file .cpp yang dihasilkan. Ditemukan di kode sumber objc
id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic)
{
return objc_getProperty_non_gc(self, _cmd, offset, atomic);
}
void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy)
{
objc_setProperty_non_gc(self, _cmd, offset, newValue, atomic, shouldCopy);
}

Mari kita lihat metode objc_setProperty terlebih dahulu. Metode ini memiliki total 6 nilai. Dua parameter pertama umum untuk metode oc. Jika Anda tidak yakin, Anda dapat melihat runtime-nya. Sudah banyak informasi di Internet. Parameter ketiga offset adalah offset relatif terhadap diri, yang digunakan untuk mencari variabel anggota yang sesuai. Tiga parameter terakhir adalah id newValue, BOOL atom, dan karakter yang ditandatangani. mustCopy, penamaan parameternya sangat jelas, tidak perlu dijelaskan lagi, objc_setProperty memanggil objc_setProperty_non_gc, parameternya sama. Lihatlah implementasi objc_setProperty_non_gc
void objc_setProperty_non_gc(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);
}

Anda dapat melihat bahwa parameter fungsi objc_setProperty_non_gc memanggil fungsi realSetProperty. Sebagai parameter terakhir, fungsi realSetProperty memiliki total 7 parameter. 5 parameter pertama sesuai dengan 5 parameter pertama objc_setProperty_non_gc. Nilai dari dua parameter terakhir ditentukan oleh mustCopy. Kode di atas menunjukkan bahwa nilai mustCopy di sini adalah 0 atau 1. Mari kita pertimbangkan dua situasi ini terlebih dahulu:
Jika seharusnyaCopy=0 maka salin = TIDAK, mutableCopy = TIDAK Jika seharusnyaCopy=1 maka salin = YA, mutableCopy = TIDAK
Mari kita lihat implementasi dari trueSetProperty
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);
}

Jika seharusnyaCopy=0 maka salin = TIDAK, mutableCopy = TIDAK Jika seharusnyaCopy=1 maka salin = YA, mutableCopy = TIDAK
if (copy) {
newValue = [newValue copyWithZone:nil];
} else if (mutableCopy) {
newValue = [newValue mutableCopyWithZone:nil];
} else {
if (*slot == newValue) return;
newValue = objc_retain(newValue);
}
上面就是copy关键字的实现了,copy == YES,调用[newValue copyWithZone:nil],返回的是不可变对应,最终还是调用了copyWithZone方法
Jika copy dan mutableCopy keduanya TIDAK, maka akan dipanggil objc_retain
objc_retain 实现如下
id objc_retain(id obj) { return [obj retain]; }

Kita juga dapat melihat dari penjelasan di atas bahwa ketika atribut yang dimodifikasi dengan copy ditetapkan, terlepas dari apakah itu objek yang dapat diubah atau tidak, itu akan menjadi objek yang tidak dapat diubah setelah ditetapkan ke atribut tersebut.
Mari kita lihat kode atomic yang relevan
if (!atomic) {
oldValue = *slot;
*slot = newValue;
} else {
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
oldValue = *slot;
*slot = newValue;
slotlock.unlock();
}
Anda dapat melihat bahwa kunci spinlock_t digunakan ketika atomik adalah ya.
Metode set penahan disebut sebagai berikut
objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct propertyTest, _nsstring___RetainTest), (id)nsstring___RetainTest, 0, 0);
Berdasarkan proses pemanggilan fungsi di atas, kita dapat memperoleh: fungsi benar-benarSetProperty Parameter atomnya adalah NO, parameter copynya adalah NO, dan mutableCopy juga NO. Implementasi akhir setara dengan
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
id oldValue;
id *slot = (id*) ((char*)self + offset);
if (*slot == newValue) return;
newValue = objc_retain(newValue);
oldValue = *slot;
*slot = newValue;
objc_release(oldValue);
}
Implementasi akhir dari metode copy set adalah sebagai berikut:
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
id oldValue;
//取出变量
id *slot = (id*) ((char*)self + offset);
newValue = [newValue copyWithZone:nil];
oldValue = *slot;
*slot = newValue;
objc_release(oldValue);
}
Metode kumpulan salinan atom adalah sebagai berikut
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
id oldValue;
id *slot = (id*) ((char*)self + offset);
newValue = [newValue copyWithZone:nil];
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
oldValue = *slot;
*slot = newValue;
slotlock.unlock();
objc_release(oldValue);
}
3 Lihatlah implementasi objc_getProperty
id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic)
{
return objc_getProperty_non_gc(self, _cmd, offset, atomic);
}

Implementasi objc_getProperty_non_gc
id objc_getProperty_non_gc(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);
}

Dari penjelasan di atas, Anda dapat melihat bahwa ketika atomik adalah ya, metode set dan get dari atribut yang sesuai menggunakan kunci spinlock_t, yang menjamin keamanan thread dari metode set dan get, tetapi tidak menjamin keamanan thread dari operasi lain, seperti operasi rilis pada atribut.
What to read next
Want more posts about iOS?
Posts in the same category are usually the best next step for reading more on this topic.
View same categoryWant to keep following #iOS?
Tags are useful for related tools, specific problems, and similar troubleshooting notes.
View same tagWant to explore another direction?
If you are not sure what to read next, return to the homepage and start from categories, topics, or latest updates.
Back home