Pesquisa sobre a implementação subjacente do modificador de cópia do iOS
Dê uma olhada na implementação subjacente dos modificadores copy, strong, reter, atmoic e nonatomic
Em relação às funções e diferenças dos modificadores de atributos @property comumente usados, como copiar, forte, reter, etc., há muitas discussões online, mas não encontrei nenhuma que explique claramente. Alguns até estão errados, ou outros deixaram isso claro, mas não entendo direito. Como diz o ditado, 源码面前,了无秘密, basta olhar o código-fonte para ver como ele está implementado. Depois de compreender o código-fonte, você nunca se desviará de seu significado original. Aqui olhamos principalmente para a implementação da cópia.
1. Dê uma olhada no código-fonte C++ forte, retido, copiado, atômico e não atômico
@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
Use clang -rewrite-objc propertyTest.m para gerar o código-fonte c++ conforme mostrado abaixo. Os comentários são muito claros.
// @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
####Resumo: Os métodos get de Strong, Copy e Retain encontram as variáveis de instância correspondentes com base no deslocamento de endereço e as retornam diretamente. O método atomic get usa o método objc_getProperty
conjunto forte encontra a variável de instância correspondente com base no deslocamento do endereço e a atribui diretamente Os métodos de retenção, cópia e conjunto atômico usam objc_setProperty
2. Como objc_getProperty e objc_setProperty são implementados?
objc_getProperty e objc_setProperty não foram encontrados no arquivo .cpp gerado. Encontrei no código fonte do 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);
}

Vejamos primeiro o método objc_setProperty. Este método tem um total de 6 valores. Os dois primeiros parâmetros são comuns ao método oc. Se não tiver certeza, você pode consultar o tempo de execução. Já existe muita informação na Internet. O terceiro parâmetro offset é o deslocamento relativo a si mesmo, que é usado para encontrar a variável de membro correspondente. Os últimos três parâmetros são id newValue, BOOL atomic e Signed Char. shouldCopy, a nomenclatura dos parâmetros é muito clara, não há necessidade de explicar mais, objc_setProperty chama objc_setProperty_non_gc, os parâmetros são os mesmos. Dê uma olhada na implementação de 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);
}

Você pode ver que o parâmetro da função objc_setProperty_non_gc chama a função realmenteSetProperty. Como último parâmetro, a função realmenteSetProperty possui um total de 7 parâmetros. Os primeiros 5 parâmetros correspondem aos primeiros 5 parâmetros de objc_setProperty_non_gc. Os valores dos dois últimos parâmetros são determinados por shouldCopy. O código acima mostra que o valor de shouldCopy aqui é 0 ou 1. Vamos considerar estas duas situações primeiro:
Se deveriaCopy=0 então copiar = NO, mutableCopy = NO Se deveriaCopy=1 então copiar = SIM, mutableCopy = NÃO
Vejamos a implementação de realmenteSetProperty
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);
}

Se deveriaCopy=0 então copiar = NO, mutableCopy = NO Se deveriaCopy=1 então copiar = SIM, mutableCopy = NÃO
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方法
Se copy e mutableCopy forem NÃO, será chamado objc_retain
objc_retain 实现如下
id objc_retain(id obj) { return [obj retain]; }

Também podemos ver pelo exposto que quando o atributo modificado com copy for atribuído, independentemente de ser um objeto mutável ou não, será um objeto imutável após ser atribuído ao atributo.
Vamos dar uma olhada no código relevante de atomic
if (!atomic) {
oldValue = *slot;
*slot = newValue;
} else {
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
oldValue = *slot;
*slot = newValue;
slotlock.unlock();
}
Você pode ver que o bloqueio spinlock_t é usado quando atomic é sim.
O método reter set é chamado da seguinte forma
objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct propertyTest, _nsstring___RetainTest), (id)nsstring___RetainTest, 0, 0);
De acordo com o processo de chamada de função acima, podemos obter: função realmenteSetProperty O parâmetro atômico é NO, o parâmetro copy é NO e mutableCopy também é NO. A implementação final é equivalente a
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);
}
A implementação final do método copy set é a seguinte:
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);
}
O método do conjunto de cópias atômicas é o seguinte
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 Dê uma olhada na implementação de objc_getProperty
id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic)
{
return objc_getProperty_non_gc(self, _cmd, offset, atomic);
}

Implementação de 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);
}

Pelo exposto, você pode ver que quando atomic é sim, os métodos set e get dos atributos correspondentes usam o bloqueio spinlock_t, que garante a segurança do thread dos métodos set e get, mas não garante a segurança do thread de outras operações, como operações de liberação em atributos.
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