Swift学习笔记(四)-协议和扩展

协议

声明

使用 protocol来声明协议。

protocol ExampleProtocol {
    var simpleDescription: String { get }
    mutating func adjust()
}

协议的遵循

枚举以及结构体,都可以遵循某个协议。

class SimpleClass: ExampleProtocol {
    var simpleDescription: String = “A very simple class.var anotherProperty: Int = 69105
    func adjust() {
        simpleDescription +=Now 100% adjusted.}
}
结构体
struct SimpleStructure: ExampleProtocol {
    var simpleDescription: String = “A simple structure”
    mutating func adjust() {
        simpleDescription +=(adjusted)}
}
mutating关键字

mutating关键字来声明在 SimpleStructure中使方法可以修改结构体。

在 SimpleClass中则不需要这样声明,因为类里的方法总是可以修改其自身属性的。

扩展

概念

扩展为现有的结构体枚举类型、或协议添加了新功能。这也包括了为无访问权限的源代码扩展类型的能力(即所谓的逆向建模)。扩展和 Objective-C 中的分类类似。(与 Objective-C 的分类不同的是,Swift 的扩展没有名字。)

Swift 中的扩展可以:

  • 添加计算实例属性计算类型属性
  • 定义实例方法类型方法
  • 提供新的构造函数
  • 定义下标
  • 定义和使用新内嵌类型
  • 使现有的类型遵循某协议

在 Swift 中,你甚至可以扩展一个协议,以提供其要求的实现或添加符合类型的附加功能。详见 协议扩展

语法

使用 extension关键字声明扩展。

extension SomeType {
    // new functionality to add to SomeType goes here
}

扩展可以使已有的类型遵循一个多个协议。在这种情况下,协议名的书写方式与类或结构体完全一样:

extension SomeType: SomeProtocol, AnotherProtocol {
    *// implementation of protocol requirements goes here*
}

计算属性

扩展可以向已有的类型添加计算实例属性计算类型属性。下面的例子向 Swift 内建的Double 类型添加了五个计算实例属性,以提供对距离单位的基本支持:

extension Double {
    var km: Double { return self * 1_000.0 }
    var m: Double { return self }
    var cm: Double { return self / 100.0 }
    var mm: Double { return self / 1_000.0 }
    var ft: Double { return self / 3.28084 }
}
let oneInch = 25.4.mm
print(One inch is \(oneInch) meters”)
*// Prints “One inch is 0.0254 meters”*
let threeFeet = 3.ft
print(Three feet is \(threeFeet) meters”)
*// Prints “Three feet is 0.914399970739201 meters”*

这些计算属性表述了Double 值应被看作是确定的长度单位。尽管它们被实现为计算属性,这些属性的名字仍可使用点符号添加在浮点型的字面量之后,作为一种使用该字面量来执行距离转换的方法。
在这个例子中,一个 1.0 的 Double值表示“一米”。这就是为什么 m 计算属性要返回 self ——表达式 1.m 表示计算 1.0 的 Double 值。
其他的单位则在以米作为计量值的基础上加以转换表示。一千米表示1000米,所以 km 计算属性将值乘 1_000.00 以用米来表示。类似的,一米有3.28084英尺,所以 ft 计算属性用 Double值除以3.28084,将英尺转换为米。

上述属性为只读计算属性,为了简洁没有使用 get关键字。他们都返回 Double 类型的值,可用于所有使用 Double值的数学计算中:

let aMarathon = 42.km + 195.m
print(“A marathon is \(aMarathon) meters long”)
*// Prints “A marathon is 42195.0 meters long”*

扩展可以添加新的计算属性,但是不能添加存储属性,也不能向已有的属性添加属性观察者

构造函数

扩展可向已有的类型添加新的构造函数。这允许你扩展其他类型以使构造函数接收你的自定义类型作为形式参数,或提供该类型的原始实现中未包含的额外初始化选项。
扩展能为类添加新的便捷构造函数,但是不能为类添加指定构造函数析构函数。指定构造函数析构函数必须由原来类的实现提供。

如果你使用扩展为一个值类型添加构造函数,且该值类型为其所有储存的属性提供默认值,而又不定义任何自定义构造函数时,你可以在你扩展的构造函数中调用该类型默认的构造函数和成员构造函数 。

下面的例子定义了一个自定义的 Rect 结构体用于描述几何矩形。这个例子也定义了两个辅助结构体 Size 和 Point ,二者的默认值都是 0.0 :

struct Size {
    var width = 0.0, height = 0.0
}
struct Point {
    var x = 0.0, y = 0.0
}
struct Rect {
    var origin = Point()
    var size = Size()
}

由于 Rect 结构体为其所有属性提供了默认值,它将自动接收一个默认的构造函数和一个成员构造函数。这些构造函数能用于创建新的 Rect 实例:

let defaultRect = Rect()
let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0),
                          size: Size(width: 5.0, height: 5.0))

你可以扩展 Rect 结构体以额外提供一个接收特定原点和大小的构造函数:

