Введение

Мы изучили основные способы использования протоколов и шаблона делегата в первой главе. Теперь давайте углубимся в некоторые другие темы в Протоколах. Если вы пропустили первую часть этой серии, то советую пройти ее Часть 1.

Общие протоколы или протоколы со связанным типом

Итак, зачем нам в первую очередь нужен общий тип? Давайте посмотрим на этот пример ниже

//Protocol Associated Types
enum AssetType{
    case Computer
    case Laptop
    case Mobile
    case Wifi
}
struct InventoryList<T> {
    var items: [T]
    mutating func add(item: T) {
        items.append(item)
    }
    mutating func remove() -> T {
        return items.removeLast()
    }
    func isCapacityLow() -> Bool {
        return items.count < 3
    }
}
var inventory = InventoryList<AssetType>(items: [AssetType.Computer, AssetType.Mobile,AssetType.Wifi])

Как мы видим, с помощью Generics мы можем хранить несколько элементов в структуре InventoryList. Это, очевидно, сохраняет наш дублированный код. Итак, как мы можем использовать дженерики в Swift? Посмотрим

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

//Associated Types
protocol CustomCollection {
    associatedtype CustomType
    var items: [CustomType] { set get }
    mutating func add(item: CustomType)
    var size: Int { get }
    mutating func removeLast() -> CustomType
}
extension CustomCollection{
    mutating func add(item: CustomType) {
        items.append(item)
    }
    mutating func removeLast() -> CustomType{
       return items.removeLast()
    }
}

Тип, который соответствует протоколу, может неявно удовлетворять требованию связанного типа, предоставляя заданный тип там, где протокол ожидает появления связанного типа:

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

Давайте посмотрим, как мы соблюдаем общие протоколы.

struct StringCollection: CustomCollection{
    typealias CustomType = String // Optional
    var items: [String]
    var size: Int
}

CustomType теперь будет считаться строкой. Это так просто!!!

Протоколы только класса

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

protocol ClassOnlyProtocol: class, CustomCollection { 
// Protocol requirements 
}

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

struct MyStruct: ClassOnlyProtocol {
 // error: Non-class type 'MyStruct' cannot conform to class protocol
 'ClassOnlyProtocol' 
}

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

protocol MyProtocol: ClassOnlyProtocol {
 // ClassOnlyProtocol Requirements 
// MyProtocol Requirements 
} 
class MySecondClass: MyProtocol { 
// ClassOnlyProtocol Requirements 
// MyProtocol Requirements 
}

Справочная семантика протоколов только для классов

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

protocol Foo : class {
 var bar : String { get set } 
} 
func takesAFoo(foo:Foo) { 
// this assignment requires reference semantics,
 // as foo is a let constant in this scope. 
foo.bar = "new value" 
}

В этом примере, поскольку Foo является протоколом только для класса, присвоение bar допустимо, поскольку компилятор знает, что foo является типом класса и, следовательно, имеет ссылочную семантику.

Расширение протокола для определенного соответствующего класса

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

protocol MyProtocol {
        func doSomething()
    }
 extension MyProtocol where Self: UIViewController {
        func doSomething() {
            print("UIViewController default protocol implementation")
        }
    }
 class MyViewController: UIViewController, MyProtocol { }
 let vc = MyViewController()
 vc.doSomething() // Prints "UIViewController default protocol 
implementation"

Реализация протокола Hashable

Типы, используемые в наборах и словарях (ключ), должны соответствовать протоколу Hashable, который наследуется от протокола Equatable.

Должен быть реализован пользовательский тип, соответствующий протоколу Hashable.

  • Вычисленное свойство hashValue
  • Определите один из операторов равенства, т. е. == или !=.

В следующем примере реализован протокол Hashable для пользовательской структуры:

struct Cell {
        var row: Int
        var col: Int
        init(_ row: Int, _ col: Int) {
            self.row = row
            self.col = col
        }
    }
    extension Cell: Hashable {
        // Satisfy Hashable requirement
        var hashValue: Int {
            get {
                return row.hashValue^col.hashValue
            }
        }
        // Satisfy Equatable requirement
       static func ==(lhs: Cell, rhs: Cell) -> Bool {
            return lhs.col == rhs.col && lhs.row == rhs.row
        }
    }
    // Now we can make Cell as key of dictonary
    var dict = [Cell : String]()
    dict[Cell(0, 0)] = "0, 0"
    dict[Cell(1, 0)] = "1, 0"
    dict[Cell(0, 1)] = "0, 1"
    // Also we can create Set of Cells
    var set = Set<Cell>()
    set.insert(Cell(0, 0))
    set.insert(Cell(1, 0))

Вывод

Apple рекомендует протокольно-ориентированное программирование, а не объектно-ориентированное программирование. Поэтому для разработчика Swift становится очень важным использовать протоколы наилучшим образом.

До скорой встречи в следующей истории. Бйе!!