Objective-C: Использовать класс singleton vs. use как объект?

Мне было интересно, в каких случаях действительно необходимо принять одноэлементный шаблон в objective-C (например, определить выделенный класс и создать один экземпляр), что использование класса как объекта не будет выполнено.

В частности, я думаю о следующем решении:

  • Определить и использовать соответствующие методы класса вместо методов экземпляров для экземпляра singleton;
  • Используйте переменные static (глобальные глобальные расширения), вместо переменных экземпляра экземпляра singleton;
  • Используйте объект класса при регистрации в качестве наблюдателя для уведомлений вместо экземпляра singleton. Хотя объект класса является объектом objective-C сам по себе (правильно?), Для этого требуется, чтобы обработчик уведомлений был методом класса; (возможно ли это?)

Например, вместо того, чтобы иметь объект Texture (модельный объект) и a TextureManager singleton (диспетчер ресурсов), вы могли бы создать или создать текстуру как методы класса и статические переменные того же класса Texture (шаблон factory плюс некоторое управление ресурсами).

Любые мысли об этом дизайне?

EDIT: Теперь, когда я думаю об этом и все еще в приведенном выше примере Texture, даже если я удерживаю два класса отдельно (Texture и TextureManager), я должен выбрать между A. Имея диспетчерский синглтон и управляйте им с использованием методов экземпляра или B. Наличие менеджера неинтересным вспомогательным классом. Чтобы уточнить:

Texture* myTexture = [[TextureManager defaultManager] textureWithName:@"TextureName"]; 
// (singleton, client uses instance methods)

против

Texture* myTexture = [TextureManager textureWithName:@"TextureName"]; 
// (Class standing in for singleton, client uses class methods)

Последний выглядит более простым и менее громоздким/многословным, но мне интересно, какой дизайн является "более правильным". Конечно, первый позволяет более чем одному экземпляру TextureManager возникнуть необходимость (не в моем случае).

Ответ 1

Я думал об одном и том же, и я думаю, что у меня есть ответ для вас.

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

Прочитайте, если вы хотите узнать, как я пришел к моему заключению, или прокрутите вниз до раздела tl; dr.

Как вы сказали, казалось бы, (внешне) менее громоздким для доступа к singleton, чтобы класс управлял синглом для вас. По существу, вы сделали бы это, заменив метод singleton factory на метод инициализации. Глядя на документацию Apple, вы можете увидеть, где они показывают "общий" метод, который действует как factory для создания синглтона по требованию.

static MyGizmoClass *sharedGizmoManager = nil;

+ (MyGizmoClass*)sharedManager
{
    if (sharedGizmoManager == nil) {
        sharedGizmoManager = [[super allocWithZone:NULL] init];
    }
    return sharedGizmoManager;
}

Вместо этого вы можете заменить метод инициализатором void следующим образом:

+ (void)initializeMyGizmo
{
    if (sharedGizmoManager == nil) {
        sharedGizmoManager = [[super allocWithZone:NULL] init];
    }
    // whatever else needs to be done to the singleton to initialize it
}

а затем ТОЛЬКО когда-либо использовать методы класса и разрешать MyGizmoClass управлять обновлениями в singleton, например [MyGizmoClass setGizmoName:@"Gadget"].

ПРИМЕЧАНИЕ.. В этом случае было бы странным, если кто-то посмотрит на файл .h, чтобы увидеть свойства, и в этом случае они могут прийти к выводу, что они должны создать экземпляр объекта самостоятельно или иметь доступ к синглтону в той или иной форме или моде. Таким образом, , если вам нужно было пройти путь инкапсуляции доступа к singleton, было бы нецелесообразно использовать общедоступные переменные.

К этому моменту:

Если вы ограничиваете доступ только к самому классу, вы теряете любые геттеры и сеттеры или другие свободные вещи, которые приходят со свойствами. Это означает, что если MyGizmoClass должен был иметь как часть модели model NSString *gizmoName, вам пришлось бы создавать пользовательские геттеры и сеттеры для этого "свойства" и сохранять его либо как ivar, либо свойство в расширении интерфейса в .m file (т.е. private) одноэлементного класса или как смежная статическая переменная.

Итак, это задает вопрос (и именно это заставило меня задуматься в первую очередь), если мы вообще включим строку static MyGizmoClass *sharedGizmoManager = nil; вообще или можем вообще расшифровать внутреннее расширение интерфейса и заменить любые возможные ивары или свойства, которые мы хотите ограничить доступ с помощью реализаций static в реализации?

Я ответил, что уже...

Это зависит от того, что вам нужно сделать с ним.

TL;DR

Первый сценарий

Если вы когда-либо (даже малейший шанс) должны подклассифицировать свою TextureManager или может создать несколько экземпляров (сделать это больше не синглет), было бы лучше придерживаться регулярного Apple соглашение для singleton.

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

В этом случае вы будете использовать свойства по мере необходимости (публично или в частном порядке), а также иваров. Вы также можете использовать смесь иваров и статики, но вы все равно должны иметь статический экземпляр ваш TextureManager внутри реализации TextureManager.

Второй сценарий

Если вам ТОЛЬКО понадобится ОДИН экземпляр TextureManager, и он будет работать полностью автономно без смешения дальше по линии, тогда вы можете полностью удалить статический экземпляр вашего класса в реализации в файле .m и замените ivars и свойства статическими переменными в рамках этой реализации.

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

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

Другие интересные вещи

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

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

Ответ 2

Да, вы можете определенно сделать класс Texture без необходимости сингла.

Синглеты, вероятно, не должны создаваться и использоваться как объект.

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

Я обычно использую одиночные игры для навигации уровня в игре со многими уровнями (например, Angry Birds).
По уровню навигации я имею в виду... когда игрок завершает определенный уровень в игре, я просто вызываю метод класса на синглете и передаю номер уровня, тогда метод singleton class определяет, какой уровень следующий (если пользователь нажимает кнопка "Следующий уровень" ).

Ответ 3

Я могу помочь вам лучше понять класс Singleton и когда он применяется.


Шаблон: Singleton

Намерение. Обеспечьте, чтобы класс мог иметь только один экземпляр, а также сделать этот экземпляр доступным для любого другого объекта.

Мотивация. Иногда нам нужно убедиться, что в нашей проблемной области существует только один объект определенного типа. Пример. У студента есть только один рюкзак, который он может заполнить книгами. Мы не хотели бы относить его к второму рюкзаку, еще больше книг.

Использовать, когда:

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