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

Что касается документов, я должен признаться, что это немного длинновато, где-то 2 000+ слов. В первой половине я рассмотрю три основных типа доступа: частный, частный файл и внутренний. Во второй половине я расскажу о создании фреймворка с общедоступным и открытым типами доступа. Оба раздела помечены, и их можно читать независимо.

Начнем с некоторой предыстории. Еще в былые дни 1970-х годов профессор информатики ETH Zurich, Швейцария, человек по имени Никлаус Вирт создал и опубликовал революционный язык для преподавания. Язык, который он назвал Паскалем. Это был язык, основанный на его книге Алгоритмы + структуры данных = программы.

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

Действительно, Паскаль как язык был разработан настолько хорошо, что Apple решила использовать его для написания Mac OS. Mac OS, как в той ОС, в которой работали обреченные компьютеры Lisa и Macintosh. Смелое решение в то время, когда все еще использовали C.

Тем временем сам C претерпевал трансформацию. Спустя десять лет после запуска Pascal с разработкой Objective C, снова язык, который принял идеи и концепцию инкапсуляции. По совпадению, новый язык снова подобрал новый язык Стива Джобса над проектом NextStep OS. Компания, которая поставила бы нам цель C.

Остальное, как говорится, уже история. Джобс вернулся в Apple в 1997 году, принеся с собой новую ОС и новый язык, Objective C. Objective C, как вы знаете, предшественник Swift, который, как мы все знаем, вышел в 2014 году.

Сегодня Swift продолжает развивать наследие Паскаля и имеет пять уровней инкапсуляции в языке: частный, частный, внутренний, общедоступный и открытый. Уровни доступа могут применяться к переменным, классам, структурам, перечислениям, функциям, методам и даже новым оболочкам, встроенным в SwiftUI. Уровни доступа, относящиеся к исходному файлу, объектам и / или модулям, в которых определен указанный код.

Private, FilePrivate и Internal

Хорошо, хватит истории, давайте код. Мы собираемся использовать два проекта, чтобы проиллюстрировать, как работают эти уровни доступа. Начнем с самого ограничительного, частного. Создайте новый проект, используя SwiftUI в качестве интерфейса, и добавьте к нему файл класса касания какао, назовите его Person.class и добавьте код, который вы видите здесь.

class Person {
  var id: String = "SecretID"
}

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

Уровень доступа по умолчанию здесь внутренний. Внутренние означает доступные во всем модуле, в котором они определены. По умолчанию все, что вы создаете в проекте Xcode, имеет внутренний доступ. Итак, присвоив классу Person имя в реализации SwiftUI, все переменные в нем доступны для него. А теперь давайте попробуем немного зафиксировать ситуацию. Измените id в классе людей, чтобы сделать его закрытым.

private var id: String = "SecretID"

Вы сразу получите сообщение об ошибке в вашем коде ContentView.swift SwiftUI, чтобы предупредить вас, что переменные класса больше не доступны. Мы сломали его, как исправить.

Теперь предположим, что мы не хотим изменять id, мы просто хотим его прочитать. Мы можем исправить эту ошибку, определив метод в классе, который мы можем использовать для возврата указанного идентификатора. Измените класс Person ID, чтобы он выглядел как показанный здесь код.

class Person {
  private var id: String = "SecretID"
  func displayid() -> String {
    return id
  }
}

И что ж, поскольку функции displayed() будут назначены внутренние разрешения, мы можем вызвать указанную функцию в нашем коде SwiftUI.

struct ContentView: View {
  @State var person = Person()
  var body: some View {
    Text("\(person.displayid())")
  }
}

Двигаясь вперед, представьте, что нам нужно изменить secret ID. Для этого мы можем создать новый класс (хотя и в том же файле, что и Person.class), к которому добавим функцию, показанную здесь.

class Management {
  static func updateID(for person:Person, with newID:String ) {   
    person.id = newID
  }
}

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

Чтобы исправить это, но сохранить некоторую безопасность, мы можем изменить уровень доступа переменной id в нашем классе Person на fileprivate. Поскольку вы определили класс Management в том же файле, что и класс Person, он получит доступ; без открытия доступа к реализации SwiftUI в файле ContentView.swift.

fileprivate var id: String = "SecretID"

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

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

Фреймворки, общедоступные и открытые

