Разработка IOS: важные концепции и вопросы интервью

Каждый разработчик IOS должен знать основы IOS, чтобы понимать общее поведение приложения. Вот некоторые заметки, которые я сделал, чтобы помочь разработчикам IOS (и, конечно же, себе!) чувствовать себя комфортно с важными понятиями, включая «Вопросы для интервью». Я собрал и рассмотрел лучшие объяснения концепций Swift и Objective C для разработчиков IOS 🙂

. . .

Atomic и Nonatomic свойства

Во-первых, в Objective-C используются Atomic и Nonatomic свойства, в Swift им нет места (говорят!). Swift по умолчанию определяет свойства как Nonatomic, однако по умолчанию для Objective-C они являются Atomic.

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

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

«Атомарный и неатомарный относится к тому, будут ли установщики/геттеры для свойства атомарно считывать и записывать значения в свойство. “

Атомарное свойство является потокобезопасным в геттере/сеттере.

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

. . .

Core data

Core Data — это платформа, которую вы используете для управления объектами уровня модели в своем приложении. Он предоставляет обобщенные и автоматизированные решения общих задач, связанных с жизненным циклом объекта и управлением графом объектов, включая хранение (документация на сайте developer.apple.com).

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

. . .

Let против Var

Let — неизменяемая переменная, то есть ее нельзя изменить.

Var — изменяемая переменная, то есть ее можно изменить.

. . .

ARC

ARC (автоматический подсчет ссылок): отвечает за удаление объектов из памяти после того, как (задание выполнено) его счетчик ссылок равен нулю.

ARC отслеживает систему управления памятью языка Swift.

Я думал, что не смогу объяснить ARC на примере лучше, чем Пол Хадсон 🙂

Когда вы создаете объект из класса, Swift запоминает, что на этот экземпляр ссылаются ровно один раз. Если вы затем укажете другую переменную на этот объект, Swift увеличит счетчик ссылок до 2, потому что две переменные указывают на один и тот же объект. Если теперь вы уничтожите первую переменную (возможно, она была внутри функции, и эта функция закончилась), Swift уменьшит счетчик ссылок до 1. Все это имеет значение, потому что пока счетчик ссылок больше 1, объект остается живым. Но когда последняя переменная, ссылающаяся на этот объект, исчезает, Swift обнуляет счетчик ссылок. Поскольку никакие существующие переменные не указывают на объект, его оперативная память может быть освобождена. Итак, ARC — это способ эффективного отслеживания времени жизни объекта, и по большей части вы даже не замечаете, как это происходит — Swift делает всю работу за вас. (Пол Хадсон)

Итак, каковы функции ARC?

ARC выделяет часть памяти для хранения информации каждый раз, когда init() создает новый экземпляр класса.

Информация о типе экземпляра и его значениях хранится в памяти.

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

ARC отслеживает текущие свойства, константы и переменные экземпляров классов, так что deinit() применяется только к тем неиспользуемым экземплярам.

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

. . .

Strong Reference Type

Swift по умолчанию использует строгий ссылочный тип.

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

class IOSDeveloper {
let burcu = Developer() //strong reference to child.
}
class Developer {}

. . .

Weak Reference Type

Слабая ссылка — это просто указатель на объект. Он не защищает объект, его можно освободить с помощью ARC!

Сильная ссылка увеличивает количество сохранений объекта на 1, а слабая ссылка — нет!

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

БОНУС:

В Swift все слабые ссылки являются неконстантными опционами (подумайте о var и let), потому что ссылка может и будет изменена на nil, когда на нее больше не будет сильной ссылки. Так что никогда не забывайте: структуры и перечисления нельзя присваивать слабым переменным.

. . .

Утечка памяти и цикл сохранения

Утечки памяти — это блоки выделенной памяти, на которые программа больше не ссылается.

Пространство памяти заполняется страницами с недоступными данными и вызывает дополнительную активность подкачки при возникновении утечек.

Утечка памяти в конечном итоге вынуждает систему выделять дополнительные страницы виртуальной памяти для приложения, выделения которых можно было бы избежать, восстанавливая утечку памяти. (документация developer.apple.com)

На https://medium.com/@vinodhswamy/strong-cycle-retain-cycle-in-swift-f452f07518b2 есть отличное объяснение ARC, утечки памяти и цикла сохранения.

Для получения дополнительной информации: https://krakendev.io/blog/weak-and-unowned-references-in-swift.

