что такое синглтон swift

Синглтон (Перевод с английского главы «Singleton» из книги «Pro Objective-C Design Patterns for iOS» Carlo Chung)

Класс синглтона в объектно-ориентированном приложении всегда возвращает один и тот же экземпляр самого себя. Он обеспечивает глобальную точку доступа для ресурсов, которые предоставляет объект класса. Паттерн с такой функциональностью называется Синглтон.
В этой главе мы изучим возможности реализации и использования паттерна Синглтон в Objective-C и фреймворке Cocoa Touch на iOS.

Что из себя представляет паттерн Синглтон?

Паттерн Синглтон – едва ли не самый простой из паттернов. Его назначение в том, чтобы сделать объект класса единственным экземпляром в системе. В первую очередь нужно запретить создавать более одного экземпляра класса. Для этого можно использовать фабричный метод (глава 4), который должен быть статическим, так как не имеет смысла разрешать экземпляру класса создавать другой единственный экземпляр. Рисунок 7-1 показывает структуру класса простого синглтона.

что такое синглтон swift. Смотреть фото что такое синглтон swift. Смотреть картинку что такое синглтон swift. Картинка про что такое синглтон swift. Фото что такое синглтон swift
Рисунок 7-1. Статическая структура паттерна Синглтон.

Примечание. Паттерн Синглтон: Проверяет, что есть только один экземпляр класса и обеспечивает единую точку доступа к нему.*
*Исходное определение, представленное в «Design Patterns» GoF (Addison-Wesley,
1994).

Когда можно использовать паттерн Синглтон?

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

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

Существует также гибкая версия паттерна Синглтон, в которой фабричный метод всегда возвращает один и тот же экземпляр, но можно в дополнение аллоцировать и инициализировать другие экземпляры. Эта менее строгая версия паттерна обсуждается в разделе «Использование класса NSFileManager» далее в этой главе.

Реализация Синглтона в Objective-C

Есть кое-что, над чем стоит подумать, чтобы спроектировать класс Синглтона правильно. Первый вопрос, которым нужно задаться, — это как удостовериться, что только один экземпляр класса может быть создан? Клиенты в приложении, написанном на других объектно-ориентированных языках, таких, как C++ и Java, не могут создать объект класса, если его конструктор объявлен закрытым. А как обстоят дела в Objective-C?

Любой Objective-C метод является открытым, и язык сам по себе динамически типизированный, поэтому любой класс может послать сообщение другому (вызов метода в C++ и Java) без значительных проверок во время компиляции (только предупреждения компилятора, если метод не объявлен). Также фреймворк Cocoa (включая Cocoa Touch) использует управление памятью методом подсчета ссылок для поддержания времени жизни объекта в памяти. Все эти особенности делают реализацию Синглтона в Objective-C довольно непростой.
В оригинальном примере книги «Паттерны проектирования» пример Синглтона на C++ выглядел, как показано в листинге 7-1.

Листинг 7-1. Исходный пример на C++ паттерна Синглтон из книги «Паттерны проектирования».

Как описано в книге, реализация в C++ проста и прямолинейна. В статическом методе Instance() статическая переменная _instance проверяется на 0 ( NULL ). Если так, то создается новый объект класса Singleton и затем возвращается. Кто-то из вас может подумать, что Objective-C версия не сильно отличается от своего собрата и должна выглядеть, как в листингах 7-2 и 7-3.

Листинг 7–2. Объявление класса Singleton в Singleton.h

Листинг 7–3. Реализация метода sharedInstance Singleton.m

Листинг 7–4. Более подходящая реализация Singleton в Objective-C

Наследование от Синглтона

Если использовать трюк с NSAllocateObject для создания экземпляра, то он станет таким:

Теперь неважно, инстанцируем ли мы класс Singleton или какой-то из его подклассов, эта версия сделает все корректно.

Потокобезопасность
Использование Синглтонов во фреймворке Cocoa Touch
Использование класса UIApplication
Использование класса UIAccelerometer
Использование класса NSFileManager

