Я хотел бы знать, верны ли оба следующих решения для ленивой инициализации.
У меня есть класс AppContext
, который должен содержать ссылки на другой класс, который должен существовать только один раз (не делая каждый из этих классов одноэлементным). Допустим, один из этих других классов называется ReferencedClass
. При этом я хотел бы лениво инициализировать ссылки со значениями по умолчанию потокобезопасным способом.
Это обсуждалось раньше, и я много читал об этом, но я все еще не уверен. Помимо личных предпочтений, я хотел бы знать следующее: Являются ли эти два решения правильным способом реализовать желаемое поведение?
Решение 1. Изначально я хотел реализовать это следующим образом:
// Getter with lazy initialized default value
- (ReferencedClass *)referencedClass {
// Check if nil. If yes, wait for lock and check again after locking.
if (_referencedClass == nil) {
@synchronized(self) {
if (_referencedClass == nil) {
// Prevent _referencedClass pointing to partially initialized objects
ReferencedClass *temp = [[ReferencedClass alloc] init];
_referencedClass = temp;
}
}
}
return _referencedClass;
}
// Setter
- (void)setReferencedClass:(ReferencedClass *)referencedClass {
@synchronized(self) {
_referencedClass = referencedClass;
}
}
Решение 2. Затем я решил использовать GCD, поэтому написал следующее:
// Getter with lazy initialized default value
- (ReferencedClass *)referencedClass {
// Check if nil. If yes, wait for "lock" and check again after "locking".
if (_referencedClass == nil) {
dispatch_sync(syncDispatchQueue, ^{
if (_referencedClass == nil) {
// Prevent _referencedClass pointing to partially initialized objects
ReferencedClass *temp = [[ReferencedClass alloc] init];
_referencedClass = temp;
}
});
}
return _referencedClass;
}
// Setter
- (void)setReferencedClass:(ReferencedClass *)referencedClass {
dispatch_sync(syncDispatchQueue, ^{
_referencedClass = referencedClass;
});
}
Конечно, где-то (например, в методе init
) я инициализировал syncDispatchQueue
чем-то вроде:
syncDispatchQueue = dispatch_queue_create("com.stackoverflow.lazy", NULL);
Является ли это правильным, потокобезопасным и свободным от взаимоблокировок кодом? Могу ли я использовать блокировку с двойной проверкой вместе с переменной temp
? Если эта блокировка с двойной проверкой небезопасна, будет ли мой код в обоих случаях безопасным, если я удалю внешние проверки? Я так думаю, да?
Большое спасибо заранее!
[Примечание: мне известно о dispatch_once и о том, что некоторые говорят, что (вопреки документации Apple) его также можно использовать с переменными экземпляра. Пока я хотел бы использовать один из этих двух вариантов. Если возможно. ]