. . .

Жизненный цикл приложения IOS и состояния приложения

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

источник изображения от hackernoon

Различные состояния приложения iOS:

  • Не работающее состояние: когда приложение не запущено или работало, но было остановлено системой.
  • Неактивное состояние: когда приложение работает на переднем плане, но в данный момент не получает событий. Приложение остается в этом состоянии ненадолго, пока переходит в другое состояние. Единственный раз, когда он остается неактивным, это когда пользователь блокирует экран или система предлагает пользователю отреагировать на какое-либо событие, такое как телефонный звонок или SMS-сообщение.
  • Активное состояние: когда приложение работает на переднем плане и получает события. Это нормальный режим для приложений переднего плана.
  • Фоновое состояние: когда приложение находится в фоновом режиме и выполняет код. Большинство приложений ненадолго переходят в это состояние на пути к приостановке. Однако приложение, запрашивающее дополнительное время выполнения, может некоторое время оставаться в этом состоянии. Кроме того, приложение, запущенное непосредственно в фоновом режиме, переходит в это состояние вместо неактивного состояния.
  • Приостановленное состояние: приостановленное приложение остается в памяти, но не выполняет никакого кода. При нехватке памяти система может удалить приостановленные приложения без предварительного уведомления, чтобы освободить место для приложения переднего плана.

Когда бы вы сказали, что приложение не находится в рабочем состоянии?

Говорят, что приложение находится в состоянии «не работает» в следующих случаях:

  • Когда он не запущен.
  • Когда он прерывается системой во время работы.

Когда говорят, что приложение находится в активном состоянии?

Говорят, что приложение находится в активном состоянии, когда оно работает на переднем плане и получает события.

Какие изменения состояния приложения происходят при его запуске?

Говорят, что приложение находится в нерабочем состоянии до его запуска.

После кратковременного перехода через неактивное состояние он переходит в активное или фоновое состояние при запуске.

Какое состояние ненадолго достигает приложение перед приостановкой? Приложение ненадолго переходит в фоновое состояние перед приостановкой.

. . .

Реагирует на изменение состояния в приложении

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

Например:

  • Метод applicationDidBecomeActive(): для подготовки к запуску в качестве приложения переднего плана.
  • Метод applicationDidEnterBackground(): для выполнения некоторого кода, когда приложение работает в фоновом режиме, которое может быть приостановлено в любое время.
  • Метод applicationWillEnterForeground(): для выполнения некоторого кода, когда приложение выходит из фона.
  • Метод applicationWillTerminate(): вызывается при завершении работы приложения.

. . .

Cocoa vs Cocoa Touch

https://www.edureka.co/blog/interview-questions/ios-interview-questions/

. . .

Поддержка JSON для iOS

  • IOS поддерживает структуру SBJson. SBJson — это парсер и генератор JSON для Objective-C. Он предоставляет гибкие API и дополнительный контроль, упрощая обработку JSON.
  • Для разработчиков IOS легко использовать класс JSONSerialization платформы Foundation для преобразования JSON в типы данных Swift, такие как Dictionary, Array, String, Number и Bool. Однако, поскольку вы не можете быть уверены в структуре или значениях JSON, которые получает ваше приложение, может быть сложно правильно десериализовать объекты модели. Для чистого кода вы можете использовать протокол Codable 🙂

Извлечение значений из JSON: метод класса JSONSerialization jsonObject(with:options:) возвращает значение типа Any и выдает ошибку, если данные не могут быть проанализированы.

Для получения дополнительной информации посетите Apple Developer 🙂

. . .

Контекст управляемого объекта и его функции

Контекст управляемого объекта (представленный экземпляром NSManagedObjectContext) — это временный «блокнот» в приложении для (предположительно) связанной коллекции объектов. Эти объекты в совокупности представляют собой внутренне согласованное представление одного или нескольких постоянных хранилищ.

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

К ключевым функциям контекста управляемого объекта относятся следующие:

  • Управление жизненным циклом: здесь контекст обеспечивает проверку, обработку обратных отношений и отмену/повтор.
  • Уведомления: это относится к уведомлениям контекстных сообщений в различных точках, которые можно дополнительно отслеживать в другом месте нашего приложения.
  • Параллелизм: здесь Core Data использует ограничение потока (или сериализованной очереди) для защиты управляемых объектов и контекстов управляемых объектов.

. . .

Параллелизм с GCD и операциями

