Правильно ли задание синглтон-паттерна C (iOS)?

Здесь много хороших ответов, но я хотел бы отметить, что их можно очень просто расширить, чтобы добиться более сложной сортировки. Единственное, что вам нужно сделать, это использовать оператор OR для цепочки сравнения, например:

objs.sort((a,b)=> fn1(a,b) || fn2(a,b) || fn3(a,b) )

Где fn1, fn2, ... являются функциями сортировки, которые возвращают [- 1,0,1]. Это приводит к «сортировке по fn1», «сортировке по fn2», которая в значительной степени равна ORDER BY в SQL.

Это решение основано на поведении оператора ||, который оценивает значение первое оцениваемое выражение, которое может быть преобразовано в true .

Простейшая форма имеет только одну встроенную функцию:

// ORDER BY last_nom
objs.sort((a,b)=> a.last_nom.localeCompare(b.last_nom) )

Имея два шага с last_nom , порядок сортировки first_nom будет выглядеть так:

// ORDER_BY last_nom, first_nom
objs.sort((a,b)=> a.last_nom.localeCompare(b.last_nom) || 
                  a.first_nom.localeCompare(b.first_nom)  )

Общая функция сравнения может быть примерно такой:

// ORDER BY 
let cmp = (a,b,n)=>a[n].localeCompare(b[n])

Эта функция может быть расширена для поддержки числовых полей , чувствительность к регистру, произвольные типы данных и т. д.

Вы можете использовать его с привязкой к ним по приоритету сортировки:

// ORDER_BY last_nom, first_nom
objs.sort((a,b)=> cmp(a,b, "last_nom") || cmp(a,b, "first_nom") )
// ORDER_BY last_nom, first_nom DESC
objs.sort((a,b)=> cmp(a,b, "last_nom") || -cmp(a,b, "first_nom") )
// ORDER_BY last_nom DESC, first_nom DESC
objs.sort((a,b)=> -cmp(a,b, "last_nom") || -cmp(a,b, "first_nom") )

. Дело в том, что чистый JavaScript с функциональным подходом может принять вас долгий путь без внешних библиотек или сложного кода. Это также очень эффективно, так как не нужно выполнять синтаксический анализ строк

29
задан vikingosegundo 2 January 2015 в 18:40
поделиться

3 ответа

Будьте проще:

+(instancetype)sharedInstance
{
    static dispatch_once_t pred;
    static id sharedInstance = nil;
    dispatch_once(&pred, ^{
        sharedInstance = [[self alloc] init];
    });
    return sharedInstance;
}

- (void)dealloc
{
    // implement -dealloc & remove abort() when refactoring for
    // non-singleton use.
    abort();
}

Вот и все. Переопределение retain, release, retainCount и всего остального просто скрывает ошибки и добавляет кучу ненужного кода. Каждая строка кода - это ошибка, ожидающая своего появления. В действительности, если вы вызываете dealloc для вашего общего экземпляра, у вас есть очень серьезная ошибка в вашем приложении . Эта ошибка должна быть исправлена, а не скрыта.

Этот подход также поддается рефакторингу для поддержки не-одиночных режимов использования. Практически каждый синглтон, выживший после нескольких выпусков, в конечном итоге будет преобразован в не-синглтонную форму. Некоторые (например, NSFileManager) продолжают поддерживать одноэлементный режим, а также поддерживают произвольное создание экземпляров.

Обратите внимание, что вышесказанное также «просто работает» в ARC.

82
ответ дан gaussblurinc 2 January 2015 в 18:40
поделиться

Если вы хотите провести модульное тестирование вашего синглтона, вы также должны сделать его так, чтобы вы могли заменить его на имитированный синглтон и / или сбросить его до нормального:

@implementation ArticleManager

static ArticleManager *_sharedInstance = nil;
static dispatch_once_t once_token = 0;

+(ArticleManager *)sharedInstance {
    dispatch_once(&once_token, ^{
        if (_sharedInstance == nil) {
            _sharedInstance = [[ArticleManager alloc] init];
        }
    });
    return _sharedInstance;
}

+(void)setSharedInstance:(ArticleManager *)instance {
    once_token = 0; // resets the once_token so dispatch_once will run again
    _sharedInstance = instance;
}

@end
1
ответ дан ToddH 2 January 2015 в 18:40
поделиться
// See Mike Ash "Care and Feeding of Singletons"
// See Cocoa Samurai "Singletons: You're doing them wrong"
+(MySingleton *)singleton {
    static dispatch_once_t pred;
    static MySingleton *shared = nil;
    dispatch_once(&pred, ^{
        shared = [[MySingleton alloc] init];
        shared.someIvar = @"blah";
    });
    return shared;
}

Имейте в виду, что dispatch_once не является повторно входящим , поэтому вызов себя изнутри блока dispatch_once приведет к блокировке программы.

Не пытайтесь защищаться от самого себя. Если вы не программируете фреймворк, относитесь к своему классу как к нормальному, а затем придерживайтесь единой идиомы выше. Думайте об идиоме синглтона как об удобном методе, а не как об определяющей черте вашего класса. Вы хотите относиться к своему классу как к нормальному классу во время модульного тестирования, поэтому можно оставить доступный конструктор.

Не утруждайте себя использованием allocWithZone:

  • Он игнорирует свои аргументы и ведет себя точно так же, как alloc. Зоны памяти больше не используются в Objective-C, поэтому allocWithZone: сохраняется только для совместимости со старым кодом.
  • Это не работает. Вы не можете применить одноэлементное поведение в Objective-C, потому что всегда можно создать больше экземпляров, используя NSAllocateObject() и class_createInstance().

Метод фабрики-одиночки всегда возвращает один из этих трех типов:

  • id, чтобы указать, что возвращаемый тип не полностью известен (случай, когда вы строите класс кластер).
  • instancetype чтобы указать, что возвращаемый тип является экземпляром включающего класса.
  • Само имя класса (MySingleton в примере), чтобы упростить его.

Поскольку вы пометили эту iOS, альтернативой одиночному файлу является сохранение ivar делегату приложения, а затем использование вспомогательного макроса, который вы можете переопределить, если передумаете:

#define coreDataManager() \
        ((AppDelegate*)[[UIApplication sharedApplication] delegate]).coreDataManager
19
ответ дан Community 2 January 2015 в 18:40
поделиться