extension Rect {
    init(center: Point, size: Size) {
        let originX = center.x - (size.width / 2)
        let originY = center.y - (size.height / 2)
        self.init(origin: Point(x: originX, y: originY), size: size)
    }
}

这个构造函数首先基于提供的 center 点和 size 值计算合适的原点。然后构造函数调用该结构体的自动成员构造函数 init(origin:size:),这样就将新的原点和大小值保存在了对应属性中:

let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
                      size: Size(width: 3.0, height: 3.0))
*// centerRect’s origin is (2.5, 2.5) and its size is (3.0, 3.0)*

如果你使用扩展提供了一个新的构造函数,你仍应确保每一个实例都在初始化完成时完全初始化。

方法

扩展可以为已有的类型添加新的实例方法和类型方法。下面的例子为Int类型添加了一个名为 repetitions 的新实例方法:

extension Int {
    func repetitions(task: () -> Void) {
        for _ in 0..<self {
            task()
        }
    }
}

repetitions(task:)方法接收一个() -> Void 类型的单一实际参数,它表示一个没有参数且无返回值的函数。
在这个扩展定义之后,你可以在任何整型数字处调用 repetitions(task:)方法,以执行相应次数的操作:

3.repetitions {
    print(Hello!)
}
*// Hello!*
*// Hello!*
*// Hello!*

异变实例方法

异变的含义:自己修改自己?

增加了扩展的实例方法仍可以修改(或异变)实例本身。

结构体和枚举类型方法在修改 self或本身的属性时必须标记实例方法为 mutating,和原本实现的异变方法一样。
下面的例子为 Swift 的 Int 类型添加了一个新的异变方法 square ,以表示原值的平方:

extension Int {
    mutating func square() {
        self = self * self
    }
}
var someInt = 3
someInt.square()
*// someInt is now 9*

下标

我的理解是通过定义subcript函数,来让一个不支持下标语法的类型支持下标语法。

扩展能为已有的类型添加新的下标。下面的例子为 Swift 内建的 Int类型添加了一个整型下标。这个下标[n]返回了从右开始第 n 位的十进制数字:

  • 123456789[0] 返回 9
  • 123456789[1] 返回 8
    ……以此类推:
extension Int {
    subscript(digitIndex: Int) -> Int {
        var decimalBase = 1
        for _ in 0..<digitIndex {
            decimalBase *= 10
        }
        return (self / decimalBase) % 10
    }
}
746381295[0]
*// returns 5*
746381295[1]
*// returns 9*
746381295[2]
*// returns 2*
746381295[8]
*// returns 7*

Int值没有所需索引的那么多数字,下标实现返回 0 ,就像是这个数左边用零填充:

746381295[9]
*// returns 0, as if you had requested:*
0746381295[9]

内嵌类型

扩展可以为已有的类、结构体和枚举类型添加新的内嵌类型

extension Int {
    enum Kind {
        case negative, zero, positive
    }
    var kind: Kind {
        switch self {
        case 0:
            return .zero
        case let x where x > 0:
            return .positive
        default:
            return .negative
        }
    }
}

这个例子为Int添加了新的内嵌枚举类型。这个名为 Kind 的枚举类型表示一个特定整数的类型。具体表示了这个数字是负数、零还是正数。
这个例还向Int 中添加了新的计算实例属性kind ,以返回该整数的合适 Kind 枚举情况。

这个内嵌的枚举类型可以和任意 Int 一起使用:

func printIntegerKinds(_ numbers: [Int]) {
    for number in numbers {
        switch number.kind {
        case .negative:
            print(-, terminator: “”)
        case .zero:
            print(0, terminator: “”)
        case .positive:
            print(+, terminator: “”)
        }
    }
    print(“”)
}
printIntegerKinds([3, 19, -27, 0, -6, 0, 7])
*// Prints “+ + - 0 - 0 + “*

这里 printIntegerKinds(_:)函数接收一个 Int 的数组并对这些值进行遍历。对数组的每一个数字,函数考虑这个整数的 kind 计算属性,并输出合适的描述。

已知 number.kindInt.Kind类型。因此, switch 语句中的所有 Int.Kind情况值都可以简写,例如用.Negative而不是 Int.Kind.Negative

关于协议的一处特殊之处

(不知道这个特殊之处有啥名字?)
你可以使用协议名称就像其他命名类型一样——比如说,创建一个拥有不同类型但是都遵循同一个协议的对象的集合。当你操作类型是协议类型的值的时候,协议外定义的方法是不可用的。

let protocolValue: ExampleProtocol = a
print(protocolValue.simpleDescription)
*// print(protocolValue.anotherProperty) // Uncomment to see the error*

尽管变量 protocolValue有 SimpleClass的运行时类型,但编译器还是把它看做 ExampleProtocol。

这意味着你不能访问类在这个协议中扩展的方法或者属性。

这一段看的不是很明白,总结下来大致意思应该是:一旦声明了一个遵循某个协议的变量,那么只能访问这个协议内定义的成员方法属性,这个变量的实际类型所定义的成员方法与属性不能再被访问。

Swift学习群

欢迎加入本人的Swift学习微信群,一同互相监督学习,我微信:reese90

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值