关闭

Swift-扩展(Extensions)(十八)

标签: Swift扩展extension
136人阅读 评论(0) 收藏 举报
分类:

前言

从这里开始的三个章节 – 扩展、协议、泛型,都是非常重要的知识点,并且在开发中有着举足轻重的意义。如果提前看过一些技术文章,难免看到面向泛型开发、扩展整合代码、优先使用协议+结构体的方式,尽量少使用类等。自己也希望这三个章节结束后可以在闲暇时间利用学的一些基础知识开发一个大一些的 Demo。

扩展 就是向一个已有的类、结构体、枚举类型或者协议类型添加新的功能。这包括在没有权限获取原始代码的情况下扩展类型的能力(即 逆向建模)。扩展和 Objective-C 中的分类 categories 类似。不同的是 Swift 的扩展没有名字,但是 Objective-C 有。

  • 扩展语法
  • 计算型属性
  • 构造器
  • 方法
  • 下标
  • 嵌套类型

注意,扩展可以对一个类型添加新的功能,但是不能重写已有的功能。

Swift 中扩展可以完成哪些功能:

o 扩展可以实现的功能
o 添加计算型属性和计算型静态属性
o 定义实例方法和类型方法
o 提供新的构造器
o 定义下标
o 定义和使用新的嵌套类型
o 使一个已有类型符合某个协议

