Objective-C: использовать синглтон вместо использования класса в качестве объекта?

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

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

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

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

Есть мысли по поводу этого дизайна?

РЕДАКТИРОВАТЬ: Теперь, когда я думаю об этом, и все еще в Texture примере выше, даже если я сохраню два класса отдельно (Texture и TextureManager), я должен выбрать между A. Если менеджер будет синглтоном, и работать с ним с помощью методов экземпляра, или Б. Наличие диспетчера не содержащим экземпляров вспомогательным классом. Чтобы уточнить:

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 экземпляра, если возникнет необходимость (не в моем случае).


person Nicolas Miari    schedule 14.09.2013    source источник
comment
Я предполагаю, что компромисс в моем Texture примере - это разделение модели данных и управления ресурсами по сравнению с меньшим количеством исходных файлов.   -  person Nicolas Miari    schedule 14.09.2013


Ответы (3)


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

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

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

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

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 управлять обновлениями синглтона, например [MyGizmoClass setGizmoName:@"Gadget"].

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

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

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

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

Я уже ответил на это ...

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

tl;dr

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

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

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

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

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

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

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

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

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

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

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

person Dean Kelly    schedule 23.01.2014
comment
@NicolasMiari Спасибо! Я не уверен на 100%, как все это влияет на ARC и управление памятью, но я знаю, поскольку это должен быть одиночный -тон, не будет слишком сложно сделать свою собственную память управление при необходимости. Все зависит от варианта использования. - person Dean Kelly; 23.01.2014
comment
Я также отмечу, что, хотя синглтон является скорее анти-шаблоном, если это окажется самым простым путем, имеет смысл встроить в реализацию какую-то безопасность потоков. - person Dean Kelly; 20.12.2016

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

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

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

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

person zamfir    schedule 14.09.2013

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


Шаблон: синглтон

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

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

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

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

person Pétur Ingi Egilsson    schedule 14.09.2013
comment
Вы предлагаете использовать синглтон для основных данных и контекста управляемого объекта, а не переходить к каждому представлению? - person DogCoffee; 14.09.2013
comment
@Smick Я никогда этого не делаю, потому что хочу иметь возможность контролировать состояние NSManagedObjectContext. Возможность выполнить revert в контексте для текущего представления (например, если пользователь нажимает кнопку «Отмена» для выхода из представления после создания нескольких объектов) удобна. Я предпочитаю использовать шаблон репозитория при взаимодействии с Core Data. Таким образом, я могу управлять контекстом, а также перемещать модель в другую структуру данных (при необходимости позже) без необходимости изменять что-либо еще в приложении, тем самым уменьшая связь при увеличении сплоченности. - person Pétur Ingi Egilsson; 14.09.2013
comment
Спасибо - я обычно создаю объекты только тогда, когда они нажимают кнопку сохранения, я никогда не использовал возврат. Посмотрим на это. Спасибо. - person DogCoffee; 14.09.2013
comment
Спасибо, я знаю назначение одноэлементного шаблона. Мне просто было интересно, не является ли мое более простое решение для одного класса лучшим в некоторых случаях. То есть, насколько значимым может быть использование объекта Class вместо традиционного синглтона. - person Nicolas Miari; 14.09.2013
comment
Уже довольно поздно, но у меня вопрос. Точки, определенные @ PéturIngiEgilsson, могут быть достигнуты путем создания объекта желаемого синглтона в классе AppDelegate. Нам не нужно было бы создавать синглтон, мы можем легко получить доступ к этому объекту, используя UIApplication.shared.delegate как! AppDelegate. Мы можем инициализировать объект в didFinishLaunchingWithOptions, а затем получить к нему доступ во всем приложении. Я имею в виду, что для использования синглтона должно быть больше аргументов - person Muhammad Nayab; 08.05.2018