Если нужно реализовать «строгий» синглтон, необходима реализация, похожая на пример, описанный в предыдущих разделах. Иначе – не переопределяйте allocWithZone: и другие связанные методы.

Источник

Паттерны проектирования, взгляд iOS разработчика. Часть 0. Синглтон-Одиночка

Я почув і забув.
Я записав і запам’ятав.
Я зробив і зрозумів.
Я навчив іншого, тепер я майстер.
(В. В. Бублик)

что такое синглтон swift. Смотреть фото что такое синглтон swift. Смотреть картинку что такое синглтон swift. Картинка про что такое синглтон swift. Фото что такое синглтон swift

Небольшое вступление.

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

И именно эти слова и сподвигли меня на написание данной серии постов. Дело в том, что я — начинающий iOS разработчик, и я очень хочу разобраться в паттернах проектирования. И я не придумал лучшего способа, чем взять книгу «Паттерны проектирования» Эрика и Элизабет Фримен, и написать примеры каждого паттерна на Objective-C и Swift. Таким образом я смогу лучше понять суть каждого паттерна, а также особенности обоих языков.

Содержание:

Итак, начнем с самого простого на мой взгляд паттерна.

Одиночка, он же — синглтон.

Итак, разберемся что такое синглтон в Objective-C и Swift на примерах из книги.

Давайте сначала узнаем как вообще создать объект какого-нибудь класса. Очень просто:

И тут авторы подводят нас к мысли, что приведенным выше способом можно создать сколько угодно объектов этого класса. Таким образом, первое что нужно сделать на пути к синглтону — запретить создание объектов нашего класса извне. В этом нам поможет приватный инициализатор.

И если в swift это реализуется тривиально:

Таким образом попытка создать объект нашего класса извне вызовет ошибку на этапе компиляции. Окей. Теперь и в objective-c у нас есть запрет на создание объектов нашего класса. Правда это еще не совсем приватный инициализатор, но мы к этому вернемся через пару секунд.

Итак, по сути мы получили класс, объекты которого не могут создаваться, потому что конструктор — приватный. И что со всем этим делать? Будем создавать объект нашего класса внутри нашего же класса. И будем использовать для этого статический метод (метод класса, а не объекта):

И если для swift опять все просто и понятно, то с objective-c возникает проблема с инициализацией:

что такое синглтон swift. Смотреть фото что такое синглтон swift. Смотреть картинку что такое синглтон swift. Картинка про что такое синглтон swift. Фото что такое синглтон swift

(да, и не забудьте вынести метод + (instancetype)sharedInstance в файл заголовка, он должен быть публичным)

Теперь все компилируется и мы можем получать объекты нашего класса таким способом:

Наш синглтон почти готов. Осталось только исправить статический метод так, чтобы объект создавался только один раз:

Как видите, для этого нам понадобилась статическая переменная, в которой и будет храниться единожды созданный объект нашего класса. Каждый раз при вызове нашего статического метода она проверяется на nil и, если объект уже создан и записан в эту переменную — он не создается заново. Наш синглтон готов, ура! 🙂

Теперь немного примеров из жизни из книги.

Итак, у нас есть шоколадная фабрика и для приготовления мы используем высокотехнологичный нагреватель шоколада с молоком (я просто обожаю молочный шоколад), который будет управляться нашим программным кодом:

Как видите — нагреватель сначала заполняется смесью ( fill ), затем доводит ее до кипения ( boil ), и после — передает ее на изготовление молочных шоколадок ( drain ). Для избежания проблем нам нужно быть уверенными, что в нашей программе присутствует только один экземпляр нашего класса, который управляет нашим нагревателем, поэтому внесем изменения в программный код:

Итак, все отлично. Мы на 100% уверены (точно на 100%?), что у нас есть только один объект нашего класса и никаких непредвиденных ситуаций на фабрике не произойдет. И если наш код на objective-c выглядит довольно неплохо, то swift выглядит недостаточно swifty. Попробуем его немного переписать:

А как же многопоточность?

Все будет работать хорошо ровно до того момента, как мы захотим применить в нашей программе работу с потоками. Как же сделать наш синглтон потокобезопасным?