Параллелизм — это «выполнение более одной задачи одновременно». Параллелизм довольно часто используется на устройствах iOS, поэтому вы можете выполнять задачи в фоновом режиме (например, загрузку или обработку данных), в то время как ваш пользовательский интерфейс остается отзывчивым.

Три способа достижения параллелизма в iOS:

Потоки, очереди отправки, очереди операций

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

Поэтому OS X и iOS предпочитают использовать асинхронный подход к решению проблемы параллелизма, а не полагаться на потоки.

Одной из технологий асинхронного запуска задач является Grand Central Dispatch (GCD), которая переводит управление потоками на системный уровень. Все, что нужно сделать разработчику, — это определить задачи, которые необходимо выполнить, и добавить их в соответствующую очередь отправки. GCD заботится о создании необходимых потоков и планировании задач для выполнения в этих потоках.

Все очереди отправки являются структурами данных «первым поступил — первым обслужен» (FIFO), поэтому задачи всегда запускаются в том же порядке, в котором они добавляются.

Очередь операций является эквивалентом Cocoa параллельной очереди диспетчеризации и реализуется классом NSOperationQueue. В отличие от очередей отправки, очереди операций не ограничиваются выполнением задач в порядке FIFO и поддерживают создание сложных графов порядка выполнения для ваших задач.

GCD (или Grand Central Dispatch) и Operations (ранее называвшиеся NSOperation) — это API-интерфейсы, которые вы используете в iOS для управления параллельными задачами (в отличие от прямой работы с потоками). Одной из технологий асинхронного запуска задач является Grand Central Dispatch (GCD), которая переводит управление потоками на системный уровень. Все, что нужно сделать разработчику, — это определить задачи, которые необходимо выполнить, и добавить их в соответствующую очередь отправки. GCD заботится о создании необходимых потоков и планировании задач для выполнения в этих потоках.

Поэтому OS X и iOS предпочитают использовать асинхронный подход к решению проблемы параллелизма, а не полагаться на потоки.

. . .

Что такое автореализационный пул?

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

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

Короче говоря, autoreleasepool по-прежнему полезен при разработке iOS/Swift, поскольку в UIKit и Foundation все еще есть устаревшие классы Obj-C, которые вызывают autorelease, но вам, вероятно, не нужно беспокоиться об этом при работе с классами Swift из-за ARC. оптимизация.

. . .

Разница между «Assign» и «Retain»

Assign создает ссылку от одного объекта к другому без увеличения счетчика сохранения источника.

Retain создает ссылку от одного объекта к другому и увеличивает количество сохранений исходного объекта.

Retain и Assign являются типами квалификаторов памяти, но не влияют на то, что представляет собой базовый объект. Он увеличивает счетчик сохранения объекта на 1 и становится владельцем объекта.

Assign — это ключевое слово, используемое для примитивов. Это довольно легко понять: если у вас нет объекта, вы не можете использовать strong, потому что strong сообщает компилятору, как работать с указателями. Но если у вас есть примитив (например, int, float, bool или что-то еще без маленькой звездочки после типа), то вы используете assign, и это заставляет его работать с примитивами.

. . .

Что такое объекты слоя?

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

. . .

AppID VS BundleID

  • Идентификатор приложения — это строка из двух частей, используемая для идентификации одного или нескольких приложений от одной группы разработчиков. Строка состоит из строк поиска Team ID и Bundle ID, разделенных точкой (.).
  • Идентификатор группы предоставляется Apple и уникален для конкретной группы разработчиков, а идентификатор пакета предоставляется разработчиком для соответствия либо идентификатору пакета отдельного приложения, либо набору идентификаторов пакета группы приложений.

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

Идентификатор пакета однозначно определяет каждое приложение. Это указано в Xcode. Один проект Xcode может иметь несколько целей и, следовательно, выводить несколько приложений. Распространенный вариант использования: приложение, имеющее как облегченную/бесплатную, так и профессиональную/полную версии, или брендированное несколькими способами.

. . .

Какая связь между iVar и @property?

iVar — это переменная экземпляра. К нему нельзя получить доступ, если мы не создадим аксессоры, которые генерируются @property. iVar и его аналог @property могут иметь разные имена.

