Синглтон Дизайн - PullRequest
       7

Синглтон Дизайн

2 голосов
/ 22 июня 2009

Я создаю игру, в которой используются карты. У меня есть класс AppController с одним экземпляром в кончике. Экземпляр AppController имеет переменную экземпляра NSArray с именем wordList. При инициализации, экземпляр пера AppController генерирует новую GameCard. Каждая игровая карта имеет массив слов, содержащий 5 слов, выбранных случайным образом из списка в AppController. Поскольку список большой, я бы хотел прочитать его в память только один раз. Поэтому я хочу только один экземпляр AppController, как одноэлементный класс. Каждый раз, когда в AppController создается новая GameCard, он должен обращаться к тому же единственному экземпляру, чтобы получить список слов. В общем, мне нужен одноэлементный AppController, который создает GameCards, где каждая GameCard имеет ссылку на оригинальный AppController. Я не уверен, как это реализовать. Извините, если объяснение сбило с толку.

Ниже приведен пример кода, который я нашел в Интернете (http://numbergrinder.com/node/29)

+ (AppController *)instance 
{
static AppController *instance;

@synchronized(self) {
    if(!instance) {
        instance = [[AppController alloc] init];
    }
}

return instance;
}

Но когда я попытался сделать что-то с этим в экземпляре GameCard с помощью приведенного ниже кода, мое приложение запустилось навсегда, и Xcode сказал мне, что загружает 99797 кадров стека.

AppController *controller = [AppController instance];

Ответы [ 5 ]

4 голосов
/ 23 июня 2009

Звучит как бесконечный цикл. Убедитесь, что - [AppController init] не вызывает + [AppController instance].

2 голосов
/ 23 июня 2009

Зачем каждой карте нужна ссылка на контроллер приложения?

Если это просто доступ к своим словам, проще дать каждой карточке свои слова напрямую. Сделайте новый метод с именем initWithWords: назначенным инициализатором для класса GameCard. Инициализируйте каждую карту массивом из пяти слов, и у карты будет свой массив на все время жизни.

Удаление ссылок карт на контроллер приложения разрешило бы бесконечный цикл, который Том проницательно обнаружил.

Кроме того, если ни одно слово не должно появляться на двух карточках одновременно, не забудьте учесть это при рисовании из Большого массива слов контроллера приложения и при уничтожении карточек (вы можете или не хотите, чтобы слова возвращались в колоду для будущих карт).

1 голос
/ 22 июня 2009

Похоже, вы на правильном пути. Однако я никогда не пытался поместить ссылку на синглтон в файл пера. Возможно, вы захотите создать отдельный одноэлементный класс, который будет поддерживать копию данных (возможно, DataManager?), А затем вызывать его из вашего экземпляра AppController, чтобы получить слова.

Вы можете обнаружить, что размещение синглтона внутри пера (с использованием кода для синглтона в посте Стю) работает очень хорошо. Удачи!

0 голосов
/ 23 июня 2009

Обычный способ создать AppController / AppDelegate - добавить пользовательский объект NSO в ваш файл MainMenu / MainWindow.xib. Установите для класса AppController. Свяжите ссылку на делегат UIApplication / NSApplication с вашим объектом AppController. Тогда вы можете получить свой единственный AppController с помощью

(AppController*)[NSApp delegate];

или

(AppController*)[[UIApplication sharedApplication] delegate];

Вам никогда не придется создавать его с помощью alloc / init, потому что он будет создан при запуске вашего приложения. Вам не нужно беспокоиться о том, чтобы сделать его синглтоном, потому что никто никогда не будет пытаться создать другой. И вам не нужно беспокоиться о том, как получить к нему доступ, поскольку он будет делегатом объекта UIApplication / NSApplication.

Все это говорит о том, что если вам нужна глобальная переменная, содержащая массив слов, тогда забудьте о AppController и создайте новый одноэлементный объект, который содержит / читает массив. В этом случае вам просто нужно:

+ (NSArray *)sharedWordListArray 
{
    static NSArray *wordList;
    if( !wordList ) {
        wordList = [[NSMutableArray alloc] init];
        // read array
    }
    return wordList;
}

Если вам действительно нужна безопасность потоков, просто вызовите [WordList sharedWordListArray] из applicationDidFinishLaunching делегата вашего приложения: метод перед запуском каких-либо потоков или добавьте NSLock, если вы действительно хотите отложить загрузку на более позднюю, но часто лучше принять время загрузки достигло начала программы, а не неожиданно, когда пользователь предпримет какое-то более позднее действие.

0 голосов
/ 23 июня 2009

Похоже, вы вызываете ваш метод instance изнутри вашего init метода. Попробуйте что-то вроде этого:

static AppController* _instance = nil;

- (id)init
{
    // depending on your requirements, this may need locking
    if( _instance ) {
        [self release];
        return _instance;
    }
    if( (self = [super init]) ) {
        _instance = [self retain];
        // do your initialization
    }
    return self;
}

+ (AppController*)instance
{
    if( _instance ) return _instance;
    else            return [[AppController alloc] init];
}

Это гарантирует, что когда-либо будет доступен только один экземпляр AppController, а также что его можно безопасно выделить, а также получить копию с помощью метода класса instance. Это не потокобезопасно, поэтому, если к нему будут обращаться несколько потоков, вы должны добавить некоторую блокировку вокруг проверок в _instance.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...