Страх просто повільно ввійшов, коли я зрозумів, що починаю вичерпати ці фотографії

Swift + Ініціалізація із закриттями

Закриття F.T.W.

Швидка примітка - всі мої майбутні публікації будуть опубліковані на моєму спеціальному веб-сайті, і ця публікація більше не оновлюється. Дякуємо за прочитане!

Я починаю по-справжньому копати весь танець ініціалізації у Свіфті. Я про це писав. Я писав про те, чому це працює навіть так, як це робиться. Я поговорив над цим. Я читав про це (багато). Але ей, я повернувся до ще одного дії з цього питання.

З усіх безлічі красивих і різноманітних способів можна ініціалізувати щось у Swift - використання закриття зазвичай не виховується як метод, в якому це можна зробити. Але, на жаль, це може зробити кодовий код init () набагато менш болісним і трохи більш керованим.

Для програмних інтерфейсів користувача, розроблених там - це для вас !

UIKit == UIHugeSetupCode ()

Подивіться, це не вина UIKits. Компоненти, з якими користувач повинен взаємодіяти, піддаються горі коду налаштування, тому що вподобання. Зазвичай багато цього опиняється або в viewDidLoad, або loadView:

замінити функцію loadView ()
{
    нехай helloWorldLbl = UILabel ()
    helloWorldLbl.text = NSLocalizedString (“controller.topLbl.helloWorld”, коментар: “Hello World!”)
    helloWorldLbl.font = UIFont.preferredFontForTextStyle (UIFontTextStyleBody)
    helloWorldLbl.textColor = UIColor.whiteColor ()
    helloWorldLbl.textAlignment =. Центр
    self.view.addSubview (helloWorldLbl)
}

Це досить стандартно для тих, хто ризикує водами какао-дотику без .xib або .storyboard. Хоча, якщо ви поділяєтеся моєю любов'ю до мізерних методів viewDidLoad або loadView, ви можете відкласти це в іншому місці.

Скажіть, власність:

нехай helloWorldLbl: UILabel = {
    нехай lbl = UILabel ()
    lbl.text = NSLocalizedString (“controller.topLbl.helloWorld”, коментар: “Hello World!”)
    lbl.font = UIFont.preferredFontForTextStyle (UIFontTextStyleBody)
    lbl.textColor = UIColor.whiteColor ()
    lbl.textAlignment =. Центр
    повернути lbl
} ()

Досить У власній книзі Apple про Swift він зазначає, що "якщо значення за замовчуванням у вашій власності вимагає певного налаштування або налаштування, ви можете використовувати закриття або глобальну функцію, щоб надати для цього властивості налаштоване значення за замовчуванням". Як ми вже згадували, UIKit контролює вихід багато налаштування та налаштування.

Хоча один із гарних побічних продуктів - це те, як зараз виглядає loadView:

переопределити функцію loadView
{
    self.view.addSubview (self.helloWorldLbl)
}

Однак врахуйте "()" в кінці закриття в декларації про властивості. Це дає можливість маленьким майстрам Swift, що компілюють ваш код, знати, що екземпляр присвоюється типу повернення закриття. Якби ми пропустили це, можливо, ми могли б призначити власне закриття для екземпляра.

І в цьому випадку це так.

Правила - це Правила

Незважаючи на те, що у нас є нова блискуча іграшка, обов’язково потрібно пам’ятати про правила цієї землі. Оскільки ми привласнюємо властивість до закриття, решта вмісту, що містить його, можливо, ще не було ініціалізовано. Через це, коли закриття виконується - не можна посилатися на інші значення властивості або на себе зсередини.

Наприклад:

нехай helloWorldLbl: UILabel = {
    нехай lbl = UILabel ()
    lbl.text = self.someFunctionToDetermineText () // Помилка компілятора
    lbl.font = UIFont.preferredFontForTextStyle (UIFontTextStyleBody)
    lbl.textColor = self.myAppTheme.textColor () // Ще одна помилка
    lbl.textAlignment =. Центр
    повернути lbl
} ()