@interface Box : NSObject{
    NSString *boxName;
}
@property (strong) NSString *boxDescription;//this will become another ivar
-(void)aMethod;
@end
@implementation Box
@synthesize boxDescription=boxName;//now boxDescription is accessor for name
-(void)aMethod {
    NSLog(@"name=%@", boxName);
     NSLog(@"boxDescription=%@",self.boxDescription);
    NSLog(@"boxDescription=%@",boxDescription); //throw an error
}
@end

NSURLConnection и свойства

Объект NSURLConnection позволяет загружать содержимое URL-адреса, предоставляя объект запроса URL-адреса. Интерфейс для NSURLConnection является разреженным, предоставляя только элементы управления для запуска и отмены асинхронной загрузки запроса URL. Вы выполняете большую часть своей настройки на самом объекте запроса URL.

Существует два способа использования класса NSURLConnection: асинхронный и синхронный.

Асинхронное соединение создаст новый поток и выполнит процесс загрузки в новом потоке.

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

БОНУС: многие разработчики думают, что синхронное соединение блокирует только основной поток, что не соответствует действительности. Синхронное соединение всегда будет блокировать поток, из которого оно запущено. Если мы запустим синхронное соединение из основного потока, основной поток будет заблокирован. Однако, если мы запускаем синхронное соединение из потока, отличного от основного потока, оно будет похоже на асинхронное соединение и не будет блокировать наш основной поток.

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

Например, чтобы создать асинхронное соединение, нам нужно:

Иметь наш URL в экземпляре NSString

Преобразуйте нашу строку в экземпляр NSURL

Поместите наш URL-адрес в запрос URL-адреса типа NSURLRequest или, в случае изменяемых URL-адресов, в экземпляр NSMutableURLRequest.

Создайте экземпляр NSURLConnection и передайте ему URL-запрос.

. . .

Что такое абстрактный класс в Cocoa?

Cocoa не предоставляет ничего, называемого абстрактным. Он может создать абстрактный класс, который проверяется только во время выполнения, но не проверяется во время компиляции.

@interface AbstractClass : NSObject
@end
@implementation AbstractClass
+ (id)alloc{
    if (self == [AbstractClass class]) {
        NSLog(@"Abstract Class can’t be used");
    }
    return [super alloc];
@end

. . .

willFinishLaunchingWithOptions() и didFinishLaunchingWithOptions()

Оба метода присутствуют в файле AppDelegate.swift. Они используются для добавления функциональности в приложение, когда произойдет запуск приложения.

application:willFinishLaunchingWithOptions — этот метод дает вашему приложению первую возможность выполнить код во время запуска.

application:didFinishLaunchingWithOptions — этот метод позволяет выполнить окончательную инициализацию до того, как ваше приложение будет показано пользователю.

. . .

Преимущества и недостатки Swift

Важные функции Swift: статическая типизация, протоколы, ссылочные типы, типы значений, необязательные параметры, дженерики.

Так что насчет статической типизации?

Это позволяет протоколам, дженерикам и дополнительным параметрам существовать и помогает (с подсказками) при кодировании.

Показывать предупреждения во время компиляции и ошибки компиляции.

Обеспечивает безопасность во время выполнения и детерминированность (необязательные параметры, var/let, общие или конкретные типы и т. д.).

В то же время статическая типизация может быть слишком ограничивающей, когда вы создаете что-то, что требует большей гибкости, например, создание библиотеки или фреймворка. Objective-C может быть лучшим выбором в этом отношении, поскольку он обеспечивает лучшие возможности метапрограммирования, чем Swift. (Хотя, возможно, на данный момент Objective-C устаревает, и вся разработка для платформ Apple должна выполняться в Swift)

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

. . .

Каковы параметры рендеринга для JSONSerialization?

  1. MutableContainers: массивы и словари создаются как объекты переменных, а не констант.
  2. MutableLeaves: конечные строки в графе объектов JSON создаются как экземпляры переменных строк.
  3. allowFragments: синтаксический анализатор должен разрешать объекты верхнего уровня, которые не являются экземплярами массивов или словарей.

. . .

Что такое iOS-приложение и как в него вписывается ваш код?

Типичное iOS-приложение — это просто блок кода и, конечно же, огромный цикл выполнения. Он ожидает ввода данных пользователем и прерывается внешними сигналами, такими как телефонные звонки, push-уведомления, жесты/нажатия кнопок и другие события жизненного цикла приложения. Единственное отличие состоит в том, что это не просто функция основного цикла, которая запускается каждый раз, когда пользователь нажимает на значок вашего приложения, а более высокий уровень абстракции, UIApplication, AppDelegate и SceneDelegate, с которыми работают разработчики.

БОНУС: остальная часть кода, который вы пишете для реализации бизнес-логики вашего приложения, размещается где-то в «точках срабатывания», делегированных этим основным циклом нашему приложению через AppDelegate или SceneDelegate. До iOS 13 AppDelegate отвечал за получение всех внешних событий для вашего приложения и запуск пользовательского интерфейса, но, начиная с iOS 13, вся логика, связанная с пользовательским интерфейсом, была перемещена в SceneDelegate.

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

. . .

Что такое MVC?

MVC расшифровывается как Model View Controller. Это шаблон проектирования программного обеспечения, который Apple выбрала в качестве основного подхода к разработке приложений для iOS. Модели представляют данные приложения; представления рисуют предметы на экране; контроллеры управляют потоком данных между моделью и представлением. Модель и представление никогда не взаимодействуют друг с другом напрямую и полагаются на контроллер для координации связи.

Это вызывает проблему «Massive View Controller»!! Поэтому вам следует искать принципы архитектурного проектирования для получения дополнительной информации (MVVM, MVP, VIPER, VIP, CleanSwift и т. д.).

Типичное представление каждого уровня Apple MVC в приложении iOS будет таким:

  • Подклассы UIView (Cocoa Touch или пользовательские) являются представлениями
  • UIViewControllers и их подклассы являются контроллерами
  • любые объекты данных, подклассы NSManagedObject и тому подобное являются моделями

. . .

А как же синглтоны?

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

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

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

Если вы работали с фреймворками Apple, то, скорее всего, вы уже использовали шаблон singleton. Они, наверное, кажутся знакомыми.

// Shared URL Session
let sharedURLSession = URLSession.shared
// Default File Manager
let defaultFileManager = FileManager.default
// Standard User Defaults
let standardUserDefaults = UserDefaults.standard
// Default Payment Queue
let defaultPaymentQueue = SKPaymentQueue.default()

Но у одноэлементного шаблона есть побочный эффект, который часто является истинной причиной принятия одноэлементного шаблона — глобальный доступ. Но глобальный доступ к одноэлементному объекту — не более чем побочный эффект одноэлементного шаблона.

К сожалению, многие разработчики используют шаблон singleton, чтобы иметь легкий доступ к объекту singleton из любой точки своего проекта. Очередь платежей по умолчанию доступна через метод класса default(). Это означает, что любой объект в проекте может получить доступ к очереди платежей по умолчанию. Хотя это удобно, это удобство имеет свою цену.

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

. . .

Как насчет Делегат и KVO?

Оба являются способами установления отношений между объектами.

Делегирование — это отношение «один к одному», когда один объект реализует протокол делегирования; другой отправляет ему сообщения, используя методы, определенные протоколом.

KVO — это отношение «многие ко многим», когда один объект передает сообщение, а один или несколько других объектов слушают его и реагируют на него. KVO не полагается на протоколы. (NSNotification и NSNotificationCenter)

Key-Value Observing — это возможность для Swift прикреплять код к переменным, так что всякий раз, когда переменная изменяется, код запускается. Это похоже на наблюдатели свойств (willSet и didSet), за исключением того, что KVO предназначен для добавления наблюдателей вне определения типа.

KVO не очень хорош в чистом коде Swift, потому что он полагается на среду выполнения Objective-C — вам нужно использовать классы @objc, которые унаследованы от NSObject, а затем пометить каждое из ваших свойств динамическим @objc.

. . .

А как насчет принципов SOLID?

SOLID расшифровывается как принцип единой ответственности, принцип открытого/закрытого, принцип замещения Лисков, принцип разделения интерфейса и принцип инверсии зависимостей. Эти принципы дополняют и поддерживают друг друга и являются одним из лучших общих подходов к проектированию, которые вы можете использовать для своего кода. Пройдемся по каждому из них.

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

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

Принцип подстановки Лискова (LSP) гласит, что объекты в программе должны заменяться экземплярами их подтипов без изменения корректности этой программы. Это означает, что когда вы наследуете от класса или абстрактного класса или реализуете интерфейс (протокол), ваши объекты должны быть заменяемыми и внедряемыми везде, где используется этот интерфейс или класс, из которого вы создали подкласс. Этот принцип часто называют проектированием по контракту или, как в последнее время в сообществе Swift, называют программированием, ориентированным на протокол. Основная идея этого принципа заключается в том, что вы не должны нарушать контракт, который ваши интерфейсы, подклассы которых вы обещаете выполнить, и что путем создания подклассов эти подклассы могут использоваться везде, где ранее использовался суперкласс.

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

Принцип инверсии зависимостей (DIP) гласит: «зависит от абстракций, а не от конкретики». Лучшим примером, демонстрирующим этот принцип, является метод внедрения зависимостей (DI). С помощью метода внедрения зависимостей, когда вы создаете объект, вы предоставляете и внедряете все его зависимости при его инициализации или конфигурации, а не позволяете объекту создавать или извлекать/находить свои зависимости для себя.

. . .

Какие у вас есть варианты реализации хранилища и хранилище на iOS?

Массивы в памяти, словари, наборы и другие структуры данных идеально подходят для промежуточного хранения данных или если их не нужно сохранять.

NSUserDefaults/Keychain — это простые хранилища ключей и значений. Один небезопасный, а другой безопасный, соответственно.

Хранилище файлов/дисков — это способ записи данных (сериализованных или нет) на/с диска с помощью NSFileManager.

Core Data и Realm — это фреймворки, упрощающие работу с базами данных.

SQLite — это реляционная база данных, и она хороша, когда вам нужно реализовать сложную механику запросов, а Core Data или Realm не подходят.

. . .

Какие параметры доступны для сети и HTTP на iOS?

В IOS есть несколько вариантов реализации сети HTTP. Вы можете использовать старый добрый NSURLSession, но если вы не абстрагируете его достаточно хорошо, работать с ним может быть сложно. Другим вариантом было бы использовать вокруг него библиотеку-оболочку. Самым популярным решением на iOS является Alamofire.

В наши дни NSURLSession и Codable являются двумя основными технологиями, используемыми для работы в сети на iOS, но также полезно знать о решениях с открытым исходным кодом, таких как Alamofire.

Для большего:

  • NSURLSession (developer.apple.com)
  • Кодируемый (developer.apple.com)
  • Аламофайр (github.com)
  • Передача представительного состояния (REST)

. . .

Как и когда сериализовать и сопоставлять данные на iOS?

Существует два наиболее распространенных сценария, в которых вам потребуется сериализовать и сопоставить данные в приложениях iOS:

Получение или отправка данных на сетевом уровне (например, JSON или XML или что-то еще)

Сохранение или получение моделей на уровне хранилища (NSData, NSManagedObject).

Каждый раз, когда вы получаете ответ JSON, XML или любой другой тип ответа от внутреннего API, вы, скорее всего, получаете его в JSON, двоичном или другом «неудобном» формате.

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

Следующим шагом является сопоставление этих данных с моделями предметной области вашего приложения. Это будут объекты модели или структуры для использования остальной частью вашего приложения. Вы можете сделать это вручную или использовать протокол Codable, предоставленный Apple, или использовать библиотеку, такую ​​​​как Mantle или SwiftyJSON. Поток данных и сериализация/отображение: двоичные данные -> json -> NSDictionary/NSArray -> объекты вашей модели домена. Давай, используй Codable!

Точно так же на уровне хранилища вам нужно будет сериализовать и сопоставлять данные с объектами пользовательской модели предметной области и из них в формат, понятный вашему хранилищу. Цепочка «отображения» для чтения данных: db -> формат необработанных данных -> модели пользовательских доменов; и для записи: модели пользовательских доменов -> формат необработанных данных -> db. Для этого вы должны использовать протокол NSManagedObject или NSCoding или Codable.

. . .

Оптимизация производительности прокрутки таблиц с динамическими размерами или коллекций

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

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

Возможными решениями проблем с производительностью прокрутки могут быть:

рассчитать высоту ячейки самостоятельно

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

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

. . .

Управление зависимостями

Вы можете выбрать менеджеры зависимостей из: CocoaPods, Carthage и Swift Package Manager.

Для большего:

  • cocoapods.org (cocoapods.org)
  • Картеж (github.com/Cartage)
  • Менеджер пакетов Swift (swift.org)

. . .

Отладка и профилирование кода в XCode

В приложениях iOS всегда есть NSLogging и print. Есть точки останова, которые вы можете установить с помощью Xcode. Для производительности отдельных фрагментов кода вы можете использовать MeasureBlock XCTest. Это здорово 🙂 Пожалуйста, посмотрите видеоролики wwdc для получения подробной информации об отладке в XCode.

. . .

А плист?

Список свойств или plist относится к списку, который организует данные в именованные значения и списки значений с использованием нескольких типов объектов. Эти типы предоставляют нам средства для создания данных, которые являются осмысленно структурированными, переносимыми, хранимыми и доступными, но при этом максимально эффективными. Списки свойств часто используются приложениями, работающими как в Mac OS X, так и в iOS. Интерфейсы программирования списка свойств для Cocoa и Core Foundation позволяют нам преобразовывать иерархически структурированные комбинации этих основных типов объектов в стандартный XML и из него. Мы можем сохранить XML-данные на диск и позже использовать их для реконструкции исходных объектов.

Система пользовательских значений по умолчанию, к которой мы программно обращаемся через класс NSUserDefaults, использует списки свойств для хранения объектов, представляющих предпочтения пользователя. Это ограничение, по-видимому, исключает многие типы объектов, такие как объекты NSColor и NSFont, из системы пользовательских значений по умолчанию. Однако, если объекты соответствуют протоколу NSCoding, их можно заархивировать в объекты NSData, которые являются объектами, совместимыми со списком свойств.

. . .

Какова цель повторного использования идентификатора? В чем преимущество установки его в ненулевое значение?

ReuseIdentifier используется для группировки похожих строк в UITableView, т. е. строк, которые отличаются только своим содержимым, но в остальном имеют схожие макеты. UITableView обычно выделяет достаточно объектов UITableViewCell для отображения содержимого, видимого в таблице.

БОНУС: если для параметра reuseIdentifier установлено значение, отличное от нуля, то UITableView сначала попытается повторно использовать уже выделенную UITableViewCell с тем же reuseIdentifier при прокрутке табличного представления. Если reuseIdentifier не был установлен, то UITableView будет вынужден выделять новые объекты UITableViewCell для каждого нового элемента, который прокручивается в поле зрения, что может привести к задержке анимации.

. . .

Мелкие вопросы 🙂

Какая структура используется для создания пользовательского интерфейса приложения для iOS?

Фреймворк UIKit используется для разработки пользовательского интерфейса приложения для iOS. Он обеспечивает обработку событий, модель чертежа, окна, представления и элементы управления, специально разработанные для интерфейса с сенсорным экраном.

В каком потоке приложения следует использовать классы UIKit?

Классы UIKit следует использовать только из основного потока приложения.

Какой API вы бы использовали для написания тестовых сценариев для проверки элементов пользовательского интерфейса приложения?

API автоматизации пользовательского интерфейса используется для автоматизации процедур тестирования. Тестовые сценарии JavaScript, написанные для API автоматизации пользовательского интерфейса, имитируют взаимодействие пользователя с приложением и возвращают информацию журнала на главный компьютер.

Что такое SpriteKit и что такое SceneKit?

SpriteKit — это фреймворк для простой разработки анимированных 2D-объектов.

SceneKit — это фреймворк, унаследованный от OS X, который помогает рендерингу 3D-графики.

Ожидается, что SpriteKit, SceneKit и Metal станут основой для мобильных игр нового поколения, которые переопределяют то, что могут предложить мощные графические процессоры устройств iOS.

Что такое iBeacons?

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

Технология iBeacon позволяет мобильным приложениям понимать свое положение в микролокальном масштабе и предоставлять пользователям гиперконтекстный контент в зависимости от их местоположения.

В основе технологии связи лежит Bluetooth Low Energy.

Какова иерархия для UIButton до NSObject?

UIButton наследуется от UIControl, UIControl наследуется от UIView, UIView наследуется от UIResponder, UIResponder наследуется от корневого класса NSObject.

Что такое метод swizzling?

Изменение метода — это процесс изменения реализации существующего селектора во время выполнения. Это возможность изменить функциональность метода во время выполнения, только в Objective-C. (давайте научимся творить магию здесь)

Что такое безымянная категория?

Безымянная категория потеряла популярность после того, как @protocol был расширен для поддержки опциональных методов @.

Содержит ли Objective-C частные методы?

Нет, в программировании на Objective-C нет ничего, что называлось бы приватным методом. Если метод определен только в .m, он становится защищенным; если он определен в .h, он общедоступен.

Если нам действительно нужен приватный метод, нам нужно добавить локальную категорию/безымянную категорию/расширение класса в класс, добавить метод в категорию и определить его в class.m.