Back home

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.