И опять же: в swift, как оказывается, совершенно не нужно выполнять каких-либо дополнительных действий. Константа уже потокобезопасна, ведь значение в нее может быть записано только один раз и это сделает тот поток, который доберется до нее первым.

А вот в objective-c необходимо внести коррективы в наш статический метод:

Блок внутри dispatch_once гарантированно выполнится только один раз, когда самый первый поток до него доберется, все остальные потоки — будут ждать, когда закончится выполнение блока.

Итоги подведем.

Итак, мы разобрались как правильно писать синглтоны на objective-c и swift. Приведу вам итоговый код класса Singleton на обоих языках:

Хочу попросить всех читателей и комментаторов: если вы увидели какую-нибудь неточность или знаете как какой-либо из приведенных мною кусков кода написать правильнее/красивее/корректнее — скажите об этом. Я пишу здесь совсем не для того, чтоб учить других, а для того, чтоб научиться самому. Ведь пока мы учимся — мы остаемся молодыми.

Источник

Синглтон, локатор сервисов и тесты в iOS

Привет, Хабр! Я Богдан, работаю в мобильной команде Badoo iOS-разработчиком.

В этой статье мы рассмотрим использование паттернов «Синглтон» и «Локатор сервисов» (service locator) в iOS и обсудим, почему их часто называют антипаттернами. Я расскажу, как и где их стоит применять, сохраняя код пригодным для тестирования.

что такое синглтон swift. Смотреть фото что такое синглтон swift. Смотреть картинку что такое синглтон swift. Картинка про что такое синглтон swift. Фото что такое синглтон swift

Синглтон

Синглтон — это класс, единовременно имеющий только один экземпляр.

Cинглтон довольно просто реализуется в Swift:

let managerA = SomeManager.shared // правильно
let managerB = SomeManager() // неправильно, ошибка компиляции

В то же время UIKit не всегда последователен в отношении своих синглтонов, например, UIDevice() создаёт для вас новый экземпляр класса, который содержит информацию о том же устройстве (довольно бессмысленно), в то время как UIApplication() выбрасывает исключение в runtime во время исполнения.

Пример ленивой (отложенной) инициализации синглтона:

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

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

Как понять, что сущность должна быть синглтоном

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

К примеру, если мы создаём автопилот для конкретного автомобиля, то эта машина является синглтоном, поскольку не может существовать более одного конкретного автомобиля. С другой стороны, если мы делаем приложение для фабрики автомобилей, тогда объект «Автомобиль» не может быть синглтоном, поскольку на фабрике множество автомобилей, и все из них релевантны нашему приложению.

В дополнение к этому стоит задать себе вопрос: «А есть ли такая ситуация, в которой приложение может существовать без этого объекта?»

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

Другой пример — приложение, требующее сущность «Пользователь». Пускай даже приложение бесполезно до тех пор, пока вы в нём не залогинились, оно всё равно работает, даже если вы не ввели свои данные. Значит, пользователь является синглтоном с ограниченным временем жизни. Для получения более подробной информации почитайте эту статью.

Злоупотребление синглтонами

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

Считайте это расширение UIImageView контрпримером:

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

imageView.downloadImage(from: url)
print(String(describing: imageView.image)) // изображение уже установлено или нет?

Синглтоны и модульное тестирование

Если вы проведёте модульное тестирование приведённого выше расширения, то поймёте, что ваш код делает сетевой запрос и что вы никак не можете на это повлиять!

Первое, что приходит на ум, — ввести вспомогательные методы в NetworkManager и вызвать их в setUp()/tearDown() :

Но это очень плохая идея, поскольку вам придётся писать production-код, пригодный лишь для поддержки тестов. Более того, вы можете случайно использовать эти методы и в самом production-коде.

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

Оптимальным решением, на мой взгляд, будет накрыть Network Service протоколом и внедрить его как явную зависимость.

Это позволит нам использовать поддельную реализацию (mock implementation) и проводить модульное тестирование. А ещё мы сможем использовать разные реализации и с лёгкостью переключаться между ними. Пошаговое руководство: medium.com/flawless-app-stories/the-complete-guide-to-network-unit-testing-in-swift-db8b3ee2c327

