swift 扩展 和 协议

swift 扩展 和 协议,第1张

扩展:可以给一个现有的类,结构体,枚举,还有协议添加新的功能

1、添加计算型实例属性和计算型类属性

2、定义实例方法和类方法

3、提供新的构造器

4、定义下标

5、定义和使用新的嵌套类型

6、使已经存在的类型遵循(conform)一个协议

语法:使用 extension 关键字声明扩展

extension SomeType {

   // 在这里给 SomeType 添加新的功能

}

扩展可以扩充一个现有的类型,给它添加一个或多个协议。

extension SomeType: SomeProtocol, AnotherProtocol {

   // 协议所需要的实现写在这里

}

协议:

1、协议的定义:与类、结构体和枚举的定义非常相似

protocol SomeProtocol {

     // 这里是协议的定义部分

}

2、遵循某个协议,各协议之间用逗号(,)分隔

struct SomeStructure: FirstProtocol, AnotherProtocol {

     // 这里是结构体的定义部分

}

3、把父类放到协议前面

class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {

     // 这里是类的定义部分

}

协议的属性要求:

1、只指定属性的名称和类型,并标明属性是 可读的 还是 可读可写的。

2、协议总是用 var 关键字来声明变量属性,在类型声明后加上 { set get } 来表示属性是可读可写的,可读属性则用 { get } 来表示:

protocol SomeProtocol {

     var mustBeSettable: Int { get set }            // 可读可写

     var doesNotNeedToBeSettable: Int { get }       // 只读

}

3、协议中定义类型属性使用 static 关键字。当类类型遵循协议时,除了 static 关键字,还可以使用 class 关键字来声明类型属性:

protocol AnotherProtocol {

     static var someTypeProperty: Int { get set }

}

协议的方法要求:

1、遵循协议的类型要实现协议中定义的方法。协议中的方法不需要大括号和方法体。

2、可以在协议中定义具有可变参数的方法,和普通方法的定义方式相同。

3、不支持为协议中的方法提供默认参数。

4、协议中定义的类方法要使用 static 关键字

protocol SomeProtocol {

     static func someTypeMethod()

}

协议的异变方法要求:

例如在值类型(即结构体和枚举)的实例方法中,使用 mutating 关键字的函数可以修改它所属的实例以及实例的任意属性的值。

1、如果协议中定义了实例方法,该方法会改变遵循该协议的类型的实例,就需要在方法前加 mutating 关键字。

2、实现协议中的 mutating 方法时,若是类类型,则不用写 mutating 关键字。而对于结构体和枚举,则必须写 mutating 关键字。

如:

protocol Togglable {

     mutating func toggle()

}

如果使用枚举或结构体来实现 Togglable 协议时,需要提供一个带有 mutating 前缀的 toggle() 方法,如果是类实现 Togglable 协议则不用。

协议的构造器要求:

1、协议可以要求遵循协议的类型实现指定的构造器

protocol SomeProtocol {

     init(someParameter: Int)

}

2、可以在遵循协议的类中实现构造器,无论是指定构造器,还是便利构造器都必须为构造器添加 required 修饰符。

class SomeClass: SomeProtocol {

     required init(someParameter: Int) {

         // 这里是构造器的实现部分

     }

}

3、如果类已经被标记为 final,那么不需要在协议构造器的实现中使用 required 修饰符,因为 final 类不能有子类。

4、如果一个子类重写了父类的指定构造器,并且该构造器满足了某个协议的要求,那么该构造器的实现需要同时标注 required 和 override 修饰符:

class SomeSubClass: SomeSuperClass, SomeProtocol {

     // 因为遵循协议,需要加上 required

     // 因为继承自父类,需要加上 override

     required override init() {

         // 这里是构造器的实现部分

     }

}

协议作为类型:

尽管协议本身并未实现任何功能,但是协议可以被当做一个功能完备的类型来使用。

委托:

定义协议来封装那些需要被委托的功能,这样就能确保遵循协议的类型能提供这些功能。

在扩展里添加协议遵循:

扩展可以为已有类型添加属性、方法、下标以及构造器。即便无法修改源代码,依然可以通过扩展令已有类型遵循并符合协议。

有条件地遵循协议:

可以通过在扩展类型时列出限制让泛型类型有条件地遵循某协议。在你采纳协议的名字后面写泛型 where 分句。

如:下面的扩展让 Array 类型只要在存储遵循 TextRepresentable 协议的元素时就遵循 TextRepresentable 协议。

extension Array: TextRepresentable where Element: TextRepresentable {

     var textualDescription: String {

         let itemsAsText = self.map { $0.textualDescription }

         return "[" + itemsAsText.joined(separator: ", ") + "]"

     }

}

let myDice = [d6, d12]

print(myDice.textualDescription)