分条详述

  1. 扩展语法

    声明一个扩展使用关键字 extension

    extension someType {
    //  添加到 someType 的新功能写到这里
    }

    一个扩展可以扩展一个已有类型,使其能够适配一个或多个协议 protocol 。当这种情况发生时,协议的名字应该完全按照类或结构体的名字的方式进行书写,即首字母大写的驼峰命名法:

    extension SomeType: SomeProtocol, AnotherProctocol {
    // 协议实现写到这里
    }

    按照这种方式添加的协议遵循者 protocol conformance 被称为 在扩展中添加协议成员

    注意,如果定义了一个扩展向一个已有类型添加新功能,那么这个新功能对所有已有实例中都是可用的,即使他们是在你的这个扩展前面定义的。所以一个文件中定义扩展的代码可以集中地写到一起,并不会有什么影响。

  2. 计算型属性

    扩展可以向已有类型添加计算型 实例 属性和计算型 类型 属性。下面的例子向 Swift 的内建 Double 类型添加了5个计算型实例属性,用于将不同的长度转化为 米,从而提供与距离单位协作的基本支持:

    //  扩展 Double,如果一个 Double 带有下面的单位,则转化为 米 -- m
    extension Double {
    var km: Double { return self * 1_000 }
    var m: Double { return self }
    var cm: Double { return self / 100.0 }
    var mm: Double { return self / 1_000.0 }
    var fm: Double { return self / 3.28084 }          //  英尺
    }
    let oneInch = 25.4.mm        //  oneInch = 0.0254
    let someKM = 12.km           //  someKM = 12000

    这些计算属性表达式的含义是把一个 Double 型的值看作是某单位下的长度值。即使他们被实现为计算型属性,但这些属性仍可以接一个带有点(dot)语法的浮点型字面量,而这恰恰是使用这些浮点型字面量实现距离转换的方式。

    注意,扩展可以添加新的计算属性,但是不可以添加存储属性,也不可以向已有属性添加属性观察器(指 willSetdidSet )。

  3. 构造器

    扩展可以向已有类型添加新的构造器。这可以让我们扩展其他类型,将自己的定制类型作为构造器参数,或者提供该类型的原始实现中没有包含的额外初始化选项。

    扩展能向类中添加新的便利构造器,但是不能向类中添加新的指定构造器或析构器。指定的构造器和析构器必须由原始的类实现来提供。

    注意1,上面两段话是有区别的。区别就是扩展中添加构造器时,值类型(包括结构体 structure 和 枚举 enumertation)和类类型(类 class)是不一样的处理方式。

    注意2,当使用扩展向一个值类型添加一个构造器,在 该值类型已经向所有存储属性提供默认值,而且没有定制任何执行构造器 custom initializers 时,可以在值类型的扩展构造器中调用默认构造器和逐一成员构造器构造器。如果已经把构造器写成值类型原始实现的一部分,那么上述特性不再适用。

    下面以一个值类型为例,原始类型没有提供指定构造器,在扩展中创建新的构造器并调用原始类型的默认构造器和逐一构造器,注释里对代码行为作了比较详细的说明:

    //  矩形尺寸,宽和高
    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()
    }
    //  定义一个使用默认值的矩形
    let defaultRect = Rect()
    //  定义一个期望的矩形
    let menberWiseRect = Rect(origin: Point(x: 3.0, y: 3.0), size: Size(width: 5, height: 7))
    /*************  有时提供的并不是起点,而是矩形的中心点,因此要扩展这个方法  *************/
    extension Rect {
    //  重新定义一个指定构造器
    init(centerPoint point: Point, rectSize size: Size) {
        //  转为为起点
        let originX = point.x - size.width / 2
        let originY = point.y + size.height / 2
        //  调用原始类型的默认构造器方法 --  注意,这里写的时候并不会代码补全!!!
        self.init(origin: Point(x: originX, y: originY), size: size)
    }
    }
    //  定义一个根据中心点确定的矩形 -- 此时,实例 Rect 时就有三个方法供选择
    //  分别是 -- 默认构造器,逐一构造器,扩展自定义构造器
    let rect1 = Rect()    //  默认构造器
    let rect2 = Rect(origin: Point(x: 3.0, y: 3.0), size: Size(width: 5, height: 7))   //  逐一构造器
    let centerRect = Rect(centerPoint: Point(x: 3.0, y: 3.0), rectSize: Size(width: 5, height: 7))     //  扩展自定义构造器
  4. 方法

    扩展可以向已有类型添加新的实例方法和类型方法。如前面的例子,给 Double 添加距离转换的计算属性,当然,那个只是属性。这里展示向 Int 类型添加一个名为 repetitions 的新实例方法,实现功能是多次执行某个任务:

    //  扩展 Int 类型
    extension Int {
    func repetitions(task: () -> ()) {
        for _ in 0 ..< self {
            //  执行这个任务 self 次
            task()
        }
    }
    }
    //  实际使用,其实这里是个尾随闭包 trailing
    3.repetitions { 
    print("Hello World")
    }
    //  效果,打印输出 3 次  Hello World

    通过扩展添加的实例方法也可以修改实例本身。结构体和枚举类型中修改 self 或其属性的方法,必须将该方法标注为 mutating ,正如来自原始实现的修改方法一样。

    下面的例子向 Swift 中 Int 类型添加一个新的名为 square 的修改方法,来实现一个原始值的平方计算,同时也说明一个小问题,就是一个文件中,可以对一个原始类型进行多次扩展:

    //  扩展,获取一个原始值的平方值
    extension Int {
    mutating func square() {
        self = self * self
    }
    }
    var number = 5
    number.square()     //  此时,number = 25
  5. 下标

    扩展可以向一个已有类型添加新下标。下面的例子向 Swift 内建类型 Int 添加一个整型下标,该下标 [n] 返回十进制数字从右向左的第 n 个数字。如果该 Int 值没有足够的位数,即下标越界,那么将会返回 0 :

    //  扩展,给 Int 添加下标
    extension Int {
    subscript(digitIndex: Int) -> Int {
        var decimalBase = 1    //  倍数
        var index = digitIndex    //  次数
        while index > 0 {
            decimalBase *= 10
            index -= 1
        }
    
        //  直接定位到第一位,显然,如果下标大于位数,那么 self / decimalBase 必然等于 0 ,即相当于在数字的左边自动补 0
        return (self / decimalBase) % 10
    }
    }
    //  举例
    654321[0]      //  1
    654321[4]      //  5
    654321[9]      //  0
  6. 嵌套类型

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

    //  扩展中添加新的嵌套类型
    extension Int {
    //  添加枚举类型,判断是正数、0、负数
    enum Kind {
        case Negative, Zero, Position
    }
    // 添加一个计算属性, 利用 switch 遍历枚举,并返回详情
    var kind: Kind {
        switch self {
        case 0:
            return .Zero
        case let x where x > 0:
            return .Position
        default:
            return .Negative
        }
    }
    }

    现在,这个嵌套枚举可以和一个 Int 值联合使用了:

    //  利用上面的扩展,判断数字的正负性
    func printIntegerKind(numbers: [Int]) {
    for number in numbers {
        switch number.kind {
        case .Negative:
            print("负数")
        case .Zero:
            print("零")
        case .Position:
            print("正数")
        }
        //  设置间隔
        print("    ")
    }
    }
    //  调用
    printIntegerKind([-10, 0, -23, 45, 78, -90])
    //  输出:负数  零  负数  正数  正数  负数

总结

这一小节主要介绍了几种可以在扩展中实现的方法、属性等,并且通过实例来演示如何使用。其实颇有一点纸老虎的感觉,可能在实际开发中有着非常强大的功能,但是入门级的掌握还是比较轻松的。

又到了周二,又可以唠唠权利的游戏第六季第6集了。下班了啊,简而言之,言而简之,龙母又开挂了啊~~~

龙母开挂啊

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:17154次
    • 积分:342
    • 等级:
    • 排名:千里之外
    • 原创:17篇
    • 转载:0篇
    • 译文:0篇
    • 评论:13条
    文章分类
    最新评论