Сервис

Сервис — это автономный объект, ответственный за выполнение одной бизнес-активности, который может обладать другими сервисами в качестве зависимостей.

Также сервис — это отличный способ обеспечить независимость бизнес-логики от UI-элементов (экраны/ UIViewControllers).

Хорошим примером является UserService (или Repository), содержащий ссылку на текущего уникального пользователя (в конкретный период времени может существовать только один экземпляр) и одновременно на других пользователей системы. Сервис — прекрасный кандидат на роль источника истины для вашего приложения.

Сервисы — отличный способ отделить экраны друг от друга. Допустим, у вас есть сущность «Пользователь». Вы можете вручную передавать её как параметр на следующий экран, и, если на следующем экране пользователь меняется, вы получаете его в виде обратной связи:

что такое синглтон swift. Смотреть фото что такое синглтон swift. Смотреть картинку что такое синглтон swift. Картинка про что такое синглтон swift. Фото что такое синглтон swift

В качестве альтернативы экраны могут изменять текущего пользователя в UserService и прослушивать изменения пользователя из сервиса:

что такое синглтон swift. Смотреть фото что такое синглтон swift. Смотреть картинку что такое синглтон swift. Картинка про что такое синглтон swift. Фото что такое синглтон swift

Локатор сервисов

Локатор сервисов — это объект, удерживающий и обеспечивающий доступ к сервисам.

Его реализация может выглядеть так:

Это может показаться заманчивой заменой внедрения зависимости, поскольку вам не приходится явно передавать зависимость:

Получаете доступ к сервису через локатор служб:

И вы всё ещё можете заменять предоставленные сервисы для тестирования:

Но на деле, если применять локатор сервисов таким образом, это может принести вам больше бед, чем пользы. Проблема в том, что за пределами сервиса user вы не можете понять, какие сервисы применяются в данный момент времени, то есть зависимости неявны. А теперь представьте, что класс, который вы написали, является публичным компонентом фреймворка. Как пользователь фреймворка поймёт, что ему следует зарегистрировать сервис?

Злоупотребление локатором служб

Если у вас запущены тысячи тестов и внезапно они начинают сбоить, то можно не сразу понять, что у тестируемой системы есть сервис со скрытой зависимостью.

Более того, когда вы добавляете или удаляете из объекта зависимость от сервиса (или глубокие зависимости), в ваших тестах не появляется ошибка компиляции, из-за которой пришлось бы обновить тест. Ваш тест даже начать сбоить может не сразу, оставаясь какое-то время «зелёным», и это худший сценарий, так как в конечном счёте тесты начинают сбоить после некоторых «несвязанных» изменений в сервисе.

Запуск неудачных тестов по отдельности приведёт к разным результатам из-за плохой изоляции, вызванной общим локатором служб.

Локатор сервисов и модульное тестирование

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

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

В модульном тесте мы не станем использовать локатор служб. Вместо этого будем постоянно передавать наши mock-объекты:

Есть ли способ всё улучшить?

Что, если мы решим не использовать статические переменные для синглтонов в нашем собственном коде? Это позволит сделать код надёжнее. И если мы запретим это выражение:

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

Все фабрики и роутеры/ контроллеры потоков будут иметь хотя бы одну зависимость, если им понадобится какой-либо сервис:

Таким образом мы получим код, пожалуй, менее удобный, но гораздо более безопасный. К примеру, он не даст нам обращаться к фабрике из слоя View, поскольку локатор сервисов попросту недоступен оттуда, и действие будет перенаправлено на роутер / контроллер потока.

Источник

Синглтон на Swift, или как правильно написать Singleton

Для написания Синглтона (Singleton) на языке Swift можно использовать несколько подходов. Теоретически — все будут работать, однако надо бы до конца разобраться какой же вариант является правильным с точки зрения работы и написания кода.

Для этого давайте разберем что делает Синглтон Синглтоном.

