nsobject swift что это
Swift собственный базовый класс или NSObject
нормально ли определять класс Swift без базового класса Cocoa/NSObject? Если это так, то это означает отказ от большей части динамизма Objective-C, таких как метод перехвата и интроспекции во время выполнения.
динамическое поведение во время выполнения находится в центре таких функций, как наблюдатели свойств, основные данные,Аспектно-Ориентированное Программирование, Сообщения Более Высокого Порядка, аналитические рамки & logging и так далее.
использование стиля вызова метода Objective-C добавляет около 20 операндов машинного кода к вызову метода, поэтому в определенных ситуациях (многие жесткие вызовы методов с небольшими телами) статический стиль C++ и отправка vtable могут выполнять лучше.
но, учитывая общее правило 95-5 (95% прироста производительности происходит от настройки 5% кода), разве не имеет смысла начинать с мощных динамических функций и затвердевать там, где это необходимо?
6 ответов
Swift классы, которые являются подклассами NSObject:
Swift классы, которые не являются подклассами NSObject:
подкласс NSObject в Swift дает вам гибкость выполнения Objective-C, но также и производительность Objective-C. Избегание NSObject может повысить производительность, если вам не нужна гибкость Objective-C.
Edit:
С Xcode 6 beta 6 появляется динамический атрибут. Это позволяет мы должны проинструктировать Swift, что метод должен использовать динамическую отправку, и поэтому будет поддерживать перехват.
Я также обнаружил, что если основывать класс Swift на NSObject, я видел неожиданное поведение во время выполнения, которое могло скрыть ошибки кодирования. Вот пример.
в этом примере, где мы опираемся на NSObject компилятор не найдите ошибку в testIncorrect_CompilerShouldSpot:
Я думаю, мораль, только база на NSObject, где вы действительно должны!
согласно ссылке на язык, нет требования к классам подкласса любого стандартного корневого класса, поэтому вы можете включить или опустить суперкласс по мере необходимости.
обратите внимание, что исключение суперкласса из объявления класса не назначает неявный базовый суперкласс любого рода. Он определяет базовый класс, который фактически станет корнем для независимой иерархии классов.
Swift классы не наследовать от универсального базового класса. Классы define без указания суперкласса автоматически становится базовым классы для вас, чтобы опираться.
попытка ссылки super из класса без супер класса (т. е. базового класса) приведет к ошибке времени компиляции
в какой степени интроспекция времени выполнения будет добавлена к языку, я не знаю. Метод перехвата, вероятно, станет возможным только в том случае, если метод явно позволяет это. Это моя догадка, но только языковые дизайнеры в Apple действительно знают, куда они действительно направляются.
следующее скопировано из Swift-eBook Apple и дает соответствующий ответ на ваш вопрос:
определение базового класса
любой класс, который не наследует от другого класса, называется базовым классом.
Swift классы не наследуются от универсального базового класса. Классы, которые вы определяете без указания суперкласса, автоматически становятся базовыми классами для построения.
ссылка
Это нормально. Посмотрите на цели дизайна Swift: цель состоит в том, чтобы заставить огромные классы проблем программирования исчезнуть. Метод swizzling, вероятно, не является одной из вещей, которые вы хотите сделать с Swift.
Немного о Swift runtime или куда пропал NSObject
Привет друзья! Я из тех, кому скучно просто дергать за ниточки, торчащие из черной коробки, хочется увидеть все своими глазами, как оно работает под капотом. Мы поговорим с вами про рантайм, да-да рантайм. Для наших опытов рассмотрим старого доброго дедушку Objective C и революционного, но пока еще находящегося в стадии развития, Swift. Нам с вами понадобиться нырнуть практически на самое дно абстракций, которые заботливо придумали программисты Apple. Давайте немного разберемся, зачем вообще понадобилось разрабатывать новый язык. Я слышал много негативных отзывов в самом начале, особенно от уже матерых разработчиков Objective C. Если посмотреть повнимательнее на новый язык Swift, он на мой взгляд значительнее взрослее и серьезнее. Во-первых, он написал на языке С++ в отличии от Си, который лежит в основе Objective C. Я здесь высказываю только свои сугубо личные предубеждения, с которыми можно согласиться, можно и поспорить.
На мой взгляд С++ на текущий момент, самый серьезный язык разработки, который позволяет делать все что угодно, но уже более изящнее нежели в Си. Я думаю поэтому именно он был выбран за основу написания языка Swift, LLDB и тд. Я не буду сейчас делать обзор функциональности языка Swift, просто осветим несколько моментов, все остальное можно почитать в специализированной документации. На мой взгляд он намного функциональнее и проще для начинающего программиста, собственно это и было одной из целей его разработки, снизить порог вхождения для новых разработчиков. Вспомните как у вас вставали волосы после первого знакомства со скобочками Objective C, особенно если до этого ты писал на лаконичном C# или Java. Конечно не обошлось и без изощрений, которые придумали программисты на мой взгляд только ради того, что бы выделиться. Но это все лирика, давайте к делу. Для начала разберем базовый класс дедушки Objectice C потому сравним его со Swift.
Для тех кто знает все это, можно пока выйти покурить. Как мы знаем в Objective C любой класс или косвенно или напрямую должен наследоваться от NSObject или NSProxy. Класс NSObject реализует неформальный протокол NSObject. К нему мы еще вернемся, когда будем рассматривать SwiftObject. Забегая вперед скажу, что именно этот протокол очень поможет подружить два языка в будущем, вот она сила полиморфизма! Я предлагаю сделать так, мы по кусочками рассмотрим все методы класса NSObject. А после этого я осмелюсь сделать заключение, что не так с этим великим NSObject. Не буду долго вам мучать, погнали!
С первыми двумя методами не так часто приходиться сталкиваться рядовому разработчику Objectice C. Не буду на них останавливаться, скажу лишь пару слов. Метод load вызывается по время загрузки класса или категории в исполняющую среду, не подчиняется классическим правилам наследования, блокирует работу приложения на момент выполнения. Метод initialize вызывается в отложенном режиме, перед первым использованием класса, не блокирует работу приложения, подчиняется классическим правилам наследования.
Все последующие методы отвечают за создания и инициализацию объекта. Тоже немножечко обсудим их. Метод allocWithZone отвечает за выделение памяти под наш объект. Внутри он вызывает наш любимый malloc. Он еще с бородатых времен, когда память разделялась на зоны. Сейчас все объекты создаются в одной зоне, потому появился метод alloc который внутри себя вызывает allocWithZone и передает ему зону по умолчанию — NSDefaultMallocZone.
Методы dealloc и finalize вызывается в момент удаления объекта. Именно в этих методах происходит зачистка всех связанных ресурсов и конечном итоге free и память уходит в пул свободной памяти. Отмечу что finalize обычно не используется а dealloc вызывается в том потоке, в котором произошло последнее освобождение.
Переходим к следующей пачке методов
Ну наверно даже тому кто никогда не писал на обжектив си нетрудно догадаться, что они для копирования. С зонами все понятно, это древние методы которые уже не актуальны. На самом деле, что бы у нас что то скопировалось надо еще реализовать протокол NSCopying, а если просто вызвать эти методы, то все упадет. Но это мы еще обсудим. А пока перейдем к следующей пачке.
Вот она интроспекция, собственной персоной. Ключ к безопасному коду в обжектив си. Эти методы позволяют нам оценить объект или класс, какие протоколы он реализует, на какие селекторы может реагировать, и тд. Идем дальше
Два первых метода вызовутся до того момента как у вас все грохнется если в них вы ничего не реализуете. Это происходит тогда, когда наш объект не понимает, что вы от него хотите и вызываете метод, которого у него нет. Если вы это делаете осмысленно то будьте любезны определить у себя один из этих методов и решить, что делать с вызовом селектора, куда его послать так сказать. На самом деле это называется механизм перенаправления сообщений. Об этом тоже можно почитать, куча статей, и методов там три. Я не буду на этом останавливаться, иначе мы потеряем основную суть этой статьи.
Давайте посмотрим на последние методы
Я думаю метод хеш всем известен, используется для сравнения объектов, для распределения объектов по гнездам в коллекциях и тд. Методы description и debugDescription необходимо переопределять если вы хотите видеть в логах не просто адреса по которым располагается ваш объект, а какую то осмысленную для него информацию.
Фух, что то я уже утомился описывать эти методы. Давайте теперь повнимательнее посмотрим на этот класс, вот он стоит во главе всех классов в обжектив си, в основном. Что с ним не так? Чем он мне так не нравиться? Да все с ним не так!
На самом деле он есть, и называется он SwiftObject от которого наследуются все классы которым необходимо взаимодействовать с Objective C, написанные на свифте. Мы еще поговорим о нем. Многие наверно скажут: что несет этот парень! Наследование это же крутая вещь, переиспользование кода, что в нем плохого. Я думаю эту тему я вынесу в отдельную статью, пока поговорим о другом. Вот например зачем мне метод copy, если я не хочу ничего копировать? Тем не мене я могу его вызывать, и само собой все упадет, если я не реализую прокол NSCopying. Давайте еще поговорим про наследование. Есть метод init который я должен вызывать, если хочу проинициализовать объект, а есть метод dealloc, который вызывается сам! Ибо это метод жизненного цикла объекта и не надо вызывать его руками, никогда! Но никто ведь не мешает мне это сделать, не правда ли здорово? Да вот совсем не здорово. Выходит сам по себе класс NSObject позволяет нам делать то чего делать не надо, или знать то, о чем нам знать не обязательно. Я могу развивать тему дальше и дальше, но я думаю уже понятно, что NSObject это уродство, которого быть не должно и поэтому он пропал в языке Swift для программиста. Формально конечно базовый класс остался и только для IOS платформы, но уже для того что бы можно было подружить между собой, эти два языка: старичка Objective C и амбициозного Swift. Давайте наверно посмотрим на него одним глазком
Та дамм! Что мы видим, а видим мы что класс SwiftObject имплементирует неформальный протокол NSObject, но реализация методов уже совсем иная. Вот теперь мы знаем врага в лицо, все классы Swift которые явно не наследуются от NSObject теперь неявно наследуются от SwiftObject класса. Сразу сделаю поправку, что это имеет место только для платформы в которой необходимо взаимодействие с Objectice C. На non-Objective C платформах (Linux например) такого нет так как нет необходимости.
Как мы это узнали это уже другая история. Я думаю можно тоже немного рассказать. Как известно язык Swift лежит в открытом доступе на репозитории Apple. Никто не мешает скачать его и собрать самому из исходников, что собственно мы и сделали. Но мы пошли немножечко дальше. Всеми известный Xcode, начиная с версии 8 позволяет подсовывать свой toolchain. Чувствуете да, что это значит? Это значит что можно собрать Swift с дебаг информацией и подложить его в Xcode.
Мой коллега так и сделал, что позволило нам дебажить прямо из Xcode исходники Swift.
Мы немного отвлеклись, продолжим наши рассуждения. Уже очевидно можно сделать вывод, что метаданные которые генерировались в Objective C и которые генерируются в Swift имеют разную природу. Любой программист который долгое время писал на Objectice C и хоть немного ковырял рантайм, знает вот эту структуру
Все мы знаем, что все объекты в конечном итоге в той или иной степени выглядят в виде этой структуры. Наш любимый с вами NSObject представляет собой абстракцию, которая уберегает программиста от прямого взаимодействия с этой структурой. Подробнее про нее можно почитать, есть куча статей написанных за время существования языка, даже на русском. Давайте вернемся к нашему Swift. Теперь для хранения метаданных появился специальный класс Metadata, который достаточно объемный и представляет собой основу для всех метаданных в Swift. Более подробное описание его структуры вынесу в отдельную статью. Еще момент, несмотря на то, что все объекты Swift умеют свою структуру метаданных, они все равно генерируют еще метаданные Objective C для совместимости. То есть каждый обьект Swift имеет два набора метаданных.
Давайте немного подведем итоги. Мы разобрались что NSObject уродлив, и не место ему в новом языке. Поэтому в Swift можно создавать классы не наследуя их ни от чего, но на самом деле для совместимости, они все равно наследуются от SwiftObject. Подружить класс SwiftObject и класс NSObject позволил неформальный протокол NSObject. Который позволяет кастить объект Swift в id и передавать в Objective C. Но было бы неплохо, что бы он там работал, поэтому каждый объект Swift генерирует помимо своих метаданных, еще метаданные Objective C. Вот как то так! Всем спасибо! Здоровья и хорошего настроения!
Совместимый с «Objective-C» «Swift»-код
Хоть Apple и написали, казалось бы, подробную документацию о том, как можно использовать «Swift»-код внутри «Objective-C»-приложения (и наоборот), но, когда доходит до дела, этого почему-то окаывается недостаточно. Когда у меня впервые появилась необходимость обеспечить совместимость фреймворка, написанного полностью на «Swift», с «Objective-C»-приложением, документация «Apple» почему-то породила больше вопросов, чем дала ответов (ну или по крайней мере оставила множество пробелов). Интенсивное использование поисковых систем показало, что данная тема освещена в Сети довольно скудно: парочка вопросов на StackOverflow, пара-тройка вводных статей (на англоязычных ресурсах, конечно) – вот и все, что удалось найти.
Данная статья является обощением найденной информации, а также полученного опыта. Подчеркну, она не претендует на то, чтобы называться, как говорится, хорошей практикой, а лишь предлагает возможные действия в описанных обстоятельствах или является неким академическим экспериментом.
Последнее обновление – февраль 2019 г.
Начало
Использование «Swift»-классов в «Objective-C»-файлах
Если мы импортируем свой собственный «Swift»-код, то у нас, конечно, есть возможность в нем и «отнаследоваться» от чего угодно, и аннотацию (или атрибут) @objc добавить. Но в таком случае, наверное, у нас есть возможность и нужный код написать на «Objective-C». Поэтому больший смысл имеет сосредоточиться на случае, когда мы хотим импортировать чужой «Swift»-код в свой проект. В этом случае, скорее всего, у нас нет возможности добавить в нужные классы ни какое-либо наследование, ни прочее. Что делать в таком случае? Остается писать обертки!
Предположим, импортируемый фреймворк содержит следующий нужный нам класс:
(Доступ к классу NSObject и аннотации @objc появляется после импорта Foundation.)
По понятным причинам мы не можем использовать те же имена классов и методов в объявлениях. И здесь нам приходит на помощь аннотация @objc :
Теперь при вызове из «Objective-C»-кода названия классов и методов будут выглядеть именно так, какими мы хотели бы их видеть – как будто мы пишем соответствующие названия из внешнего класса:
Особенности использования «Swift»-методов в «Objective-C»-файлах
К сожалению, не любые (публичные) «Swift»-методы можно просто пометить @objc и использовать внутри «Objective-C». «Swift» и «Objective-C» – разные языки с разными возможностями и разной логикой, и довольно часто при написании «Swift»-кода мы пользуемся его возможностями, которыми не обладает «Objective-C» или которые реализованы фундаментально по-разному.
Например, от значений параметров по умолчанию придется отказаться. Такой метод:
…внутри «Objective-C»-кода будет выглядеть так:
( 1 – это переданное нами значение, значение по умолчанию у аргумента отсутствует.)
Названия методов
Методы, выбрасывающие исключения
Использование этого метода будет происходить в духе «Objective-C» (если можно так выразиться):
Использование «Swift»-типов в параметрах и возвращаемых значениях
Если в значениях параметров или возвращаемом значении «Swift»-функции используется не стандартный «Swift»-тип, который не переносится автоматически в среду «Objective-C», этот метод использоваться в среде «Objective-C» опять-таки не выйдет… если над ним не «поколдовать».
Использование внутри «Objective-C»:
Реализация «Swift»-протоколов «Objective-C»-классами
Для примера возьмем, конечно же, протокол, в параметрах или возвратных значениях методов которого используются «Swift»-типы, которые не могут быть использованы в «Objective-C:
Придется снова оборачивать. Для начала – SwiftClass :
Далее – самое интересное: объявим „Swift“-класс, адаптирующий нужный нам „Swift“-протокол. Он будет чем-то вроде моста между нашим протоколом, который мы написали для адаптации в „Objective-C“-проекте и „Swift“-методом, который принимает объект исходного „Swift“-протокола. В членах класса будет числиться экземпляр протокола, который мы описали. А методы класса в методах протокола будут вызывать методы написанного нами протокола:
К сожалению, без оборачивания метода, принимающего экземпляр протокола, не обойтись:
Не самая простая цепочка? Да. Хотя, если используемые классы и протоколы обладают ощутимым количеством методов, обертка уже не покажется такой непропорционально-объемной по отношению к исходному коду.
Собственно, использование протокола в самом „Objective-C“-кода будет выглядеть уже вполне гармонично. Реализация методов протокола:
И использование метода:
Перечисляемые типы в „Swift“ и „Objective-C“
Заключение
Вот, пожалуй, и все, что я хотел сообщить на данную тему. Скорее всего, есть и другие аспекты интеграции „Swift“-кода в „Objective-C“, но, уверен, с ними вполне можно справиться вооружившись описанной выше логикой.
У данного подхода, конечно, есть и свои минусы. Помимо самого очевидного (написание ощутимого количества дополнительного кода), есть еще один немаловажный: „Swift“-код переносится в среду выполнения „Objective-C“ и будет работать, скорее всего, уже не так быстро или, по-крайней мере, иначе. Хотя разница во многих случаях невооруженным взглядом заметна не будет.
Core Data + Swift для самых маленьких: необходимый минимум (часть 2)
Это вторая часть трилогии о Core Data, первая доступна здесь: Core Data + Swift для самых маленьких: необходимый минимум (часть 1).
В первой части мы познакомились с общими сведениями о Core Data, основными компонентами (NSManagedObjectModel, NSPersistentStoreCoordinator, NSManagedObjectContext), Редактором модели данных и создали нашу модель данных.
В этой части мы будем работать с объектами, познакомимся с NSEntityDescription и NSManagedObject, автогенерацией классов, а также напишем вспомогательный класс, существенно повышающий удобство работы с Core Data.
NSEntityDescription и NSManagedObject
Начнем с NSEntityDescription — как можно догадаться из названия, это объект, который содержит описание нашей сущности. Все то, что мы нафантазировали с сущностью в Редакторе модели данных (атрибуты, взаимосвязи, правила удаления и прочее), содержится в этом объекте. Единственное, что мы будем делать с ним — получать его и передавать куда-то в качестве параметра, больше ничего.
NSManagedObject — это сам управляемый объект, экземпляр сущности. Продолжая аналогию с СУБД (начатую в прошлой статье), можно сказать, что NSManagedObject — запись (строка) в таблице базы данных.
Чтобы понять, как с этим работать, давайте создадим нового Заказчика. Так как у нас еще нет готовой интерфейсной части (мы займемся этим в следующей статье), то давайте немного попрограммируем прямо в модуле делегата приложения ( AppDelegate.swift ). Не беспокойтесь, это только демонстрация, которая важна для понимания, чуть позже мы все перенесем отсюда в другое место. Я воспользуюсь для демонстрации работы следующей функцией:
Создание управляемого объекта (в данном случае Заказчика) выполняется следующим образом:
Сначала мы получаем описание сущности (entityDescription), передав в соответствующий конструктор строку с именем нужной нам сущности и ссылку на контекст. Как это работает: контекст управляемого объекта, как мы помним из первой части, связан с координатором постоянного хранилища, а координатор, в свою очередь, связан с объектной моделью данных, где и будет произведен поиск сущности по указанному имени. Обратите внимание, что данная функция возвращает опциональное значение.
Затем, на основании полученного описания сущности, мы создаем сам управляемый объект (managedObject). Вторым параметром мы передаем контекст, в котором этот объект должен быть создан (в общем случае, как вы помните, может быть несколько контекстов).
Хорошо, мы создали объект, как теперь установить значения его атрибутов? Для это используется кодирование по типу Key-Value, суть которого в том, что есть два универсальных метода, один который устанавливает указанное значение по указанному имени, а второй извлекает значение по указанному имени. Звучит гораздо сложнее, чем выглядит.
Как видите, все довольно просто. Идем дальше. Теперь надо сохранить этот объект в нашей базе данных. Разве то, что мы создали объект — недостаточно? Нет, любой объект «живет» в конкретном определенном контексте и только там. Вы можете его там создавать, модифицировать и даже удалять, но это все будет происходить внутри определенного контекста. До тех пор, пока вы явно не сохраните все изменения контекста, вы не измените реальных данных. Можно провести аналогию с файлом на диске, который вы открываете для редактирования — пока вы не нажали кнопку «Сохранить» никакие изменения не записаны. На самом деле это очень удобно и здорово оптимизирует весь процесс работы с данными.
Сохранение изменений контекста выполняется элементарно:
У нас даже есть в модуле делегата готовая функция для более «умного» сохранения (мы говорили о ней вскользь в прошлой статье), запись происходит только в том случае, если данные действительно изменены:
Таким образом, весь код создания и записи объекта будет выглядеть следующим образом:
Мы создали объект и записали его в нашу базу данных. Как нам теперь его получить обратно? Это не намного сложнее. Давайте взглянем на код.
Здесь мы создаем объект-запрос NSFetchRequest, передав в конструктор в качестве параметра название сущности данные которой мы хотим получить. Затем вызываем метод контекста, передав этот запрос в качестве параметра. Это максимально простой вариант извлечение записей, вообще NSFetchRequest очень гибок и предоставляет обширные возможности извлечение данных по определенным условиям. Пример фильтрации и сортировки данных с его помощью мы рассмотрим в следующей части статьи.
Важное замечание: функция managedObjectContext.executeFetchRequest всегда возвращает массив объектов, даже если объект всего один — возвращен будет массив, если объектов нет вообще — пустой массив.
С учетом вышесказанного, у нас будет следующий текст функции:
Как только вы получили объект, в вышеприведенном листинге это переменная result внутри цикла, то вы можете его произвольным образом редактировать (здесь нет никаких отличий от установки атрибутов для нового объекта), либо удалить. Удаление осуществляется вызовом соответствующего метода переменной контекста, которому в качестве параметра и передается удаляемый объект:
После удаления также необходимо принудительно вызвать сохранение контекста, не забывайте об этом.
Если вы хотите «пощупать» Core Data поближе, на уровне таблиц, то это проще чем может показаться. Если вы используете Симулятор, то файл базы данных лежит где-то здесь:
Не торопитесь искать этот файл вручную, гадая какой-же ID у вашего приложения. Есть замечательная утилита, делающая это все за вас — SimSim (Пользуясь случаем, хочу сказать спасибо авторам).
После запуска она висит в строке меню и выглядит вот так (значок летучей мышки):
Собственно, назначение очевидно: утилита показывает список хранилищ установленных на симуляторе приложений и позволяет сразу к ним перейти:
Для просмотра самого файла SQLite можно воспользоваться любым бесплатным просмоторщиком, например Datum Free
Автогенерация классов Core Data
Метод Key-Value, хорош тем, что он прост, универсален и работает «из коробки». Но есть два момента, которые портят впечатление: во-первых, кода больше, чем хотелось бы, а во-вторых, передавая имя реквизита каждый раз в виде строки, легко ошибиться (автодополнения здесь нет). И как нам быть, если захочется немного больше функциональности от управляемых объектов, например, вычисляемые поля или свои конструкторы? У Core Data есть решение! Мы легко можем создать свой класс (даже больше — Core Data сделает это за нас), унаследовав его от NSManagedObject и дополнив всем необходимым. В результате, мы сможем работать с управляемым объектов как с обычным объектом ООП, создавая его путем вызова своего конструктора и обращаясь к его полям «через точку» используя автодополнение (то есть вся мощь ООП в ваших руках).
Откройте Редактор модели данных и выделите любую сущность. Выберете в меню (оно контекстно-зависимое, поэтому надо выделить какую-нибудь сущность) Editor \ Create NSManagedObject Subclass…
Откроется окно выбора модели данных; да, в общем случае, может быть несколько независимых моделей данных, но у нас она одна, поэтому выбор очевиден.
В следующем окне нам предлагают выбрать сущности, для которых необходимо сгенерировать классы, давайте выберем сразу все.
Следующее стандартное окно вам должно быть знакомо, единственное что здесь может насторожить, это опция «Use scalar properties for primitive data types». В чем смысл этой опции: если эту опцию не выбирать, то вместо примитивных типов данных (Float, Double, Int и прочее) будет использоваться своеобразная «обертка», содержащая значение внутри себя. Это скорее актуально для Objective-C, так как там нет такого понятия как Optional. Но мы используем Swift, так что я не вижу причин не выбирать эту опцию (возможно, более опытные коллеги в комментариях меня поправят).
В итоге, Core Data создаст для нас несколько файлов, давайте посмотрим, что это за файлы.
Хорошо, мы создали классы для наших сущностей, теперь мы можем обращаться к полям класса «через точку», давайте придадим нашему примеру более привычный вид.
Так гораздо лучше. Но создание объекта выглядит несколько тяжеловесно. Можно было бы спрятать все это в конструктор, но для этого нам нужна ссылка на управляемый контекст в котором должен быть создан объект. Кстати, мы до сих пор пишем код в модуле делегата, так как именно здесь у нас определен Core Data Stack. Может можно придумать что-нибудь получше?
Core Data Manager
Наиболее распространенной практикой при работе с Core Data является использование паттерна Singleton на базе Core Data Stack. Напомню, если кто не знает или забыл, что Singleton гарантирует наличие только одного экземпляра класса с глобальной точкой доступа. То есть, у класса всегда существует один и только один объект, независимо от того, кто, когда и откуда к нему обращается. Этот подход мы сейчас и реализуем, у нас будет Singleton для глобального доступа и управления Core Data Stack.
Создайте новый пустой файл с именем CoreDataManager.swift
Для начала давайте добавим директиву импорта Core Data и создадим сам Singleton.
Теперь давайте переместим из модуля делегата приложения все функции и определения, связанные с Core Data.
Теперь у нас есть Singleton и мы можем обращаться к Core Data Stack из любой точки нашего приложения. Например, обращение к управляемому контексту будет выглядеть так:
Давайте теперь перенесем все необходимое для создания управляемого объекта в его конструктор.
Снова вернемся в модуль делегата приложения и внесем несколько изменений. Во-первых, создание управляемого объекта у нас упрощается до одной строки (вызов нового конструктора нашего класса), а во-вторых, такую ссылку на управляемый контекст
надо заменить следующей
Теперь код будет выглядеть совсем знакомым, а работа с управляемыми объектами будет мало отличаться от обычных объектов ООП.
Теперь вернемся в модуль Customer.swift и изменим код следующий образом.
Вот теперь точно все, дублирование кода сведено к минимуму. Давайте создадим аналогичные конструкторы для остальных сущностей. Я приведу только один для примера, это просто и не должно вызвать никаких затруднений (абсолютно все то же самое, за исключением имени сущности).
Вместо заключения
Обратите внимание, что CoreDataManager, который мы создали, довольно универсален, в том смысле, что его можно использовать в любом приложении, основанном на Core Data. Единственное, что его связывает именно с нашим проектом, — это имя файла модели данных. Больше ничего. То есть, написав этот модуль один раз, вы можете им пользоваться постоянно в разных проектах.