Теперь, чтобы проиллюстрировать последние два уровня инкапсуляции, мы поменяем проекты. Вернитесь на рабочий стол и создайте новый проект. Назовите это BuyersPortal; Я собираюсь снова использовать SwiftUI в качестве интерфейса.

Откройте ContentView.swift и добавьте к нему эти структуры и классы.

Здесь мы определяем запись продукта и класс, который будет ее использовать. Теперь обновите ContentView.swift, чтобы получить доступ к вашей новой структуре / классам.

Когда вы будете готовы к бегу; Действуй. Будет отображаться newCustomer с нулевым значением. Нажмите на текст, и он получит доступ к только что определенному вами методу, и цена изменится. Разрешения по умолчанию для классов Product и Purchases являются внутренними, и со всем в одном файле все будет работать отлично.

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

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

Чтобы создать фреймворк, вернитесь в Xcode и начните новый проект. Назовите это Engine, но подождите, не выбирайте обычное одностраничное приложение, на этот раз выберите платформу.

Появится проект, который будет выглядеть довольно пустым, если в нем будет всего несколько файлов. Добавьте в него новый файл быстрого исходного кода, скопируйте и вставьте приведенный выше код, определяющий объекты и методы Product и Purchase в вашем новом проекте фреймворка.

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

  • Вернитесь к проекту BuyersPortal, с кодом, из которого вы только что вырезали и вставили структуру и класс, и закомментируйте все.
  • Откройте папку, содержащую проект Engine (со структурой и классом внутри), а затем перетащите xcodeproj из нее в BuyersPortal.

Инспектор файлов проекта BuyersPortal теперь должен выглядеть так. Проект Engine находится в проекте BuyersPortal.

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

Теперь скомпилируйте все, чтобы убедиться, что нам не удалось повредить наш рабочий стол. Хорошо, мы готовы постепенно восстанавливать ContentView.swift в BuyersPortal. Начните со строки, определяющей Purchases. Это не сработает; вы получите сообщение об ошибке «Не могу найти…»

Это не проблема с быстрым разрешением, это говорит вам, что он не понимает, о чем вы говорите. Это все еще проблема Xcode.

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

Теперь выберите проект BuyersPortal и прокрутите вниз до области Frameworks, Libraries и Embedded Content. Вам нужно перетащить Engine.framework, который вы только что нашли, в этот раздел. BuyersPortal Платформы, библиотеки и встроенное содержимое проекта должны выглядеть так, если вы все поняли правильно.

Не совершайте ошибку, перетаскивая вниз в раздел «Платформы, библиотеки и встроенный код» проекта Engine, это не сработает!

Подключив его, убедитесь, что вы теперь включили его с оператором импорта и в свой ContentView.swift файл. ContentView.swift в BuyersPortal то есть.

import Engine

Теперь раскомментируйте начальные строки, чтобы определить ваш первый Purchases объект.

@State var newCustomer = Purchases()

Это не сработает, но не волнуйтесь, мы почти у цели. Теперь вам нужно исправить разрешения Swift.

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

Раскомментируйте onTapGesture с объектом Text, он снова будет жаловаться на права доступа к структуре Product и переменной products в классе Purchases, а также на доступ к методу calculatePrice. Сделайте их всех общедоступными в вашем классе Engine. Ваш код в Engine продукте должен выглядеть так.

Скомпилируйте и запустите. Нажмите на поле newCustomer, и он должен запустить метод calculate price и сообщить новую цену. И что ж, вот и все, публичный доступ.

Мы рассмотрели все, кроме последнего типа контроля доступа, а именно открытый. А теперь представьте, что Стюарт хочет создать здесь подкласс нашего класса Purchases. Например, ему может понадобиться дисконтный класс. Класс, наследующий свои значения и методы непосредственно от нашего Purchases класса.

Отредактируйте проект BuyersPortal и добавьте к нему новый файл DiscountPurchases.swift. Добавьте этот код в файл DiscountPurchases.swift. Стюарт предлагает скидки покупателям старше 55 лет.

Чтобы было ясно, это пока не сработает. Нам нужно снова настроить разрешения для нашего класса Engine.

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

open class Purchases {

Теперь вернитесь к классу DiscountPurchases и добавьте этот код.

override func calculatePrice() -> Double {
  super.calculatePrice() * (1 - discountPercentage / 100)
}

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

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