Грешка при използване на Realm Object като сингълтън. Добра идея ли е?

В момента се опитвам да използвам Realm в моето приложение и си помислих да използвам подклас на RLMObject като сингълтон за съхранение на състояние на приложението.

Първата ми мисъл е дали идеята е много лоша.

Моят AppState клас има 2 основни метода:

+ (instancetype)sharedInstance
{
    static dispatch_once_t once;
    static id sharedInstance = nil;

    RLMResults *result = [[self class] allObjects];

    if (result.count == 0) {
        dispatch_once(&once, ^{
            RLMRealm *realm = [RLMRealm defaultRealm];
            sharedInstance = [[self alloc] init];
            [realm beginWriteTransaction];
            [realm addObject:sharedInstance];
            [realm commitWriteTransaction];
        });
    }
    else {
        sharedInstance = [[[self class] allObjects] lastObject];
    }

    return sharedInstance;
}

- (void)update {
    RLMRealm *realm = self.realm;

    [realm beginWriteTransaction];
    [realm addOrUpdateObject:[[self class] sharedInstance]];
    [realm commitWriteTransaction];
}

Имам свойства за съхраняване на няколко параметъра за състоянието на приложението.

В един от моите контролери за изглед получавам следната грешка:

„Опит за модифициране на обект извън записваща транзакция – първо извикайте beginWriteTransaction на RLMRealm екземпляр.“

Кодовият фрагмент, в който възниква грешката, е както следва:

AppState *defaultState = [AppState sharedInstance];
defaultState.appStateX = newAppStateValue; // This is where the app crashes.
[defaultState update];

Промених self.realm на [RLMRealm defaultRealm] в моя -(void)update метод. Без зарове!

Чувствам, че има нещо фундаментално нередно с разбирането ми за Singletons и/или RLMObjects. Всяка помощ се оценява.


Актуализация

Въз основа на отговора на Сакамото промених моя -(void)update метод, за да приема block като аргумент. Има много място за грешки при този подход, но мога да продължа с текущото си внедряване, без да правя драстични промени.

- (void)update:(void(^)(void))block {
    RLMRealm *realm = self.realm;

    [realm beginWriteTransaction];
    block();
    [realm commitWriteTransaction];
}

Актуализирам всички имоти в блока.


person srik    schedule 31.12.2014    source източник


Отговори (1)


Според документа на Realm http://realm.io/docs/cocoa/0.89.1/

Realm обектите могат да бъдат създадени и използвани самостоятелно точно като обикновените обекти.

...

След като добавите обекта към Realm, можете да продължите да го използвате и всички промени, които направите в него, ще бъдат запазени (и трябва да бъдат направени в рамките на транзакция за запис).

Обектът, който беше върнат от метода [AppState sharedInstance], вече беше добавен към Realm. Следователно промяната на свойствата на обекта изисква транзакция за запис, както следва.

AppState *defaultState = [AppState sharedInstance];
[defaultState.realm beginWriteTransaction];
defaultState.appStateX = newAppStateValue;
[defaultState.realm commitWriteTransaction];
person Kazuki Sakamoto    schedule 31.12.2014
comment
Този отговор е правилен, въпреки че по-добрият подход би бил изобщо да не използвате Singleton. Той не само насърчава редица опасни дизайнерски модели, но ще заобиколи няколко от вътрешните дизайнерски решения на Realm. - person jpsim; 01.01.2015
comment
Защо не публикувате своя по-добър подход без Сингълтън? Основната идея на AppState е точно същата като постоянния [NSUserDefaults standardUserDefaults] единичен модел. - person Kazuki Sakamoto; 01.01.2015
comment
Еха! Прочетох този ред в документите, но той никога не се е регистрирал в главата ми. Имах собствено предположение как работят тези обекти и се опитвах да ги използвам по този начин. Както правилно казахте, опитвам се да използвам AppState като заместител на NSUserDefaults. Може да се наложи да преразгледам тази идея. Благодаря! - person srik; 01.01.2015