十九、扩展 Extensions

1. 概述

扩展用于给已经存在的类、枚举、结构体添加新的功能。Swift中的扩展(extensions)与Objective-C中的分类(categories)的类似,但不同点在于,扩展没有名字。

扩展有如下功能:

  • 1)增加 computed属性和静态的computed属性
  • 2)定义实例方法 instance methods 和 类型方法type methods(比如类方法)
  • 3)提供新的构造器
  • 4)定义下标
  • 5)定义和使用新的嵌套类型
  • 6)使某个已存在的类型符合某个协议( 原话:Make an existing type conform to a protocol)

注意:扩展可以给一个类型增加功能,但是它不能覆盖已经存在的功能。

2. 扩展的语法

使用 extention 关键字定义扩展

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

可以扩展一个已经存在的类型,使它可以适用一个或多个协议:

    extension SomeType: SomeProtocol, AnotherProtocol {
    // 在这里实现协议的方法
    }

3. Computed 属性

扩展可以给一个已存在的类型增加Computed属性,这个Computed属性可以实例属性,也可以是类型属性(computed instance properties and computed type properties)。

下面的例子给Double类型增加了5个computed instance properties:

    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
    println("One inch is \(oneInch) meters")
    // prints "One inch is 0.0254 meters"
    let threeFeet = 3.ft
    println("Three feet is \(threeFeet) meters")
    // prints "Three feet is 0.914399970739201 meters"
    let aMarathon = 42.km + 195.m
    println("A marathon is \(aMarathon) meters long")
    // prints "A marathon is 42195.0 meters long"

注意:扩展只能增加新的Computed属性,不能增加存储属性,不能给已经存在的属性增加属性监视器。

4. 构造器 Initializers

扩展可以给已存在的类型增加构造器。

注意1:扩展只能给类增加 convenience构造器,不能增加 designated构造器,不能增加析构器。designated构造器和析构器必须在类定义时就提供,不能通过扩展提供。

注意2:如果你通过扩展给值类型(即枚举和结构体)提供一个为所有stored属性提供默认值的构造器,并且这个值类型没有定义任何其他的构造器,那么你可以在你扩展的构造器中使用值类型默认的构造器和成员组个初始化构造器。

如下代码定义了几个结构体:

    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为所有属性提供了默认值,所以编译器会自动为他提供一个默认构造器和一个成员逐个初始化构造器(参见十三、初始化):

    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)
      }
    }

那么:

    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)

注意:如果你在扩展中提供构造器,你有责任确保一旦构造器执行完毕,所有的实例被完全初始化了。

5. 方法 Methods

可以在扩展中给已存在的类型增加新的实例方法 instance methods 和 类型方法type methods(比如类方法):

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

上面的函数只有一个参数 task: () -> (),没有返回值。  

调用扩展的方法:

    3.repetitions({
      println("Hello!")
    })
    // Hello!
    // Hello!
    // Hello!

由于参数在最后,可以使用尾部闭包 trailing closure(详见六、闭包) 的写法:

    3.repetitions {
      println("Goodbye!")
    }
    // Goodbye!
    // Goodbye!
    // Goodbye!

6、可变的实例方法 Mutating Instance Methods

扩展中定义的 Instance methods 可以定义为 mutate ,即可以改变它自身。结构体和枚举的方法如果要改变self,或者自己的属性时,必须将方法定义为mutating 。

看如下代码:

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

7、下标 Subscripts

扩展可以给已存在的类型增加下标:

    extension Int {
      subscript(var digitIndex: Int) -> Int {
        var decimalBase = 1
        while digitIndex > 0 {
          decimalBase *= 10
          --digitIndex
        }
        return (self / decimalBase) % 10
      }
    }
    746381295[0]
    // returns 5
    746381295[1]
    // returns 9
    746381295[2]
    // returns 2
    746381295[8]
    // returns 7

8、嵌套类型 Nested Types

扩展可以给一个已存在的类、结构体、枚举增加一个新的嵌套类型:

    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
        }
      }
    }

那么:

    func printIntegerKinds(numbers: [Int]) {
      for number in numbers {
        switch number.kind {
          case .Negative:
            print("- ")
          case .Zero:
            print("0 ")
          case .Positive:
            print("+ ")
        }
      }
      print("\n")
    }
    printIntegerKinds([3, 19, -27, 0, -6, 0, 7])
    // prints "+ + - 0 - 0 +"

因为number.kind已知为Int.kind类型,所以Int.kind的成员的值可以再switch中使用简写形式,比如.Negative,而不是Int.Kind.Negative

参考:https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Extensions.html#//apple_ref/doc/uid/TP40014097-CH24-ID151

时间: 2024-11-05 16:33:58

十九、扩展 Extensions的相关文章

YbSoftwareFactory 代码生成插件【十九】:实体类配合数据库表字段进行属性扩展的小技巧

实体类通常需要和数据库表进行了ORM映射,当你需要添加新的属性时,往往同时也需要在数据库中添加相应的字段并配置好映射关系,同时可能还需对数据访问组件进行重新编译和部署才能有效.而当你开始设计一个通用数据访问组件后,因为项目需求的不同和需求的不断变化演变,很难不能保证不会再添加额外的属性和字段.特别是项目部署运行后,添加一个属性和字段带来的额外维护的工作量可能要远远超过对代码进行调整的工作量.本文提供了属性字段扩展的一种思路,在满足核心字段可通过实体类强类型进行访问的同时,还可通过C# 4.0提供

第十九课预习任务

第十九课预习任务 11.25 配置防盗链11.26 访问控制Directory11.27 访问控制FilesMatch11.28 限定某个目录禁止解析php11.29 限制user_agent11.30/11.31 php相关配置11.32 php扩展模块装安扩展几种限制ip的方法 http://ask.apelearn.com/question/6519apache 自定义header http://ask.apelearn.com/question/830apache的keepalive和k

Android学习笔记二十九之SwipeRefreshLayout、RecyclerView和CardView

Android学习笔记二十九之SwipeRefreshLayout.RecyclerView和CardView 前面我们介绍了AlertDialog和几个常用的Dialog,ProgressDialog进度条提示框.DatePickerDialog日期选择对话框和TimePickerDialog时间选择对话框.这一节我们介绍几个新的API控件SwipeRefreshLayout.RecyclerView和CardView,这几个API控件都是google在Android5.0推出的.下面我们来学

微软云计算介绍与实践(实践之十九)

以下板块的实践着重于如何通过自动化来提升效率,通过自助服务和授权控制,并对基础设施提供统一的管理,这些实践中使用的重要组件为Service Manager.Orchestrator.App Controller 以及 Virtual Machine Manager. 一.微软私有云中的自动化自助服务 关于微软私有云中的自动化自助服务,相关背景知识和产品功能介绍,我就不多说了,贴上几张图,大家看了明白个大概就行. (IT需求和System Center功能) (租户和应用程序所有者的场景) 二.O

QT开发(四十九)——数据库用户接口层

QT开发(四十九)--数据库用户接口层 用户接口层主要包括Qt SQL模块中的QSqlQueryModel.QSqlTableModel.QSqlRelationalTableModel.用户接口层的类实现了将数据库中的数据链接到窗口部件上,是使用模型/视图框架实现的,是更高层次的抽象,即便不熟悉SQL也可以操作数据库.需要注意的是,在使用用户接口层的类之前必须先实例化QCoreApplication对象. QT中使用了自己的机制来避免使用SQL语句,提供了更简单的数据库操作及数据显示模型,分别

Linux运维 第三阶段 (十九) varnish(1)

Linux运维 第三阶段 (十九) varnish 一.相关概念: http/1.0-->http/1.1(重大改进:对缓存功能实现了更精细化的设计) RFC(request file comment,每一种协议都有请求注解文档,讲协议规范) http页面由众多的web object组成,有些是静态,有些是通过程序执行后生成的:为加速web的访问,browser中引入了缓存机制,能将访问的静态内容或可缓存的动态内容缓存到本地,而后client再次到原始server上请求之前相同的内容时,如果原始

设计模式 ( 十九 ):Strategy策略模式 -- 行为型

设计模式 ( 十八 ) 策略模式Strategy(对象行为型) 1.概述 在软件开发中也常常遇到类似的情况,实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能.如查找.排序等,一种常用的方法是硬编码(Hard Coding)在一个类中,如需要提供多种查找算法,可以将这些算法写到一个类中,在该类中提供多个方法,每一个方法对应一个具体的查找算法:当然也可以将这些查找算法封装在一个统一的方法中,通过if…else…或者case等条件判断语句来进行选择.这

全栈JavaScript之路(十九)HTML5 插入 html标记 ( 一 )innerHTML 与outerHTML

在需要给文档插入大量的html 标记下,通过DOM操作很麻烦,你不仅要创建一系列的节点,而且还要小心地按照顺序把它们接结起来. 利用html 标签 插入技术,可以直接插入html代码字符串,简单.高效! 以下插入html标签相关的扩展已经纳入html5 规范. 1.innerHTML 属性 2.outerHTML 属性 3.insertAdjacentHTML 方法 innerHTML 属性 有两种模式,写模式与读模式. 在读模式下,返回的是html 代码字符串. 例如: <div id="

angular学习笔记(十九)

本篇主要介绍angular使用指令修改DOM: 使用angular指令可以自己扩展html语法,还可以做很多自定义的事情.在后面会专门讲解这一块的知识,这一篇只是起到了解入门的作用. 与控制器,过滤器,服务,一样,可以通过模块实例的directive的方法来创建指令: var someModule = angular.module('SomeModule',[]); someModule.directive('directiveName',function(){ return { link: f

NO.145 禅道使用分享第十九期:有效控制项目的风险和成本

关于禅道使用经验分享 禅道使用经验分享,欢迎大家一起来探讨关于禅道项目管理软件使用方面的问题.我们广泛的收集对禅道的看法和建议,充分了解广大用户使用禅道的具体情况和要求,进而不断完善禅道的功能和服务. 想分享你的禅道使用经验,可直接联系先知@禅道,QQ:1292676069. 本期禅道使用经验分享来自于成都锤子工厂(Hammer Studio)的联合创始人罗聪翼.非常感谢他百忙之中接受禅道的采访,分享他和工作团队使用禅道的经验和心得. 成都锤子工厂(Hammer Studio)项目管理情况 Ha