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

Swift 中的扩展可以:
1.添加计算型属性和计算静态属性
2.定义实例方法和类型方法
3.提供新的构造器
4.定义下标
5.定义和使用新的嵌套类型
6.使一个已有类型符合某个协议

注意:如果你定义了一个扩展向一个已有类型添加新功能,那么这个新功能对该类型的所有已有实例中都是可用的,即使它们是在你的这个扩展的前面定义的。

 一.扩展语法(Extension Syntax)

声明一个扩展使用关键字extension:
extension SomeType {
    // 加到SomeType的新功能写到这里
}

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

extension SomeType: SomeProtocol,AnotherProctocol {
    // 协议实现写到这里
}
按照这种方式添加的协议遵循者(protocol conformance)被称之为在扩展中添加协议遵循者

 二.计算型属性(Computed Properties)
扩展可以向已有类型添加计算型实例属性和计算型类型属性。
Swift 的内建Double类型添加了5个计算型实例属性,从而提供与距离单位协作的基本支持。
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")
// 打印输出:"One inch is 0.0254 meters"
let threeFeet = 3.ft
println("Three feet is \(threeFeet) meters")
// 打印输出:"Three feet is 0.914399970739201 meters"

这些计算属性表达的含义是把一个Double型的值看作是某单位下的长度值。即使它们被实现为计算型属性,但这些属性仍可以接一个带有.语法的浮点型字面值,而这恰恰是使用这些浮点型字面量实现距离转换的方式。
在上述例子中,一个Double型的值1.0被用来表示“1米”。这就是为什么m计算型属性返回self——表达式1.km被认为是计算1.0的Double值(1*1000)这些属性是只读的计算型属性,所有从简考虑它们不用get关键字表示。它们的返回值是Double型,而且可以用于所有接受Double的数学计算中:

let aMarathon = 42.km + 195.m
println("A marathon is \(aMarathon) meters long")
// 打印输出:"A marathon is 42195.0 meters long"
注意:扩展可以添加新的计算属性,但是不可以添加存储属性,也不可以向已有属性添加属性观测器(property observers)。
    
 三.构造器(Initializers)
扩展可以向已有类型添加新的构造器。这可以让你扩展其它类型,将你自己的定制类型作为构造器参数,或者提供该类型的原始实现中没有包含的额外初始化选项。扩展能向类中添加新的便利构造器,但是它们不能向类中添加新的指定构造器或析构函数。指定构造器和析构函数必须总是由原始的类实现来提供。
注意:如果你使用扩展向一个值类型添加一个构造器,在该值类型已经向所有的存储属性提供默认值,而且没有定义任何定制构造器(custom initializers)时,你可以在值类型的扩展构造器中调用默认构造器(default initializers)和逐一成员构造器(memberwise initializers)。正如在值类型的构造器代理中描述的,如果你已经把构造器写成值类型原始实现的一部分,上述规则不再适用。
下面的例子定义了一个用于描述几何矩形的定制结构体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的原点是 (2.5,2.5),大小是 (3.0,3.0)

注意:如果你使用扩展提供了一个新的构造器,你依旧有责任保证构造过程能够让所有实例完全初始化。

 四.方法(Methods)
扩展可以向已有类型添加新的实例方法和类型方法。下面的例子向Int类型添加一个名为repetitions的新实例方法:
extension Int {
    func repetitions(task: () -> ()) {
        for i in 0..<self {
            task()
        }
    }
    func redefine(task:()->()){
        for i in 0..<self{
            task()
        }
    }
}
//这个repetitions方法使用了一个() -> ()类型的单参数(single argument),表明函数没有参数而且没有返回值。
//定义该扩展之后,你就可以对任意整数调用repetitions方法,实现的功能则是多次执行某任务:

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

//可以使用 trailing 闭包使调用更加简洁:

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

 五.修改实例方法(Mutating Instance Methods)

通过扩展添加的实例方法也可以修改该实例本身。结构体和枚举类型中修改self或其属性的方法必须将该实例方法标注为mutating,正如来自原始实现的修改方法一样。
下面的例子向Swift的Int类型添加了一个新的名为square的修改方法,来实现一个原始值的平方计算:
extension Int {
    // 平方
    mutating func square() {
        self = self * self
    }
    // 立方
    mutating func cube(){
        self=self*self*self
    }
    
}
var someInt = 3
someInt.square()
someInt.cube()
// someInt 现在值是 9

 六.下标(Subscripts)
扩展可以向一个已有类型添加新下标。这个例子向Swift内建类型Int添加了一个整型下标。该下标[n]返回十进制数字从右向左数的第n个数字
123456789[0]返回9
123456789[1]返回8

extension Int {
    // digit 数字
    subscript(var digitIndex: Int) -> Int {
        
        // decimal 小数的,十进位的
        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

注意点:如果该Int值没有足够的位数,即下标越界,那么上述实现的下标会返回0,因为它会在数字左边自动补0:
746381295[9]
//returns 0,即等同于:
0746381295[9]

 七.嵌套类型(nested Types)

扩展可以向已有的类、结构体和枚举添加新的嵌套类型:
extension Character {
    enum Kind {
        case Vowel,Consonant,Other
    }
    var kind: Kind {
        switch String(self).lowercaseString {
        case "a","e","i","o","u":
            return .Vowel
        case "b","c","d","f","g","h","j","k","l","m","n","p","q","r","s","t","v","w","x","y","z":
            return .Consonant
        default:
            return .Other
        }
    }
}
该例子向Character添加了新的嵌套枚举。这个名为Kind的枚举表示特定字符的类型。具体来说,就是表示一个标准的拉丁脚本中的字符是元音还是辅音(不考虑口语和地方变种),或者是其它类型。
这个例子还向Character添加了一个新的计算实例属性,即kind,用来返回合适的Kind枚举成员。
现在,这个嵌套枚举可以和一个Character值联合使用了:
func printLetterKinds(word: String) {
    println("'\(word)' is made up of the following kinds of letters:")
    for character in word {
        switch character.kind {
        case .Vowel:
            print("vowel ")
        case .Consonant:
            print("consonant ")
        case .Other:
            print("other ")
        }
    }
    print("\n")
}
printLetterKinds("Hello")
// 'Hello' is made up of the following kinds of letters:
// consonant vowel consonant consonant vowel

函数printLetterKinds的输入是一个String值并对其字符进行迭代。在每次迭代过程中,考虑当前字符的kind计算属性,并打印出合适的类别描述。所以printLetterKinds就可以用来打印一个完整单词中所有字母的类型,正如上述单词"hello"所展示的。
注意:由于已知character.kind是Character.Kind型,所以Character.Kind中的所有成员值都可以使用switch语句里的形式简写,比如使用 .Vowel代替Character.Kind.Vowel

五 Swift学习之扩展(Extension)的更多相关文章

  1. 寒城攻略:Listo 教你 25 天学会 Swift 语言 - 22 Extensions

    //***********************************************************************************************//1.Extensions(扩展)//___________________________________________________________________________________

  2. 【Swift初见】Swift构造过程

    构造过程是通过构造器来实现的,其实每个构造器就可以看作是一个函数,只是这个函数是为了执行初始化的。每个类都必须拥有一个指定构造器。

  3. Swift 2

    letlabel="Thewidthis"letwidth=94letwidthLabel=label+String练习:删除最后一行中的String,错误提示是什么?你可以一起使用if和let来处理值缺失的情况。switch支持任意类型的数据以及各种比较操作——不仅仅是整数以及测试相等。运行switch中匹配到的子句之后,程序会退出switch语句,并不会继续向下运行,所以不需要在每个子句结尾写break。

  4. swift之inherit

    新的Bicycle类自动获得Vehicle类的特性,比如maxPassengers和numberOfWheels属性。Car重写了继承来的description方法,它的声明与Vehicle中的description方法一致,声明前面加上了override关键字。Car中的description方法并非完全自定义,而是通过super.desc

  5. swift之constructor

    所以我们将属性回答response声明为String?当SurveyQuestion实例化时,它将自动赋值为空nil,表明暂时还不存在此字符串。下面例子中创建了一个类ShoppingListItem,它封装了购物清单中的某一项的属性:名字、数量和购买状态purchasestate。上面例子中使用默认构造器创造了一个ShoppingListItem类的实例,并将其赋值给变量item。Swift可以根据这两个属性的初始赋值0.0自动推导出它们的类型Double。

  6. Swift教程16-使用Swift调用AFNetworking进行网络请求

    如何使用Swift进行网络请求呢?

  7. 五 Swift学习之扩展(Extension)

  8. Swift 构造器探究

    Swift构造器探究什么时候要用构造器?然而Swift的构造器又有两种,一种是designated构造器,一种是convenience构造器。所有designated构造器都必须初始化那些没有满足第一种情况的存储属性。具体怎么做请看后文Designated构造器对于结构体Swift中结构体和类的构造器其实差不多。构造器的继承designated构造器designated构造器在Swift中很常见,顾名思义这个构造器就是你类中所有构造器的“原型”。在这个构造器中只调用父类的designated构造器或者不调

  9. swift1.2新增和改动

    Swift语言的改变“确保转换”和“可失败转换”的概念现在被分为两个操作符。从桥接Objective-C类到它Swift中值类型的隐式转化被移除。这将是Swift的类型系统更加简单和可预测。柯里化函数现在可以指定参数标签了:Swift现在可以检测在Swift类型系统中覆盖和重写的差异以及通过Objective-C运行时可见的影响。

  10. Swift学习笔记-面向对象

随机推荐

  1. Swift UITextField,UITextView,UISegmentedControl,UISwitch

    下面我们通过一个demo来简单的实现下这些控件的功能.首先,我们拖将这几个控件拖到storyboard,并关联上相应的属性和动作.如图:关联上属性和动作后,看看实现的代码:

  2. swift UISlider,UIStepper

    我们用两个label来显示slider和stepper的值.再用张图片来显示改变stepper值的效果.首先,这三个控件需要全局变量声明如下然后,我们对所有的控件做个简单的布局:最后,当slider的值改变时,我们用一个label来显示值的变化,同样,用另一个label来显示stepper值的变化,并改变图片的大小:实现效果如下:

  3. preferredFontForTextStyle字体设置之更改

    即:

  4. Swift没有异常处理,遇到功能性错误怎么办?

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  5. 字典实战和UIKit初探

    ios中数组和字典的应用Applicationschedule类别子项类别名称优先级数据包contactsentertainment接触UIKit学习用Swift调用CocoaTouchimportUIKitletcolors=[]varbackView=UIView(frame:CGRectMake(0.0,0.0,320.0,CGFloat(colors.count*50)))backView

  6. swift语言IOS8开发战记21 Core Data2

    上一话中我们简单地介绍了一些coredata的基本知识,这一话我们通过编程来实现coredata的使用。还记得我们在coredata中定义的那个Model么,上面这段代码会加载这个Model。定义完方法之后,我们对coredata的准备都已经完成了。最后强调一点,coredata并不是数据库,它只是一个框架,协助我们进行数据库操作,它并不关心我们把数据存到哪里。

  7. swift语言IOS8开发战记22 Core Data3

    上一话我们定义了与coredata有关的变量和方法,做足了准备工作,这一话我们来试试能不能成功。首先打开上一话中生成的Info类,在其中引用头文件的地方添加一个@objc,不然后面会报错,我也不知道为什么。

  8. swift实战小程序1天气预报

    在有一定swift基础的情况下,让我们来做一些小程序练练手,今天来试试做一个简单地天气预报。然后在btnpressed方法中依旧增加loadWeather方法.在loadWeather方法中加上信息的显示语句:运行一下看看效果,如图:虽然显示出来了,但是我们的text是可编辑状态的,在storyboard中勾选Editable,再次运行:大功告成,而且现在每次单击按钮,就会重新请求天气情况,大家也来试试吧。

  9. 【iOS学习01】swift ? and !  的学习

    如果不初始化就会报错。

  10. swift语言IOS8开发战记23 Core Data4

    接着我们需要把我们的Rest类变成一个被coredata管理的类,点开Rest类,作如下修改:关键字@NSManaged的作用是与实体中对应的属性通信,BinaryData对应的类型是NSData,CoreData没有布尔属性,只能用0和1来区分。进行如下操作,输入类名:建立好之后因为我们之前写的代码有些地方并不适用于coredata,所以编译器会报错,现在来一一解决。

返回
顶部