Екземпляр самостійно може бути ще не безпечним для використання, або він може не закінчитися двофазним ініціалізацією Swift. Те саме стосується будь-яких властивостей екземпляра, які можуть бути, а можуть і не бути виділені та ініціалізовані, оскільки закриття виконується негайно.

Це чіткий, але обґрунтований недолік використання закриття для ініціалізації. Це має повний сенс - і це правильно у відповідності з однією з трьох цілей дизайну Swift: безпека.

Знайдіть мило з колекціями

Однією з областей, в якій я вважаю цю техніку особливо корисною, є випадки, які представляють одну з безлічі різних форм колекції в Swift. З багатьох талантів Свіфта, розсікання та просіювання колекцій потужністю тисячі титанів є одним з моїх найулюбленіших.

Розглянемо наступний приклад, взятий з ініціалізатора проекту, над яким я зараз працюю. Клас, в якому знаходиться цей код, має властивість [Developer]. Під час нового запуску я встановлюю їх початкові значення з файлу .plist. Потім вони зберігаються через NSKeyedArchiver.

охороняйте, щоб devs = NSKeyedUnarchiver.unarchiveObjectWithFile (DevDataManager.ArchiveURL.path!) як? [Розробник] інше
{
    self.developers = {
        нехай pListData = // Отримати дані списку
        var devArray: [Developer] = [Developer] ()
        // Налаштування devArray з даних плістів
        повернути devArray.map {$ 0.setLocation ()}
                       .filter {$ 0.isRentable}
                       .sort {$ 0.name <$ 1.name}
     } ()
    повернення
}
self.developers = devs

Мені дуже подобається такий підхід, оскільки, хоча ми не використовуємо його поза ініціалізатором, наміри коду дуже зрозумілі, оскільки він відповідає лише за налаштування властивості.

Коли ініціалізатори та перегляди viewDidLoad стають все більшими, розділення таких речей (як мінімум) є бажаним подарунком з точки зору читабельності.

Отримання NSCute

Якщо ви справді копаєте ініціалізацію речей із закриттям, але страждаєте від гострої недостатності використання цих функціональних $ у своєму коді, підбадьоряйтесь. Використовуючи деякий адекватний Swiftery, можна створити якийсь код, який підводить тип у самому закритті, що дає певну конфігурацію професіонала. Розглянемо цей код, який я вперше натрапив на завжди інформативний NSHipster:

@warn_unused_result
public func Init  (значення: Тип, блок @noescape: (об’єкт: Тип) -> Пустота) -> Тип
{
    блок (об'єкт: значення)
    повернене значення
}

Мені подобається, куди це йде. Загальнодоступна функція, яка займає закриття введеним об'єктом за допомогою generics, яка повертає цей тип. Це означає, що ви можете розвернутись та ініціалізувати речі з додатковою інформацією про тип. Тоді наш перший зразок коду виглядатиме так:

нехай helloWorldLbl = Init (UILabel ()) {
    $ 0.text = NSLocalizedString ("controller.topLbl.helloWorld", коментар: "Hello World!")
    $ 0.font = UIFont.preferredFontForTextStyle (UIFontTextStyleBody)
    $ 0.textColor = UIColor.whiteColor ()
    $ 0.textAlignment =. Центр
}

Хоча це може здатися, це справді усуває необхідність змінної екземпляра в межах закриття, і вона позбавляється від вимоги "()". Дуже приємно .

Фінальні думки

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

Це цікавий спосіб зробити все, і є навіть кілька способів зробити те саме в нашому старому другові Objective-C. Але ей, чим більше ти знаєш, амірит?

До наступного тижня = {нехай тиждень = тиждень () тиждень.досконаленийBy (днів: 7)} ()

Джордан Морган - інженер програмного забезпечення iOS, який керує Dreaming In Binary

Якщо ви дізналися про використання закриття для ініціалізаторів, будь ласка, не соромтеся йти далі та NSRecommend (це, де: нижче);