iOS9 with Swift 扩展

扩展:

扩展是将你自己的代码插入到已经存在的对象类型中的方法,即extending an existing object type。这种扩展可以在自己定义的类型中,也可以是Swift本身的对象类型,还可以是Cocoa的对象类型。总之,扩展可以将你的代码打入你的甚至别人的类型中!

扩展的声明必须是在文件的最顶端。声明扩展:关键字extension放在已经存在的对象类型名之前,之后如果要加协议,那么就添加冒号和要添加的协议,之后再加大括号,里面写内容。此处的限制如下:

1、扩展不能重写已经存在的成员。(但是它可以重载已经存在的方法)

2、扩展不能声明存储属性。(但是它可以声明计算属性

3、类的扩展不能声明指定构造器析构器。(但是它可以声明便利构造器

扩展对象类型:

在真实的编程过程中,为了封装一些不存在的函数(通过将它表达为属性或者方法),我有时会扩展Swift内部类型或者Cocoa类型。这是一些真实app的例子:

在某个纸牌游戏中,我需要进行洗牌,而牌储存在数组里面。那么我就扩展了Swift的Array,给它一个shuffle方法:


1

再如Cocoa的Core Graphics 框架有很多很有用的函数与CGRect结构体有关,Swift对它有诸多扩展,不过对于取得CGRect的中心坐标(Center Point : CGPoint),还没有捷径可走。所以我就扩展出了center属性:


2

扩展可以定义static和class方法。由于对象类型常常是全局可用的,所以这不失为是构造一个全局函数的好办法。比如,如果觉得金色会在开发中常常使用到,就没有必要每一次都重复定义,可以将它封装在全局函数中。然而也不需要直接构造一个全局函数,我们可以将他封装在UIColor中:


3

现在你就可以像调用UIColor.redcolor一样,使用UIColor.myGoldenColor来调用颜色了。

另一个扩展很好用的地方就是使Cocoa内置类和你自己的数据类型一起工作。比如在Zotz App中,我定义了一个枚举类型,用来读档存档。


4

其中的问题就是,让我存档时我需要每次调用rawValue。


5

这看起来很丑,所以必须要改。我们可以教会NSCoder(coder的类)怎样去干活儿。在这个扩展中,我重载了encoderObject:forKey" 方法:


6

事实上,我将rawValue的调用从我自己的代码中放进了NSCoder里。现在我就可以这样调用了:


7

扩展还可以整理对象类型的代码。一个常见的应用就是为对象类型增加其采用的协议。比如这样:

8

如果你觉得多行定义你的对象类型太复杂,那么用扩展可以很好地分解代码。

当你对Swift的结构体(Struct)进行扩展的时候,一个有趣的事情发生了:你声明一个构造器,原先的隐式构造器(Implicit initializer)可以保留下来:


9

这段代码意味着你可以通过显式构造器Digit(),也可以用原先的隐式构造器Digit(number:)。这样即使我们显式定义了构造器,也不会使原先的隐式构造器消失。

扩展协议(Extending Protocols):

在Swift2.0中,你可以扩展协议了!

像扩展其他的对象类型一样,你也可以扩展协议的方法和属性。不像协议的声明中方法和属性仅仅是要求采用者实现,扩展协议会是采用者继承该方法和协议,也就是说它们是真正的方法和属性。


10

看上面,Bird可以不用实现fly()就继承Flier。这是因为Flier协议的扩展提供了该方法,Bird实际上继承了fly():


11

采用者可以实现从扩展协议继承来的方法,再重写它:


12

但是请注意:这种继承不是多态的。采用者的实现不是重写;而仅仅是另一个实现。内部识别规则不适用,关键是引用

13

即使f在内部其实是Insect(用is运算符可以知道),fly消息还是会发送给Flier。

为了将它变得看起来像多态继承,我们必须将fly在原协议中定义:


14

现在Insect就保持了它的内部完整:


15

这种差异是有意义的。因为协议采用不会引入动态分配。因此编译器必须进行静态决定。如果在原协议中,要求方法被声明,那么采用者就必须实现它,所以我们就可以调用该方法。但是如果该方法仅仅存在于协议扩展,这就取决于运行的动态分配(Dynamic Dispatch)。这就有可能使得协议落空,所以消息就被发送到协议扩展去了。

协议扩展的主要好处就是允许代码被转移至一个合适的Scope中。这里有一个例子:我有四个枚举类型,每一个都代表了Card的一个属性,Fill、Color、Shape和Number。他们都有一个人int类型的初始值。我对与每次都赋给rawValue有点烦躁了,所以就给每个枚举类型一个没有外部参数名的代理构造器,它们会调用内置的init(rawValue:)构造器:


16

尽管我不喜欢这样的方式,但是在Swift1.2之前我没有任何办法。而现在,我可以用协议扩展来改变这种情况。带有初始值的枚举类型将会自动采用内置的协议 Rawpresentable,初始值的类型别名叫做RawValue。所以我可以将我的构造器加入到此协议中:

17

在Swift标准库中,协议扩展意味着很多全局函数可以被改写为方法。比如,在Swift1.2之前,enumerate是一个全局函数:


18

原先这个函数只能是全局函数。

这个函数只作用于序列(sequence),所以也是SequenceType协议的采用者。在Swift2.0之前,怎样实现呢?enumerate可能在SequenceType协议中被定义(requirement),但是这意味着所有的采用者都要实现它,这显然不可能。所以我们只能把它声明为一个全局函数,然后用泛型约束保证入口,用序列作为参数。

在Swift2.0,enumerate就可以当做方法定义在SequenceType协议中了:


19

现在就不需要什么泛型约束了,也不需要泛型,甚至不需要参数。被发送了enumerate消息的序列就是要飙车enumerate的序列。

这个例子的方法可以被广泛使用。很多Swift标准库的全局函数变成了方法。

扩展泛型:

当你扩展泛型类型的时候,占位符类型名对于你的扩展声明是可见的。你很可能用到它们。不过这会使你的代码变得不清楚,因为看起来你在使用没有被定义过的变量。所以最好加点注释。


20

在Swift2.0中,泛型类型扩展可以包含where语句了。这和泛型约束的效果类似:它限制了决定泛型类型的代码,向编译器保证你的代码是合法的。

通过协议扩展,这意味着全局函数可以用作方法。看下面的这个例子:


21

为什么我要用全局函数呢?因为在Swift2.0之前我不得不这么做。如果我想把它(求最小值)变成Array的一个方法,在Swift1.2之前,我可以扩展Array而且也可以引用Array的泛型占位符,然而它不能进一步限制占位符。所以这里没有办法将此方法插入Array并保证该占位符代表的类型是 Comparable,所以编译器也不允许使用<比较Array的元素。而在Swift2.0就大大不同了。不仅可以更进一步地约束泛型类型,而且还可以将它变成Array的方法。


22

这个方法只能被元素是Comparable的序列调用,否则编译器将拒绝它们。

第二行不编译的原因就是因为我没有使Digit结构体采用Comparable。

同样这个改变也使得Swift的标准库有大规模的改变,允许把全局函数写入结构体扩展或者协议扩展作为方法。比如,全局函数find(在Swift1.2之前)变成了集合类型的indexOf方法,此方法的元素必须是Equatable。


23

这就是协议扩展,同样也是运用where语句的泛型扩展,二者在Swift2.0之前都是不被允许的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容

  • importUIKit classViewController:UITabBarController{ enumD...
    明哥_Young阅读 3,805评论 1 10
  • 136.泛型 泛型代码让你可以写出灵活,可重用的函数和类型,它们可以使用任何类型,受你定义的需求的约束。你可以写出...
    无沣阅读 1,466评论 0 4
  • 扩展 扩展就是向一个已有的类、结构体、枚举类型或者协议类型添加新功能。这包括在没有权限获取原始源代码的情况下扩展类...
    cht005288阅读 465评论 0 0
  • Hello Word 在屏幕上打印“Hello, world”,可以用一行代码实现: 你不需要为了输入输出或者字符...
    restkuan阅读 3,176评论 0 6
  • Swift属性 Swift属性将值跟特定的类,结构体,枚举关联。分为存储属性和计算属性,通常用于特定类型的实例。属...
    小小厨师阅读 854评论 0 0