Я не буду вдаваться в подробности зачем вообще нужен Singleton и как его использовать, — это не является темой для этой статьи. А вот синтаксис написания — мы разберем подробно, от исторического определения на Objective-C до современного Swift 2.1

Есть по существу три параметра или свойства (кому как удобно), которыми должен обладать Singleton:

Начнем издалека: давайте вспомним как инициализация Singleton выглядела при написании на языке программирования Objective-C

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

Как было указано ранее, — этот вариант — это портирование кода из начала статьи.

2) Второй вариант — это запись с использованием статичной структуры.

Тут надо учитывать, что Swift 1.0, не поддерживал статические переменные класса. Однако, в структурах их можно было использовать! Из-за этих ограничений на статические переменные, программисты были вынуждены использовать модели подобные этой. Это лучше, чем прямой Objective-C порт, но все еще не достаточно хорошо.

3) Третий вариант — установка глобальной переменной

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

Сейчас на этом месте, внимательный читатель может спросить, почему мы не использовали dispatch_once в наших реализациях структур или глобальных переменных? Ответ на этот вопрос нам дает Swift Блог Apple:

The lazy initializer for a global variable (also for static members of structs and enums) is run the first time that global is accessed, and is launched as `dispatch_once` to make sure that the initialization is atomic. This enables a cool way to use `dispatch_once` in your code: just declare a global variable with an initializer and mark it private.

То есть Apple утверждает, что если создать глобальную переменную и ее инициализатор сделать private, то за кулисами языка инициализация оборачивается в блок dispatch_once при доступе в глобальное пространство.

Финальный рабочий вариант

Ну и в конце концов мы подошли к четвертому варианту определения Singleton, и это будет вариант описания одной строкой!

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

Да! Язык Swift предоставляет весьма элегантные решения для тривиальных и не очень задач. Именно поэтому программисты которые решили освоить программирование iOS и Mac выбирают именно это язык программирования.

Источник

Шаблоны программирования на Swift: И снова Singleton

что такое синглтон swift. Смотреть фото что такое синглтон swift. Смотреть картинку что такое синглтон swift. Картинка про что такое синглтон swift. Фото что такое синглтон swift

Продолжаем изучать шаблоны программирования на Swift. Сегодня мы поговорим о шаблоне Singleton. Мы уже не раз рассматривали его на страницах даже данного сайта, например в статье Singleton на Swift: как правильно писать синглтон. Более того, каждый из Вас, кто пробовал писать программы на iOS, использовал этот шаблон. И сейчас Вы в этом убедитесь.

Singleton

Для начала посмотрим, что нам скажет книга:

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

Собственно вот и все:) iOS сам за нас позаботится о том, чтобы создан был только один экземпляр нашего объекта. Тут стоит сделать шаг назад и описать проблему, которая является головной болью любого кто более — менее близко работал с потоками:

Представьте что есть 2 потока. И тут каждый, одновременно создает singleton. Вроде бы и должен создаться только один объект, но потому что все происходит в один момент –бывают случаи когда создается два объекта. Или еще более сложная ситуация: объекта singleton не существует. Два потока хотят его создать одновременно: Поток 1 делает проверку на существование объекта. Видит что его нет, и проходит этап проверки. 2.Поток 2 делает проверку на существование объекта, и хоть и поток 1 проверку УЖЕ прошел, но объект ЕЩЕ не существует. Для решения таких проблем dispatch_once делает блокирование кода, для других потоков, пока он исполняется в каком–либо потоке. Потому, ни один поток не может зайти в этот код, пока он занят.

Вывод лога в консоль:

Думаю, понятно, что при создании второго указателя twoST, он сослался на тот же экземпляр что и oneST, и поэтому изменения свойств через один указатель повлияют и на другой.

Итак, шаблон Singleton гарантирует, что только один объект определенного класса будет когда-либо создан. Все последующие ссылки на объекты этого класса указывают на тот же экзеvпляр.

Инициализатор private используется, чтобы невозможно было создать еще один объект.

А теперь давайте вспомним, где Вы уже применяли это шаблон программирования.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *