Мне было необходимо использовать класс NSNumberFormatter для изменения вида представления числа в моем CalculatorBrain в Задании 2 стэнфордского курса «Разработка iOS 8 приложений на Swift. Winter 2015», например, с 2324.00 на 2 324 без десятичных знаков, то есть придать универсальному NSNumberFormatter некоторую нужную мне конфигурацию. Из-за того, что инициализация NSNumberFormatter может быть экстремально дорогой операцией в iOS, способной «поставить на колени» отдельные приложения, не хотелось бы, чтобы производительность приложения зависела от этого, особенно если используется одна и та же конфигурацию NSNumberFormatter всюду в моем приложении. Будем использовать Singleton ( синглтон ) — ровно один экземпляр некоторого класса, глобально доступный всем клиентам.
Singleton (синглтон) — это популярный паттерн (шаблон ) проектирования. Если вы новичок в Swift, вам будет интересно, как можно в Swift создать Singleton. В Objective-C, возможно, вы уже пробовали для создания синглтона dispatch_once из Grand Central Dispatch.
В настоящее время существует прекрасный пост в stack overflow, в котором рассматриваются все пути создания Singleton в Swift, хотя этот пост не снабжен каким-то «глубоким» практическим примером. Класс NSNumberFormatter широко используется и пример использования Singletons в классе NSNumberFormatter будет очень полезен.
Давайте начнем создавать Singleton для NSNumberFormatter .Во-первых, нам нужно импортировать библиотеку Foundation
[objc]import Foundation
[/objc]
Затем, мы декларируем класс, называемый CalculatorFormatter, который будет наследовать от класса NSNumberFormatter.
[objc]class CalculatorFormatter: NSNumberFormatter {
}
[/objc]
Чтобы наследовать от класса NSNumberFormatter, необходимо реализовать required init функцию. Мы выполним это, просто вызвав ту же самую функцию для super класса, и затем передадим параметр aDecoder в эту функцию. Это совершенно не влияет на решение нашей проблемы, но это обязательное требование.
[objc]required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}[/objc]
Переопределяем init()
Теперь мы можем переопределить (override) стандартную init функцию, в которой мы установим некоторые значения по умолчанию при инициализации. Внутри init функции, во-первых, необходимо вызвать super.init функцию. Вот код для нужного нам форматирования числа в CalculatorBrain (у вас он может быть другим).
[objc]override init() {
super.init()
self.locale = NSLocale.currentLocale()
self.maximumFractionDigits = 10
self.notNumberSymbol = "Error"
self.groupingSeparator = " "
self.numberStyle = .DecimalStyle
}
[/objc]
Далее рассмотрим два способа создания Singleton: один для версии Swift 1.2 и выше, второй — для версии Swift ниже 1.2. Первый способ опирается на создание константы ссылочного типа, к которому относится class. Такие константы разрешены в Swift 1.2.
Singleton одной строкой в версии Swift 1.2 и выше
В Swift 1.2 или выше вам разрешено иметь в классах “static” методы и свойства (как алиасы для “class final”). Теперь вы можете декларировать хранимые static свойства (static stored properties ) в классах, которые хранятся глобально и lazy инициализируются по первому обращению (как глобальные переменные). Это так называемые “static” методы и свойства типа (это согласна терминологии Swift, а если по-старому, мы бы сказали класса). Свойства могут декларироваться как константы с помощью ключевого слова «let» и как переменные с помощью ключевого слова «var«.
Для получения Singleton нам достаточно задекларировать static свойство типа как константу (type constant property), используя ключевые слова “static let”. Назовем нашу константу sharedInstance и инициализируем ее как экземпляр нашего класса CalculatorFormatter .
[objc]static let sharedInstance = CalculatorFormatter()
[/objc]
Теперь наш Singleton готов — он был создан буквально одной строкой.
Получаем окончательный код для случая Swift 1.2 и выше, когда используется static константа типа .
[objc]import Foundation
class CalculatorFormatter: NSNumberFormatter {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init() {
super.init()
self.locale = NSLocale.currentLocale()
self.maximumFractionDigits = 10
self.notNumberSymbol = "Error"
self.groupingSeparator = " "
self.numberStyle = .DecimalStyle
}
// Swift 1.2 or above
static let sharedInstance = CalculatorFormatter()
}
[/objc]
На Playground проверим, что вызывая дважды CalculatorFormatter.sharedInstance, мы получаем один и тот же экземпляр Calcуlatorformatter. Далее вызоваем метод stringFromNumber у синглтона sharedInstance и передаем ему число. Например, 20.00. Используя оператор ??, получим представление «20» числа 20.00. Попробуем число 55550.00 и получим представление «55 550» числа 55550.00. Мы видим, что наш формат представления числа убирает нули и добавляет пробел в качестве разделителя групп цифр. Это то, что нам нужно.
Singleton в виде глобальной константы
Есть еще один путь — создание глобальной константы. Apple утверждает
Глобальные константы и переменные всегда являются отложенными (lazy) вычислениями, так же как и отложенные хранимые свойства (Lazy Stored Properties). В отличие от отложенных хранимых свойств, глобальные константы и переменные не нуждаются в маркировке lazy модификатором.
Кроме того, глобальные константы и переменные всегда используют «за сценой» ‘dispatch_once‘ . Поэтому можно использовать глобальную константу в качестве синглтона
[objc]
let formatter = CalculatorFormatter()
[/objc]
Изменения в коде CalculatorBrain в Задании 2.
Добавляем в конец файла CalculatorBrain.swift класс CalculatorFormatter.
Делаем изменения в CalculatorBrain.swift и в ViewController.swift подобно тому, как показано ниже (используется 1-ый способ создания синглтона в Swift 1.2)
Добавляем в файл CalculatorBrainTests.swift тесты для нашего Singleton, проверяющие, что это действительно один и тот же экземпляр класса CalculatorFormatter, независимо от того, в какой очереди он вызывается.
Код для CalculatorBrain вместе с Playground можно найти на GitHub.
Код для случая использования глобальной константы в качестве Singleton можно найти на GitHub.
Использовались материалы
How to Create NSNumberFormatter Singleton in Swift.
Класс! На примере NumberFormatter в CalculatorBrain разобрался как это работает!! Очень красивое решение 🙂 Спасибо!
Нашел вариант с использованием closure 🙂
class CalculatorFormatter: NumberFormatter {
static let sharedInstance: CalculatorFormatter = {
var instance = CalculatorFormatter()
// setup code
instance.locale = .current
instance.numberStyle = .decimal
instance.maximumFractionDigits = 10
instance.notANumberSymbol = «Error»
instance.groupingSeparator = » »
return instance
}()
}
https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html#//apple_ref/doc/uid/TP40014216-CH7-ID14