// 打印 "[A 6-sided dice, A 12-sided dice]"

在扩展里声明采纳协议:

当一个类型已经遵循了某个协议中的所有要求,却还没有声明采纳该协议时,可以通过空的扩展来让它采纳该协议。

使用合成实现来采纳协议:

Swift 可以自动提供一些简单场景下遵循 Equatable、Hashable 和 Comparable 协议的实现。在使用这些合成实现之后,无需再编写重复的代码来实现这些协议所要求的方法。

Swift 为以下几种自定义类型提供了 Equatable 协议的合成实现:

1、遵循 Equatable 协议且只有存储属性的结构体。

2、遵循 Equatable 协议且只有关联类型的枚举

3、没有任何关联类型的枚举

Swift 为以下几种自定义类型提供了 Hashable 协议的合成实现:

1、遵循 Hashable 协议且只有存储属性的结构体。

2、遵循 Hashable 协议且只有关联类型的枚举

3、没有任何关联类型的枚举

Swift 为没有原始值的枚举类型提供了 Comparable 协议的合成实现。

协议的继承:

协议能够继承一个或多个其他协议,可以在继承的协议的基础上增加新的要求。

protocol InheritingProtocol: SomeProtocol, AnotherProtocol {

     // 这里是协议的定义部分

}

类专属的协议:

通过添加 AnyObject 关键字到协议的继承列表,就可以限制协议只能被类类型采纳。

protocol SomeClassOnlyProtocol: AnyObject, SomeInheritedProtocol {

     // 这里是类专属协议的定义部分

}

协议合成:

1、要求一个类型同时遵循多个协议是很有用的。你可以使用协议组合来复合多个协议到一个要求里。协议组合不定义任何新的协议类型。

2、协议组合使用 SomeProtocol & AnotherProtocol 的形式

下面的例子中,将 Named 和 Aged 两个协议按照上述语法组合成一个协议,作为函数参数的类型:

protocol Named {

     var name: String { get }

}

protocol Aged {

     var age: Int { get }

}

struct Person: Named, Aged {

     var name: String

     var age: Int

}

func wishHappyBirthday(to celebrator: Named & Aged) {

     print("Happy birthday, \(celebrator.name), you're \(celebrator.age)!")

}

let birthdayPerson = Person(name: "Malcolm", age: 21)

wishHappyBirthday(to: birthdayPerson)

// 打印 “Happy birthday Malcolm - you're 21!”

检查协议一致性:is 和 as

1、is 用来检查实例是否遵循某个协议,若遵循则返回 true,否则返回 false;

2、as? 返回一个可选值,当实例遵循某个协议时,返回类型为协议类型的可选值,否则返回 nil;

3、as! 将实例强制向下转换到某个协议类型,如果强转失败,将触发运行时错误。

可选的协议方法:optional

协议中使用 optional 关键字来定义可选方法或属性。可选方法或属性需要和 OC 打交道。协议和可选方法或属性都必须带上 @objc 属性。标记 @objc 特性的协议只能被继承自 OC 类或者 @objc 类遵循,其他类以及结构体和枚举均不能遵循这种协议。

@objc protocol CounterDataSource {

     @objc optional func increment(forCount count: Int) -> Int

     @objc optional var fixedIncrement: Int { get }

}

严格来讲,CounterDataSource 协议中的方法和属性都是可选的,因此遵循协议的类可以不实现这些要求,尽管技术上允许这样做,不过最好不要这样写。

协议扩展:

1、协议可以通过扩展来为遵循协议的类型提供属性、方法以及下标的默认实现。

2、如果遵循协议的类型自己实现了协议的方法或属性,那么就不会使用默认实现了。

如:PrettyTextRepresentable 协议的扩展提供了默认实现

extension PrettyTextRepresentable  {

     var prettyTextualDescription: String {

         return textualDescription

     }

}

为协议扩展添加限制条件:

扩展协议时,可以指定一些限制条件,只有遵循协议的类型满足这些限制条件时,才能获得协议扩展提供的默认实现。这些限制条件写在协议名之后,使用 where 子句来描述

例如,你可以扩展 Collection 协议,当集合中的元素遵循了 Equatable 协议的情况。

extension Collection where Element: Equatable {

     func allEqual() -> Bool {

         for element in self {

             if element != self.first {

                 return false

             }

         }

         return true

     }

}

let equalNumbers = [100, 100, 100, 100, 100]

let differentNumbers = [100, 100, 200, 100, 200]

由于数组遵循 Collection 而且整数遵循 Equatable,所以 equalNumbers 和 differentNumbers 都可以使用 allEqual() 方法。

print(equalNumbers.allEqual())     // 打印 "true"

print(differentNumbers.allEqual()) // 打印 "false"

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/993884.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存