Лучший способ переопределить установщик атомарных свойств для ARC и поточно-ориентированный в Objective-C
У меня есть класс, содержащий свойство enum, и я хочу инициализировать его экземпляры из файла plist, используя метод setValuesForKeysWithDictionary:
, Но файл может содержать недопустимые значения. Сценарий таков:
В файле "MyClass.h":
typedef NS_ENUM(NSInteger, FruitTag) {
Apple = 0,
Banana = 1,
Orange = 2,
Unknown = 3
};
@interface MyClass : NSObject
@property (strong, nonatomic) NSString *name;
@property FruitTag tag;
@end
файл plist:
<plist version="1.0">
<array>
<dict>
<key>name</key>
<string>Apple</string>
<key>tag</key>
<integer>0</integer>
</dict>
<dict>
<key>name</key>
<string>Banana</string>
<key>tag</key>
<string>1</string>
</dict>
<dict>
<key>name</key>
<string>Pineapple</string>
<key>tag</key>
<string>5</string>
</dict>
</array>
</plist>
Как видите, последнее значение ключа "5" недопустимо, так как максимальное значение enum равно "3"! Но метод setValuesForKeysWithDictionary:
просто установите значение, независимо от того, является ли оно действительным или нет. Поэтому я хочу переопределить сеттер, но свойство enum является атомарным! Еще одна вещь: я хочу, чтобы его геттер и сеттер были безопасны в многопоточном сценарии. Думая о двух, я изменил код как:
В файле "Myclass.h":
typedef NS_ENUM(NSInteger, FruitTag) {
Apple = 0,
Banana = 1,
Orange = 2,
Unknown = 3
};
@interface MyClass : NSObject
@property (strong, nonatomic) NSString *name;
//@property FruitTag tag;
- (FruitTag)tag;
- (void)setTag:(NSInteger)aTag;
@end
И в файле "Myclass.m":
#import "MyClass.h"
@implementation MyClass
{
FruitTag tag;
}
- (FruitTag)tag
{
@synchronized (self) {
return tag;
}
}
- (void)setTag:(NSInteger)aTag
{
@synchronized (self) {
if ((aTag < Apple) || (aTag > Unknown)) {
tag = Unknown;
} else {
tag = aTag;
}
}
}
- (void)print
{
@synchronized (self) {
NSLog(@"the tag is: %d", tag);
}
}
@end
Хм, это выглядит странно, но работает, когда я тестирую, используя метод setValuesForKeysWithDictionary:
, Но я не уверен в этом: это нормально? потокобезопасен достаточно? любой потенциальный риск? или лучший способ переопределить более приветствуется! Спасибо!
1 ответ
Почему вы думаете, это выглядит странно? Вы только что вручную реализовали "свойство" и добавили простую проверку границ - хороший повод для реализации свойства самостоятельно.
Это потокобезопасный достаточно? Ну, это либо есть, либо нет, и @synchronized
конечно делает это так. Могут быть более эффективные, низкоуровневые способы сделать операции потокобезопасными; или вы можете даже обнаружить, что этого "достаточно", чтобы не делать ничего особенного; но если вы не обнаружили проблему с производительностью, вам не нужно идти туда - код прост и ваши намерения ясны.
НТН
Я бы порекомендовал такую реализацию:
@property (nonatomic, strong) dispatch_queue_t atomicSyncQueue;
@synthesize tag = _tag;
// @init time
_atomicSyncQueue = dispatch_queue_create("com.YOUR_DEBUG_INFO", DISPATCH_QUEUE_SERIAL);
- (NSInteger)tag {
__block NSInteger result = NO;
dispatch_sync(self.atomicSyncQueue, ^{
result = _tag;
});
return result;
}
- (void)setTag:(NSInteger)value {
dispatch_async(self.atomicSyncQueue, ^{
if (self->_tag != tag) {
self->_tag = tag;
// custom code:
}
});
}