Конкретный тип против абстрактного
В Swift имеются различные типы категорий, включая два фундаментальных понятия: конкретные и абстрактные типы.
Что такое «конкретный тип»?
Конкретный тип непосредственно инстанцируется для создания объектов:
- Определяются все свойства и методы.
- Примеры конкретных типов — классы и структуры.
Простой конкретный тип
struct Person {
var name: String
var age: Int
}
let person = Person(name: "John", age: 20)
print("Person name \(person.name) Age \(person.age)")
Конкретный тип с использованием класса
class Employee {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
func empActiveStatus() {
print("Active")
}
}
let employee = Employee(name: "Jones", age: 35)
print("Employe name \(employee.name) Age \(employee.age)")
print("Active Status \(employee.empActiveStatus())")
Что такое «абстрактный тип»?
Это тип, которым определяется набор требований без фактической реализации:
- Требования относятся к свойствам и методам.
- Абстрактный тип не инстанцируется напрямую.
protocol Shape {
func area() -> Double
}
struct Circle: Shape {
var radius: Double
func area() -> Double {
return Double.pi * radius * radius
}
}
struct Square: Shape {
var side: Double
func area() -> Double {
return side * side
}
}
let circle = Circle(radius: 5.0)
print("Area of circle: ", circle.area())
let square = Square(side: 5.0)
print("Area of square: ", square.area())
Как получают абстрактный тип?
Основной способ — с помощью протокола.
Протокол — это абстрактный тип: им объявляется требование, но без фактической реализации.
Нужен конкретный тип с соответствием протоколу и конкретной реализацией.
Суммируем
- Конкретный тип инстанцируется и используется непосредственно.
- Как и протоколом, абстрактным типом конкретная реализация определяется, но не предоставляется.
- Определяется общий интерфейс с несколькими конкретными типами: полиморфизм в деле.
Обработка исключений
В Swift процесс обработки исключений или ошибок — это еще и управление: выбрасывание, перехват и манипулирование ими во время выполнения.
Способы обработки ошибок или исключений:
- do;
- try;
- catch;
- throw;
- throws.
Прежде чем обрабатывать исключения, создадим перечисление ошибок с соответствием протоколу ошибок и выбрасыванием значения ошибки внутри функции:
enum CustomError: Error {
case someError
}
С do-try-catch — do-catch перехватываем и обрабатываем ошибки, выбрасываемые блоком кода в блоке do
:
enum CustomError: Error {
case someError(message: String)
}
func someFunction() throws {
let randomNum = Int.random(in: 0...1)
if randomNum == 0 {
throw CustomError.someError(message: "Error A Occurred")
} else {
print("Function Executed successfully")
}
}
do {
try someFunction()
} catch CustomError.someError {
print("Caught a specific Error: CustomError.someError")
} catch {
print("Unknown Error \(error)")
}
Выбрасывающая функция: выбрасываем ошибки выбрасывающей функцией с ключевым словом throws
или словом throw
внутри блока:
enum CustomError: Error {
case someError(message: String)
}
func someFunction() throws {
let randomNum = Int.random(in: 0...1)
if randomNum == 0 {
throw CustomError.someError(message: "Error A Occurred")
} else {
throw CustomError.someError(message: "Error B Occurred")
}
}
try someFunction()
try: даем понять, что знаем о потенциальной ошибке, как ее обработать, еще до выбрасывания ее функцией:
enum CustomError: Error {
case someError
}
func someFunction() throws -> Int {
let randomNum = Int.random(in: 0...1)
if randomNum == 0 {
throw CustomError.someError
} else {
return 100
}
}
do {
try someFunction()
} catch CustomError.someError {
print("Caught a specific Error: CustomError.someError")
} catch {
print("Unknown Error \(error)")
}
try!: используем эту функцию, только убедившись, что никакой ошибки ею не выдается:
enum CustomError: Error {
case someError
}
func someFunction() throws -> Int {
let randomNum = Int.random(in: 0...1)
if randomNum == 0 {
throw CustomError.someError
} else {
return 100
}
}
let result = try! someFunction()
print("Result \(result)")
try?: необязательная, но внутри возможен ответ или ошибка. Так отключится блок catch
, в случае ошибки просто вернется nil
:
enum CustomError: Error {
case someError
}
func someFunction() throws -> Int {
let randomNum = Int.random(in: 0...1)
if randomNum == 0 {
throw CustomError.someError
} else {
return 100
}
}
let result = try? someFunction()
print("Result \(result ?? 0)")
Спецификаторы доступа
Спецификаторы доступа характеризуются видимостью и доступностью членов класса, структур, перечислений.
Но прежде снова о базовых понятиях приложения iOS:
- Модуль.
- Фреймворк.
- Исходный файл.
- Цель.
- Пакет.
Модуль — единый блок распределения кода, импортируемый ключевым словом import
.
Цель — пакет приложения или фреймворк, называемый отдельным модулем.
Исходный файл — единый исходный файл в одном модуле, фреймворке или приложении.
Пакет — набор файлов для приложения сборки, делаются с помощью ipa-файла.
Спецификаторы доступа используются для управления видимостью и доступностью классов, структур, перечислений, свойств, методов.
В Swift доступно пять уровней доступа:
- Open.
- Public.
- Internal.
- File private.
- Private.
Open
- Самый разрешенный уровень доступа.
- Доступен из любого исходного файла любого модуля, цели или фреймворка.
- Возможно создание подкласса или переопределение внешними модулями.
import UIKit
// Модуль
class SomeClass {
var tableView: UITableView = UITableView()
}
// Из документации Apple
open class UITableView : UIScrollView, NSCoding, UIDataSourceTranslating {
}
// Пример
open class MyClass {
open var property: Int = 0
open func someFunc() {
}
}
// Выбросится ошибка, так как нет открытой структуры.
// Заменяем «open» на «struct»
open struct MyStruct {
}
Public
- Аналогично уровню доступа Open, но с ограничениями.
- Доступен из любого исходного файла любого модуля, цели или фреймворка.
- Невозможно создание подкласса или переопределение внешними модулями.
Попробуем public с фреймворком UIKit:
public class SomeButtonView: UIButton {
public func setButtonTitle(_ title: String) {
setTitle(title, for: .normal)
}
}
Internal
- В Swift это уровень доступа по умолчанию.
- Доступен из любого исходного файла того же модуля, цели или фреймворка, но не из внешних модулей.
- Полезен для определения деталей внутренней реализации.
// Внутренние класс и функции
internal class InternalViewController: UIViewController {
internal override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
private lazy var label: UILabel = {
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
label.text = "Internal Label View"
label.textAlignment = .center
label.font = .systemFont(ofSize: 12, weight: .bold)
label.numberOfLines = 1
label.textColor = .blue
label.sizeToFit()
return label
}()
private func setupUI() {
view.addSubview(label)
}
}
File private
- Доступен из того же исходного файла.
- Полезен для сокрытия деталей реализации в одном исходном файле.
Вот пример fileprivate и private в одном и том же файле:
class Employee {
fileprivate var firstName: String
private var nickName: String
init (firstName: String, nickName: String) {
self.firstName = firstName
self.nickName = nickName
}
fileprivate func updateFirstName(){}
private func updateNickName() {}
}
class FetchRecords: Employee {
func getEmployee() -> String {
updateFirstName() // Доступно
return firstName // Доступно
}
func getAnotherEmployee() -> String {
updateNickName() // «updateNickName» недоступен из-за уровня защиты «private»
return nickName // «nickName» недоступен из-за уровня защиты «private»
}
}
extension Employee {
fileprivate func modifyName() {
print("Employee Name is \(firstName)") // Доступно
print("Employee Nick Name is \(nickName)") // Доступно
}
}
var employee = Employee(firstName: "Sabapathy", nickName: "Saba")
employee.modifyName()
Private
- Самый ограниченный уровень доступа.
- Доступен из объявления или расширений того же исходного файла.
- Используется для инкапсуляции деталей реализации в пределах конкретной области.
Вот пример fileprivate и private в разных файлах:
// Main.swift
class Employee {
fileprivate var firstName: String
private var nickName: String
init (firstName: String, nickName: String) {
self.firstName = firstName
self.nickName = nickName
}
fileprivate func updateFirstName(){}
private func updateNickName() {}
}
// Extension.swift
extension Employee {
fileprivate func modifyName() {
print("Employee Name is \(firstName)")
// Имя недоступно из-за уровня защиты «file-private»
print("Employee Nick Name is \(nickName)")
// «nickName» недоступно из-за уровня защиты «private»
}
}
«Open» против «Public»
- Open применяется к классу и членам класса, не к структурам.
- Доступ, создание подкласса и переопределение класса или членов класса возможны вне определенного модуля.
- Public применяется к классу, структурам, перечислениям и их членам.
- Вне определенного модуля возможен только доступ: без создания подкласса или переопределения класса.
Когда используются «Open» или «Public»?
- Open при разработке общедоступного API с возможностью расширения его другими и изменения реализации вне модуля.
- Public при доступе к ним других модулей, но без возможности их расширения или изменения.
Когда используется «Internal»?
- При создании фреймворка, причем без доступа других модулей/фреймворков к сущностям.
- При работе над крупномасштабными проектами с несколькими модулями/фреймворками.
Когда используются «File-private» или «Private»?
- private при наличии модели данных с пользовательской информацией. Так данные защищены от внесения изменений.
- file-private применяется для служебных или вспомогательных классов. Так скрываются детали реализации, предотвращается доступ к ним.
Не забываем и о «Final» для:
- Запрета класса во фреймворке для создания подклассов.
- Обозначения свойств и методов.
- Осуществления статической диспетчеризации, увеличения производительности.
- Предотвращения переопределения методов во фреймворке и избежания проблем.
Читайте также:
- Использование стека навигации SwiftUI для идеального поведения TabView
- Подписки, чеки и StoreKit в iOS 14
- Использование SwiftUI в UIKit
Читайте нас в Telegram, VK и Дзен
Перевод статьи Kanagasabapathy Rajkumar: iOS — Swift Interview Questions 2023