iOS开发系列--Swift语言

概述

Swift是苹果2014年推出的全新的编程语言,它继承了C语言、ObjC的特性,且克服了C语言的兼容性问题。Swift发展过程当中不只保留了ObjC不少语法特性,它也借鉴了多种现代化语言的特色,在其中你能够看到C#、Java、Javascript、Python等多种语言的影子。同时在2015年的WWDC上苹果还宣布Swift的新版本Swift2.0,并宣布稍后Swift即将开源,除了支持iOS、OS X以外还将支持linux。linux

本文将继续iOS开发系列教程,假设读者已经有了其余语言基础(强烈建议初学者从本系列第一章开始阅读,若是您但愿从Swift学起,那么推荐你首先阅读苹果官方电子书《the swift programming language》),不会从零基础一点点剖析这门语言的语法,旨在帮助你们快速从ObjC快速过分到Swift开发中。即使如此,要尽量全面的介绍Swift的语法特色也不是一件容易的事情,所以本文将采用较长的篇幅进行介绍。spring

  1. 基础部分
    1. 第一个Swift程序
    2. 数据类型
      1. 基础类型
      2. 集合类型
      3. 元组
      4. 可选类型
    3. 运算符
    4. 控制流
  2. 函数和闭包
    1. 函数
    2. 闭包
    1. 属性
    2. 方法
    3. 下标脚本
    4. 继承
  3. 协议
  4. 扩展
  5. 枚举和结构体
    1. 结构体
    2. 枚举
  6. 泛型

基础部分

第一个Swift程序

建立一个命令行程序以下:编程

import Foundation

/**
*  Swift没有main函数,默认从top level code的上方开始自上而下执行(所以不能有多个top level代码)
*/
println("Hello, World!")

从上面的代码能够看出:swift

  1. Swift没有main函数,从top level code的上方开始往下执行(就是第一个非声明语句开始执行[表达式或者控制结构,类、结构体、枚举和方法等属于声明语句]),不能存在多个top level code文件(不然编译器没法肯定执行入口,事实上swift隐含一个main函数,这个main函数会设置并调用全局 “C_ARGC C_ARGV”并调用由top level code构成的top_level_code()函数);
  2. Swift经过import引入其余类库(和Java比较像);
  3. Swift语句不须要双引号结尾(尽管加上也不报错),除非一行包含多条语句(和Python有点相似);

数据类型

Swift包含了C和ObjC语言中的全部基础类型,Int整形,Float和Double浮点型,Bool布尔型,Character字符型,String字符串类型;固然还包括enum枚举、struct结构体构造类型;Array数组、Set集合、Dictionary字典集合类型;不只如此还增长了高阶数据类型元组(Tuple),可选类型(Optinal)。数组

基础类型

Xcode 从6.0开始加入了Playground代码测试,能够实时查看代码执行结果,下面使用Playground简单演示一下Swift的基础内容,对Swift有个简单的认识:安全

import Foundation

var a:Int=1 //经过var定义一个变量

//下面变量b虽然没有声明类型,可是会自动进行类型推断,这里b推断为Int类型
var b=2

var c:UInt=3
let d=a+b //经过let定义一个变量

//下面经过"\()"实现了字符串和变量相加(字符串插值),等价于println("d="+String(d))
println("d=\(d)") //结果:d=3

//注意因为Swift是强类型语言,a是Int类型而c是UInt类型,两者不能运算,下面的语句报错;可是注意若是是相似于:let a=1+2.0是不会报错的,由于两个都是字面量,Swift会首先计算出结果再推断a的类型
//let e=a+c

//Int.max是Int类型的最大值,相似还有Int.min、Int32.max、Int32.min等
let e=Int.max //结果:9223372036854775807

var f:Float=1.0
var g=2.0 //浮点型自动推断为Double类型

var h:String="hello "

//emoj表情也能够做为变量或者常量,事实上全部Unicode字符都是能够的
var 💖🍎="love and apple"

//两个字符串相加,可是注意不一样类型不能相加
var i=h+💖🍎 //结果:hello love and apple

//布尔类型只有两个值true、false,相似于if语句中的条件只能是布尔类型不能像ObjC同样非0即真
var j:Bool=true

//字符类型,一样使用双引号,可是只能是一个字符,若是不指定类型则"c"默认会推断为字符串(var k:Character="c"是字符类型,可是var k="c"是字符串类型)
var k:Character="c"

var l=00100 //等于100,能够在前面添加额外的0
var m=10_000_000 //等于10000000,可使用增长额外的下划线方便阅读而不改变值的大小
  1. Swift经过var进行变量定义,经过let进行常量定义(这和其余高级语言比较相似,例如F#);
  2. Swift添加了类型推断,对于赋值的常量或者变量会自动推断其具体类型;
  3. Swift是强类型语言(应该说它比C#、Java等强类型语言控制还要严格),不一样的数据类型之间不能隐式转化,若是须要转化只能强制转化;
  4. 在Swift中类型转换直接经过其类型构造函数便可,下降了API的学习成本;

集合类型

Swift提供了三种集合类型:数组Array、集合Set、字典Dictionary。和ObjC不一样的是,因为Swift的强类型,集合中的元素必须是同一类型,而不能像ObjC同样能够存储任何对象类型,而且注意Swift中的集合是值类型而非引用类型(事实上包括String、结构体struct、枚举enum都是值类型)。闭包

首先看一下Swift中的数组:app

//声明数组的时候必须肯定其类型,下面使用[String]声明一个字符串数组([String]是Array<String>简单表达形式)
//var a:Array<String>=["hello","world"]
var a:[String]=["hello","world"]
a[0] //访问数组元素

//下面建立一个Double类型的数组,这里没有使用字面量,当前是一个空数组,固然也能够写成var b:[Double]=[]
var b=[Double]()

for i in a{
    println("i=\(i)")
}

//添加元素,Swift中可变类型再也不由单独的一个类型来表示,通通使用Array,若是想声明为不可变数组只要使用let定义便可
a.append("!")

a+=["I" ,"am" ,"Kenshin"] //追加元素

println("a.count=\(a.count)") //结果:a.count=6

a[3...5]=["I","Love","Swift"] //修改元素,可是注意没法用这种方式添加元素
//a[6]=["."]//这种方式是错误的

a.insert("New", atIndex: 5) //插入元素:hello world! I Love New Swift

a.removeAtIndex(5) //删除指定元素

//使用全局enumerate函数遍历数据索引和元素
for (index,element) in enumerate(a){
    println("index=\(index),element=\(element)")
}

//使用构造函数限制数组元素个数而且指定默认值,等价于var c=Array(count: 3, repeatedValue: 1),自动推断类型
var c=[Int](count: 3, repeatedValue: 1)

Set表示没有顺序的集合:编程语言

//注意集合没有相似于数组的简化形式,例如不能写成var a:[String]=["hello","world"]
var a:Set<String>=["hello","world"]
var b:Set=[1,2] //类型推断:Set<Int>

a.insert("!") //注意这个插入不保证顺序

if !a.isEmpty { //判断是否为空
    a.remove("!")
}

if !a.contains("!"){
    a.insert("!")
}

Dictionary字典一样是没有顺序的,而且在Swift中字典一样要在使用时明确具体的类型。和ObjC中同样,字典必须保证key是惟一的,而这一点就要求在Swift中key必须是可哈希的,不过幸运的是Swift中的基本类型(如Int、Float、Double、Bool、String)都是可哈希的,均可以做为key。ide

//经过字面量进行字典初始化,注意等价于var a:Dictionary<Int,String>=[200:"success",404:"not found"]
var a:[Int:String]=[200:"success",404:"not found"]
var b=[200:"success",404:"not found"] //不声明类型,根据值自动推断类型

a[200] //读取字典
a[404]="can not found" //修改

a[500]="internal server error" //添加
//a=[:] //设置为空字典,等价于:a=[Int:String]()

for code in a.keys{
    println("code=\(code)")
}

for description in a.values{
    println("description=\(description)")
}

for (code,description) in a{
    println("code=\(code),description=\(description)")
}

注意:在Swift中集合的可变性不是像ObjC同样由单独的数据类型来控制的,而是经过变量和常量来控制,这一点和其余高级语言比较相似。

元组(Tuple)

在开发过程当中有时候会但愿临时组织一个数据类型,此时可使用一个结构体或者类,可是因为这个类型并无那么复杂,若是定义起来又比较麻烦,此时能够考虑使用元组。

/**
 * 元组的基本用法
 */
var point=(x:50,y:100) //自动推断其类型:(Int,Int)
point.x //能够用相似于结构体的方式直接访问元素,结果:50
point.y //结果:100
point.0 //也能够采用相似数组的方式使用下标访问,结果:50
point.1 //结果:100

//元组也能够不指定元素名称,访问的时候只能使用下标
let frame:(Int,Int,Int,Float)=(0,0,100,100.0)
println(frame) //结果:(0, 0, 100, 100.0)

//注意下面的语句是错误的,若是指定了元组的类型则没法指定元素名称
//let frame:(Int,Int,Int,Int)=(x:0,y:0,width:100,height:100)


var size=(width:100,25) //仅仅给其中一个元素命名
size.width //结果:100
size.1 //结果:25


var httpStatus:(Int,String)=(200,"success") //元组的元素类型并不必定相同

var (status,description)=httpStatus //一次性赋值给多个变量,此时status=200,description="success"

//接收元组的其中一个值忽略另外一个值使用"_"(注意在Swift中不少状况下使用_忽略某个值或变量)
var (sta,_)=httpStatus
println("sta=\(sta)") //结果:sta=200

/**
 * 元组做为函数的参数或返回值,借助元组实现了函数的多个返回值
 */
func request()->(code:Int,description:String){
    return (404,"not found")
}
var result=request()
result.0 //结果:404
result.1 //结果:not found
result.code //结果:404
result.description //结果:not found

可选类型

所谓可选类型就是一个变量或常量可能有值也可能没有值则设置为可选类型。在ObjC中若是一个对象类型没有赋值,则默认为nil,同时nil类型也只能做为对象类型的默认值,对于相似于Int等基本类型则对应0这样的默认值。因为Swift是强类型语言,若是在声明变量或常量时没有进行赋值,Swift并不会默认设置初值(这一点和其余高级语言不太同样,例如C#虽然也有可选类型,可是要求并无那么严格)。

/**
 * 可选类型基础
 */
var x:Float? //使用?声明成一个可选类型,若是不赋值默认为nil
x=172.0

var y:Float=60.0

//var z=x+y //注意此句报错,由于Int和Int?根本就是两种不一样的类型,在Swift中两种不一样的类型不能运算(由于不会自动进行类型转化)
var z=x!+y //使用!进行强制解包

var age="29"
var ageInt=age.toInt() //注意ageInt是Int可选类型而不是Int类型(由于String的toInt()方法并不能保证其必定能转化为Int类型)
  1. Swift中相似于Int和Int?并非同一种类型,不能进行相关运算,若是要运算只能解包;
  2. 可选类型其本质就是此类型内部存储分为“Some”和“None”两个部分,若是有值则存储到“Some”中,没有值则为“None”(早期Playground中能够看到两个部分,现在已经取消显示Some等描述了),使用感叹号强制解包的过程就是取出“Some”部分;

既然可选类型有可能有值,也可能没有值那么每每有时候就须要判断。可使用if直接判断一个可选类型是否为nil,这样一来就能够根据状况进行强制解包(从Some部分取出值的过程);另外一个选择就是在判断的同时若是有值则将值赋值给一个临时变量或常量,不然不进入此条件语句,这个过程称之为“可选绑定”。

/**
* 可选类型判断
*/
var age="29"
var ageInt=age.toInt() //注意ageInt是Int可选类型而不是Int类型(由于String的toInt()方法并不能保证其必定能转化为Int类型)

if ageInt==nil {
    println("ageInt=nil")
}else{
    println("ageInt=\(ageInt!)") //注意这里使用感叹号!强制解析
}

/**
 * 可选类型绑定
 * 若是可选类型有值则将值赋值给一个临时变量或者常量(此时此变量或者常量接受的值已经不是可选类型),若是没有值则不执行此条件
 */
if let newAge=ageInt{ //此时newAge能够定义成常量也能够定义成变量
    println("newAge=\(newAge)") //注意这里并不须要对newAge强制解包
}else{
    println("ageInt=nil")
}

经过前面的演示能够看出Swift中的可选绑定若是实际计算不得不进行强制解包,若是一个可选类型从第一次赋值以后就能保证有值那么使用时就没必要进行强制解包了,这种状况下可使用隐式可选解析类型(经过感叹号声明而不是问号)

/**
* 隐式解析可选类型
*/
var age:Int!=0 //经过感叹号声明隐式解析可选类型,此后使用时虽然是可选类型可是不用强制解包
age=29
var newAge:Int=age //不用强制解包直接赋值给Int类型(程序会自动解包)

if var tempAge=age {
    println("tempAge=\(tempAge)")
}else{
    println("age=nil")
}

运算符

Swift中支持绝大多数C语言的运算符并改进以减小没必要要的错误(例如等号赋值后不返回值),算术运算会检查溢出状况,必要时还能使用新增的溢出运算符。另外Swift中还能够对浮点数使用取余运算符,新增了区间运算符。对于基本的运算符这里再也不一一介绍,简单看一下Swift中的区间运算符和溢出运算符。

/**
 * 区间运算符,一般用于整形或者字符范围(例如"a"..."z")
 */
for i in 1...5 { //闭区间运算符...(从1到5,包含5)
    println("i=\(i)")
}

for i in 1..<5{ //半开区间运算符..<(从1到4)
    println("i=\(i)")
}

var str = "hello world."
var range="a"..."z"
for t in str {
    if range.contains(String(t)) {
        print(t) //结果:helloworld
    }
}

/**
 * 溢出运算符
 */
var a=UInt8.max //a=255
//var b:UInt8=a+1 //注意b会出现溢出,此句报错

//下面使用溢出运算符,结果为:0,相似的还有&-、&*、&/
//使用溢出运算符能够在最大值和最小值以前循环而不会报错
var b:UInt8=a &+ 1

 溢出运算符的原理其实很简单,例如对于UInt8,若是8位均为1则十进制表示是255,可是当加1以后则变成了9位“100000000”,出现了溢出可是UInt8自己值只能存储8位,因此取后面8位就变成了“00000000”,十进制表示就是0。

 控制流

Swift中的多数控制流和其余语言差异并不大,例如for、while、do while、if、switch等,并且有些前面已经使用过(例如for in循环),这里将着重介绍一些不一样点。

var a=["a","b","c","d","e","f","g"]
let b=a[1]
/**
*  switch支持一个case多个模式匹配,同时case后不用写break也会在匹配到种状况后自动跳出匹配,不存在隐式贯穿,若是想要贯穿在case以后添加"fallthrough"关键字
*/
switch b{
case "a","b":
    println("b=a or b=b")
case "c","d","e","f":
    println("b in (c,d,e,f)")
default:
    println("b=g")
}

/**
 * 匹配区间,同时注意switch必须匹配全部状况,不然必须加上default
 */

let c:Int=88
switch c{
case 1...60:
    println("1-60")
case 61...90:
    println("61-90")
case 91...100:
    println("91-100")
default:
    println("1>c>100")
}

/**
*  元组匹配、值绑定、where条件匹配
*  注意下面的匹配没有default,由于它包含了全部状况
*/
var d=(x:900,y:0)
switch d{
case (0,0):
    println("d in (0,0)")
case (_,0): //忽略x值匹配
    println("d in y")
case (0,let y)://值绑定
    println("d in x,y=\(y)")
case (-100...100,-100...100): //注意这里有可能和第1、2、三个条件重合,可是Swift容许多个case匹配同一个条件,可是只会执行第一个匹配
    println("x in(0-100),y in (0-100)")
case let (x,y) where x==y: //where条件匹配,注意这里的写法等同于:(let x,let y) where x==y
    println("x=y=\(x)")
case let (x, y):
    println("x=\(x),y=\(y)")
    
}

在其余语言中一般可使用break、continue、return(Swift中添加了fallthrough)等来终止或者跳出某个执行语句,可是对于其行为每每是具备固定性的,例如break只能终止其所在的内层循环,而return只能跳出它所在的函数。在Swift中这种控制转移功能获得了增强,那就是使用标签。利用标签你能够随意指定转移的位置,例以下面的代码演示了如何直接经过标签跳出最外层循环:

var a=5
whileLoop:
while --a>0 {
    for var i=0;i<a;++i{
        println("a=\(a),i=\(i)")
        
        break whileLoop
        //若是此处直接使用break将跳出for循环,而因为这里使用标签直接跳出了while,结果只会打印一次,其结果为:a=4,i=0
    }
}

函数和闭包

函数

函数是一个完成独立任务的代码块,Swift中的函数不只能够像C语言中的函数同样做为函数的参数和返回值,并且还支持嵌套,而且有C#同样的函数参数默认值、可变参数等。

//定义一个函数,注意参数和返回值,若是没有返回值能够不写返回值或者写成Void、空元组()(注意Void的本质就是空元组)
func sum(num1:Int,num2:Int)->Int{
    return num1 + num2
}

sum(1, 2)

能够看到Swift中的函数仅仅表达形式有所区别(定义形式相似于Javascript,可是js不用书写返回值),可是本质并无太大的区别。不过Swift中对函数参数强调两个概念就是局部参数名(又叫“形式参数”)和外部参数名,这极大的照顾到了ObjC开发者的开发体验。在上面的例子中调用sum函数并无传递任何参数名,由于num一、num2仅仅做为局部参数名在函数内部使用,可是若是给函数指定一个外部参数名在调用时就必须指定参数名。另外前面也提到关于Swift中的默认参数、可变长度的参数,包括一些高级语言中的输入输出参数,经过下面的例子你们会有一个全面的了解。

/**
/**
*  函数参数名分为局部参数名和外部参数名
*/
func split(string a:String,seperator b:Character)->[String]{
    return split(a, maxSplit: Int.max, allowEmptySlices: false, isSeparator: {$0==b})
}

//因为给split函数设置了外部参数名string和seperator,因此执行的时候必须带上外部参数名,此处能够看到一个有意义的外部参数名大大节省开发者使用成本
split(string: "hello,world,!", seperator: ",") //结果:["hello", "world", "!"]

//下面经过在局部参数名前加上#来简写外部参数名(此时局部参数名和外部参数名相同)
func split2(#string:String,#seperator:Character)->[String]{
    return split(string, maxSplit: Int.max, allowEmptySlices: false, isSeparator: {$0==seperator})
}
split2(string: "hello,world,!", seperator: ",")

//上面的split函数的最后一个参数默认设置为",",注意若是使用默认参数那么此参数名将默认做为外部参数名(此时局部参数名和外部参数名相同)
func split3(#string:String,seperator:Character=",")->[String]{
    return split(string, maxSplit: Int.max, allowEmptySlices: false, isSeparator: {$0==seperator})
}
split3(string: "hello,world,!", seperator: ",") //结果:["hello", "world", "!"]
split3(string: "hello world !", seperator: " ") //结果:["hello", "world", "!"]

//可是若是有默认值,又不想指定局部参数名可使用“_”取消外部参数名
func split4(string:String,_ seperator:Character=",")->[String]{
    return split(string, maxSplit: Int.max, allowEmptySlices: false, isSeparator: {$0==seperator})
}
split4("hello,world,!", ",") //结果:["hello", "world", "!"]

/**
 * 可变参数,一个函数最多有一个可变参数而且做为最后一个参数
 * 下面strings参数在内部是一个[String],对于外部是不定个数的String参数
 */
func joinStr(seperator:Character=",",strings:String...)->String{
    var result:String=""
    for var i=0;i<strings.count;++i{
        if i != 0{
            result.append(seperator)
        }
        result+=strings[i]
    }
    return result
}

joinStr(seperator:" ", "hello","world","!") //结果:"hello world !"

/**
 * 函数参数默认是常量,不能直接修改,经过声明var能够将其转化为变量(可是注意C语言参数默认是变量)
 * 可是注意这个变量对于外部是无效的,函数执行完就消失了
 */
func sum2(var num1:Int,num2:Int)->Int{
    num1 = num1 + num2
    return num1
}

sum2(1, 2) //结果:3

/**
 *  输入输出参数
 *  经过输入输出参数能够在函数内部修改函数外部的变量(注意调用时不能是常量或字面量)
 *  注意:下面的swap仅仅为了演示,实际使用时请用Swift的全局函数swap
 */
func swap(inout a:Int ,inout b:Int){
    a=a+b
    b=a-b
    a=a-b
}

var a=1,b=2
swap(&a, &b) //调用时参数加上“&”符号
println("a=\(a),b=\(b)") //结果:"a=2,b=1"

 和不少语言同样,Swift中的函数自己也能够看作一种类型,既能够做为参数又能够做为返回值。

/**
 * 函数类型
 */
var sum3=sum //自动推断sum3的类型:(Int,Int)->Int,注意不一样的函数类型之间不能直接赋值
sum3(1,2) //结果:3

//函数做为返回值
func fn()->(Int,Int)->Int{
    //下面的函数是一个嵌套函数,做用因而在fn函数内部
    func minus(a:Int,b:Int)->Int{
        return a-b
    }
    return minus;
}
var minus=fn()

//函数做为参数
func caculate(num1:Int,num2:Int,fn:(Int,Int)->Int)->Int{
    return fn(num1,num2)
}

caculate(1, 2, sum) //结果:3
caculate(1,2, minus) //结果:-1

闭包

Swift中的闭包其实就是一个函数代码块,它和ObjC中的Block及C#、Java中的lambda是相似的。闭包的特色就是能够捕获和存储上下文中的常量或者变量的引用,即便这些常量或者变量在原做用域已经被销毁了在代码块中仍然可使用。事实上前面的全局函数和嵌套函数也是一种闭包,对于全局函数它不会捕获任何常量或者变量,而对于嵌套函数则能够捕获其所在函数的常量或者变量。一般咱们说的闭包更多的指的是闭包表达式,也就是没有函数名称的代码块,所以也称为匿名闭包。

在Swift中闭包表达式的定义形式以下:

{ ( parameters ) -> returnType in

    statements

}

下面经过一个例子看一下如何经过闭包表达式来简化一个函数类型的参数,在下面的例子中闭包的形式也是一而再再而三的被简化,充分说明了Swift语法的简洁性:

func sum(num1:Int,num2:Int)->Int{
    return num1 + num2
}

func minus(num1:Int,num2:Int)->Int{
    return num1 - num2
}

func caculate(num1:Int,num2:Int,fn:(Int,Int)->Int)->Int{
    return fn(num1,num2)
}

var (a,b)=(1,2)

caculate(a, b, sum) //结果:3
caculate(a, b, minus) //结果:-1

//利用闭包表达式简化闭包函数
caculate(a, b, {(num1:Int,num2:Int)->Int in
    return num1 - num2
}) //结果:-1

//简化形式,根据上下文推断类型而且对于单表达式闭包(只有一个语句)能够隐藏return关键字
caculate(a, b, { num1,num2 in
    num1 - num2
}) //结果:-1

//再次简化,使用参数名缩写,使用$0...$n表明第n个参数,而且此in关键字也省略了
caculate(a, b, {
    $0 - $1
}) //结果:-1

考虑到闭包表达式的可读取性,Swift中若是一个函数的最后一个参数是一个函数类型的参数(或者说是闭包表达式),则能够将此参数写在函数括号以后,这种闭包称之为“尾随闭包”。

func sum(num1:Int,num2:Int)->Int{
    return num1 + num2
}

func minus(num1:Int,num2:Int)->Int{
    return num1-num2
}

func caculate(num1:Int,num2:Int,fn:(Int,Int)->Int)->Int{
    return fn(num1,num2)
}

var (a,b)=(1,2)

//尾随闭包,最后一个参数是闭包表达式时能够卸载括号以后,同时注意若是这个函数只有一个闭包表达式参数时能够连通括号一块省略
//请注意和函数定义进行区分
caculate(a, b){
    $0 - $1
} //结果:-1

前面说过闭包之因此称之为“闭包”就是由于其能够捕获必定做用域内的常量或者变量进而闭合并包裹着。

func add()->()->Int{
    var total=0
    var step=1
    func fn()->Int{
        total+=step
        return total
    }
    return fn
}

//fn捕获了total和step,尽管下面的add()执行完后total和step被释放,可是因为fn捕获了两者的副本,因此fn会随着两个变量的副本一块儿被存储
var a=add()
a() //结果:1
a() //结果:2,说明a中保存了total的副本(不然结果会是1)

var b=add()
b() //结果:1 ,说明a和b单独保存了total的副本(不然结果会是3)

var c=b
c() //结果:2,说明闭包是引用类型,换句话说函数是引用类型(不然结果会是1)

Swift会自动决定捕获变量或者常量副本的拷贝类型(值拷贝或者引用拷贝)而不须要开发者关心,另外被捕获的变量或者常量的内存管理一样是由Swift来管理,例如当上面的函数a再也不使用了那么fn捕获的两个变量也就释放了。

做为一门面向对象语言,类固然是Swift中的一等类型。首先经过下面的例子让你们对Swift的class有一个简单的印象,在下面的例子中能够看到Swift中的属性、方法(包括构造方法和析构方法):

//Swift中一个类能够不继承于任何其余基类,那么此类自己就是一个基类
class Person {
    //定义属性
    var name:String
    var height=0.0
    
    //构造器方法,注意若是不编写构造方法默认会自动建立一个无参构造方法
    init(name:String){
        self.name=name
    }
    
    //定义方法
    func showMessage(){
        println("name=\(name),height=\(height)")
    }
    
    //析构方法,在对象被释放时调用,相似于ObjC的dealloc,注意此函数没有括号,没有参数,没法直接调用
    deinit{
        println("deinit...")
    }
    
}


var p=Person(name: "Kenhin")
p.height=172.0
p.showMessage() //结果:name=Kenhin,height=172.0

//类是引用类型
var p2 = p
p2.name = "Kaoru"
println(p.name) //结果:Kaoru
if p === p2 { //“===”表示等价于,这里不能使用等于“==”(等于用于比较值相等,p和p2是不一样的值,只是指向的对象相同)
    println("p===p2") //p等价于p2,两者指向同一个对象
}

 从上面的例子不难看出:

  1. Swift中的类没必要须继承一个基类(可是ObjC一般必须继承于NSObject),若是一个类没有继承于任何其余类则这个类也称为“基类”;
  2. Swift中的属性定义形式相似于其余语句中的成员变量(或称之为“实例变量”),尽管它有着成员变量没有的特性;
  3. Swift中若是开发者没有本身编写构造方法那么默认会提供一个无参数构造方法(不然不会自动生成构造方法);
  4. Swift中的析构方法没有括号和参数,而且不支持自行调用;

属性

Swift中的属性分为两种:存储属性(用于类、结构体)和计算属性(用于类、结构体、枚举),而且在Swift中并不强调成员变量的概念。 不管从概念上仍是定义方式上来看存储属性更像其余语言中的成员变量,可是不一样的是能够控制读写操做、经过属性监视器来属性的变化以及快速实现懒加载功能。  

class Account {
    var balance:Double=0.0
}

class Person {
    //firstName、lastName、age是存储属性
    var firstName:String
    var lastName:String
    
    let age:Int
    
    //fullName是一个计算属性,而且因为只定义了get方法,因此是一个只读属性
    var fullName:String{
        get{
            return firstName + "." + lastName
        }
        set{
            let array=split(newValue, maxSplit: Int.max, allowEmptySlices: false, isSeparator: {$0=="."})
            if array.count == 2 {
                firstName=array[0]
                lastName=array[1]
            }
            
        }
        //set方法中的newValue表示即将赋的新值,能够本身设置set中的newValue变量,以下:
//        set(myValue){
//        }
    }
    //若是fullName只有get则是一个只读属性,只读属性能够简写以下:
//    var fullName:String{
//        return firstName + "." + lastName
//    }
    
    //属性的懒加载,第一次访问才会计算初始值,在Swift中懒加载的属性不必定就是对象类型,也能够是基本类型
    lazy var account = Account()
    
    
    //构造器方法,注意若是不编写构造方法默认会自动建立一个无参构造方法
    init(firstName:String,lastName:String,age:Int){
        self.firstName=firstName
        self.lastName=lastName
        self.age=age
    }
    
    //定义方法
    func showMessage(){
        println("name=\(self.fullName)")
    }
}


var p=Person(firstName: "Kenshin", lastName: "Cui",age:29)
p.fullName="Kaoru.Sun"
p.account.balance=10
p.showMessage()

 须要提醒你们的是:

  1. 计算属性并不直接存储一个值,而是提供getter来获取一个值,或者利用setter来间接设置其余属性;
  2.  lazy属性必须有初始值,必须是变量不能是常量(由于常量在构造完成以前就已经肯定了值);
  3. 在构造方法以前存储属性必须有值,不管是变量属性(var修饰)仍是常量属性(let修饰)这个值既能够在属性建立时指定也能够在构造方法内指定;

从上面的例子中不难区分存储属性和计算属性,计算属性一般会有一个setter、getter方法,若是要监视一个计算属性的变化在setter方法中便可办到(由于在setter方法中能够newValue或者自定义参数名),可是若是是存储属性就没法经过监视属性的变化过程了,由于在存储属性中是没法定义setter方法的。不过Swift为咱们提供了另外两个方法来监视属性的变化那就是willSet和didSet,一般称之为“属性监视器”或“属性观察器”。

class Account {
    //注意设置默认值0.0时监视器不会被调用
    var balance:Double=0.0{
        willSet{
            self.balance=2.0
            //注意newValue可使用自定义值,而且在属性监视器内部调用属性不会引发监视器循环调用,注意此时修改balance的值没有用
            println("Account.balance willSet,newValue=\(newValue),value=\(self.balance)")
        }
        didSet{
            self.balance=3.0
            //注意oldValue可使用自定义值,而且在属性监视器内部调用属性不会引发监视器循环调用,注意此时修改balance的值将做为最终结果
            println("Account.balance didSet,oldValue=\(oldValue),value=\(self.balance)")
        }
    }
}

class Person {
    var firstName:String
    var lastName:String
    let age:Int
    
    var fullName:String{
        get{
            return firstName + "." + lastName
        }
        set{
            //对于计算属性能够直接在set方法中进行属性监视
            let array=split(newValue, maxSplit: Int.max, allowEmptySlices: false, isSeparator: { $0 == "." })
            if array.count == 2 {
                firstName=array[0]
                lastName=array[1]
            }
        }
    }
    lazy var account = Account()
        init(firstName:String,lastName:String,age:Int){
        self.firstName=firstName
        self.lastName=lastName
        self.age=age
    }
    
    //类型属性
    static var skin:Array<String>{
        return ["yellow","white","black"];
    }

}

var p=Person(firstName: "Kenshin", lastName: "Cui",age:29)
p.account.balance=1.0
println("p.account.balance=\(p.account.balance)") //结果:p.account.balance=3.0
for color in Person.skin {
    println(color)
}
  1.  和setter方法中的newValue同样,默认状况下载willSet和didSet中会有一个newValue和oldValue参数表示要设置的新值和已经被修改过的旧值(固然参数名一样能够自定义);
  2. 存储属性的默认值设置不会引发属性监视器的调用(另外在构造方法中赋值也不会引发属性监视器调用),只有在外部设置存储属性才会引发属性监视器调用;
  3. 存储属性的属性监视器willSet、didSet内能够直接访问属性,可是在计算属性的get、set方法中不能直接访问计算属性,不然会引发循环调用;
  4. 在didSet中能够修改属性的值,这个值将做为最终值(在willSet中没法修改);

方法

方法就是与某个特定类关联的函数,其用法和前面介绍的函数并没有二致,可是和ObjC相比,ObjC中的函数必须是C语言,而方法则必须是ObjC。此外其余语言中方法一般存在于类中,可是Swift中的方法除了在类中使用还能够在结构体、枚举中使用。关于普通的方法这里不作过多赘述,用法和前面的函数区别也不大,这里主要看一下构造方法。

class Person {
    //定义属性
    var name:String
    var height:Double
    var age=0
    
    //指定构造器方法,注意若是不编写构造方法默认会自动建立一个无参构造方法
    init(name:String,height:Double,age:Int){
        self.name=name
        self.height=height
        self.age=age
    }
    
    //便利构造方法,经过调用指定构造方法、提供默认值来简化构造方法实现
    convenience init(name:String){
        self.init(name:name,height:0.0,age:0)
    }
    
    //实例方法
    func modifyInfoWithAge(age:Int,height:Double){
        self.age=age
        self.height=height
    }
    
    //类型方法
    class func showClassName(){
        println("Class name is \"Person\"")
    }
    
    //析构方法,在对象被释放时调用,相似于ObjC的dealloc,注意此函数没有括号,没有参数,没法直接调用
    deinit{
        println("deinit...")
    }
    
}

//经过便利构造方法建立对象
var p=Person(name: "kenshin")
  1. 除构造方法、析构方法外的其余方法的参数默认除了第一个参数是局部参数,从第二个参数开始既是局部参数又是外部参数(这种方式和ObjC的调用方式很相似,固然,可使用“#”将第一个参数同时声明为外部参数名,也可使用“_”将其余参数设置为非外部参数名)。可是,对于函数,默认状况下只有默认参数既是局部参数又是外部参数,其余参数都是局部参数。
  2. 构造方法的全部参数默认状况下既是外部参数又是局部参数;
  3. Swift中的构造方法分为“指定构造方法”和“便利构造方法(convenience)”,指定构造方法是主要的构造方法,负责初始化全部存储属性,而便利构造方法是辅助构造方法,它经过调用指定构造方法并指定默认值的方式来简化多个构造方法的定义,可是在一个类中至少有一个指定构造方法。

下标脚本

下标脚本是一种访问集合的快捷方式,例如:var a:[string],咱们常用a[0]、a[1]这种方式访问a中的元素,0和1在这里就是一个索引,经过这种方式访问或者设置集合中的元素在Swift中称之为“下标脚本”(相似于C#中的索引器)。从定义形式上经过“subscript”关键字来定义一个下标脚本,很像方法的定义,可是在实现上经过getter、setter实现读写又相似于属性。假设用Record表示一条记录,其中有多列,下面示例中演示了如何使用下标脚本访问并设置某一列的值。

class Record {
    //定义属性,假设store是Record内部的存储结构
    var store:[String:String]
    
    init(data:[String:String]){
        self.store=data
    }
    
    //下标脚本(注意也能够实现只有getter的只读下标脚本)
    subscript(index:Int)->String{
        get{
            var key=sorted(Array(self.store.keys))[index]
            return self.store[key]!
        }
        set{
            var key=sorted(Array(self.store.keys))[index]
            self.store[key]=newValue //newValue参数名能够像属性同样从新自定义
        }
    }
    
    //下标脚本(重载)
    subscript(key:String)->String{
        get{
            return store[key]!
        }
        set{
            store[key]=newValue
        }
    }
}

var r=Record(data:["name":"kenshin","sex":"male"])
println("r[0]=\(r[0])") //结果:r[0]=kenshin
r["sex"]="female"
println(r[1]) //结果:female

继承

和ObjC同样,Swift也是单继承的(能够实现多个协议,此时协议放在后面),子类能够调用父类的属性、方法,重写父类的方法,添加属性监视器,甚至能够将只读属性重写成读写属性。

class Person {
    var firstName:String,lastName:String
    var age:Int=0
    var fullName:String{
        get{
            return firstName+" "+lastName
        }
    }
    
    init(firstName:String,lastName:String){
        self.firstName=firstName
        self.lastName=lastName
    }
    
    func showMessage(){
        println("name=\(fullName),age=\(age)")
    }
    
    //经过final声明,子类没法重写
    final func sayHello(){
        println("hello world.")
    }
}

class Student: Person {
    //重写属性监视器
    override var firstName:String{
        willSet{
            println("oldValue=\(firstName)")
        }
        didSet{
            println("newValue=\(firstName)")
        }
    }
    
    var score:Double
    
    //子类指定构造方法必定要调用父类构造方法
    //而且必须在子类存储属性初始化以后调用父类构造方法
    init(firstName:String,lastName:String, score:Double){
        self.score=score
        super.init(firstName: firstName, lastName: lastName)
    }
    
    convenience init(){
        self.init(firstName:"",lastName:"",score:0)
    }
    
    //将只读属性重写成了可写属性
    override var fullName:String{
        get{
            return super.fullName;
        }
        set{
            let array=split(newValue, maxSplit: Int.max, allowEmptySlices: false, isSeparator: { $0 == "." })
            if array.count == 2 {
                firstName=array[0]
                lastName=array[1]
            }
        }
    }
    
    //重写方法
    override func showMessage() {
        println("name=\(fullName),age=\(age),score=\(score)")
    }
    
}

var p=Student()
p.firstName="kenshin"

在使用ObjC开发时init构造方法并不安全,首先没法保证init方法只调用一次,其次在init中不能访问属性。可是这些彻底依靠文档约定,编译时并不能发现问题,出错检测是被动的。在Swift中构造方法(init)有了更为严格的规定:构造方法执行完以前必须保证全部存储属性都有值。这一点不只在当前类中必须遵循,在整个继承关系中也必须保证,所以就有了以下的规定:

  1. 子类的指定构造方法必须调用父类构造方法,并确保调用发生在子类存储属性初始化以后。并且指定构造方法不能调用同一个类中的其余指定构造方法;
  2. 便利构造方法必须调用同一个类中的其余指定构造方法(能够是指定构造方法或者便利构造方法),不能直接调用父类构造方法(用以保证最终以指定构造方法结束);
  3. 若是父类仅有一个无参构造方法(无论是否包含便利构造方法),子类的构造方法默认就会自动调用父类的无参构造方法(这种状况下能够不用手动调用);
  4. 常量属性必须默认指定初始值或者在当前类的构造方法中初始化,不能在子类构造方法中初始化; 

协议

协议是对实例行为的一种约束,和ObjC相似,在Swift中能够定义属性和方法(ObjC中之因此能定义属性是由于@property的本质就是setter、getter方法)。和其余语言不一样的是Swift中的协议不只限于类的实现,它一样能够应用于枚举、结构体(若是只想将一个协议应用于类,能够在定义协议时在后面添加class关键字来限制其应用范围)。

protocol Named{
    //定义一个实例属性
    var name:String { get set }

    //定义一个类型属性
    static var className:String { get }
    
    //定义构造方法
    init(name:String)
    
    //定义一个实例方法
    func showName()
    
    //定义一个类型方法
    static func showClassName()
}

protocol Scored{
    var score:Double { get set }
}

//Person遵循了Named协议
class Person:Named {
    //注意从Named协议中并不知道name是存储属性仍是计算属性,这里将其做为存储属性实现
    var name:String
    
    var age:Int = 0
    
    static var className:String{
        return "Person"
    }
    
    //协议中规定的构造方法,必须使用required关键字声明,除非类使用final修饰
    required init(name:String){
        self.name=name
    }
    
    //遵循showName方法
    func showName() {
        println("name=\(name)")
    }
    
    //遵循showClassName方法
    static func showClassName() {
        println("Class name is \"Person\"")
    }
}

//Student继承于Person而且实现了Scored协议
class Student: Person,Scored {
    var score:Double=0.0
    
    init(name:String, score:Double){
        self.score = score
        super.init(name: name)
    }

    //因为上面自定义了构造方法则必须实现协议中规定的构造方法
    required init(name: String) {
        super.init(name: name)
    }
    
    func test(){
        println("\(self.name) is testing.")
    }
}

var p=Person(name: "Kenshin Cui")
p.showName() //结果:name=Kenshin Cui
println("className=\(Person.className)") //结果:className=Person
Person.showClassName() //结果:Class name is "Person"
p.age=28

var s:Named=Student(name: "Kaoru",score:100.0) //尽管这里将s声明为Named类型,可是运行时仍然能够正确的解析(多态),可是注意此时编译器并不知道s有test方法,因此此时调用test()会报错
s.showName()

//在下面的函数中要求参数stu必须实现两个协议
func showMessage(stu:protocol<Named,Scored>){
    println("name=\(stu.name),score=\(stu.score)")
}
var s2=Student(name: "Tom",score:99.0)
showMessage(s2) //结果:name=Tom,age=99.0


//检测协议
let b1 = s is Scored //判断p是否遵循了Scored协议
if b1 {
    println("s has score property.")
}
//类型转化
if let s3 = s as? Scored { //若是s转化成了Scored类型则返回实例,不然为nil
    println("s3' score is \(s3.score)") //结果:s3' score is 100.0
}
let s4 =  s as! Scored //强制转换,若是转化失败则报错
println("s4' score is \(s4.score)") //结果:s4' score is 100.0
  1. 协议中虽然能够指定属性的读写,但即便协议中规定属性是只读的但在使用时也能够将其实现成可读写的;
  2. Swift的协议中能够约定属性是实例属性仍是类型属性、是读写属性仍是只读属性,可是不能约束其是存储属性仍是计算属性;
  3. 协议中的类型属性和类型方法使用static修饰而不是class(尽管对于类的实现中类型属性、类型方法使用class修饰);
  4. 协议中约定的方法支持可变参数,可是不支持默认参数;
  5. 协议中约定的构造方法,在实现时若是不是final类则必须使用require修饰(以保证子类若是须要自定义构造方法则必须覆盖父类实现的协议构造方法,若是子类不须要自定义构造方法则没必要);
  6. 一个协议能够继承于另一个或多个协议,一个类只能继承于一个类但能够实现多个协议;
  7. 协议自己就是一种类型,这也体现除了面向对象的多态特征,可使用多个协议的合成来约束一个实例参数必须实现某几个协议;

扩展

Swift中的扩展就相似于ObjC中的分类(事实上在其余高级语言中更多的称之为扩展而非分类),可是它要比分类强大的多,它不只能够扩展类还能够扩展协议、枚举、结构体,另外扩展也不局限于扩展方法(实例方法或者类型方法),还能够扩展便利构造方法、计算属性、下标脚本、

class Person {
    var firstName:String,lastName:String
    var age:Int=0
    var fullName:String{
        get{
            return firstName+" "+lastName
        }
    }
    
    init(firstName:String,lastName:String){
        self.firstName=firstName
        self.lastName=lastName
    }
    
    func showMessage(){
        println("name=\(fullName),age=\(age)")
    }
}

extension Person{
    
    //只能扩展便利构造方法,不能扩展指定构造方法
    convenience init(){
        self.init(firstName:"",lastName:"")
    }
    
    //只能扩展计算属性,没法扩展存储属性
    var personInfo:String{
        return "firstName=\(firstName),lastName=\(lastName),age=\(age)";
    }

    //扩展实例方法
    func sayHello(){
        println("hello world.")
    }
    
    //嵌套类型
    enum SkinColor{
        case Yellow,White,Black
    }
    
    //扩展类型方法
    static func skin()->[SkinColor]{
        return [.Yellow,.White,.Black]
    }
}

var p=Person()
p.firstName="Kenshin"
p.lastName="Cui"
p.age=28
println(p.personInfo) //结果:firstName=Kenshin,lastName=Cui,age=28
p.sayHello() //结果:hello world.
Person.skin()

枚举和结构体

结构体

结构体和类是构造复杂数据类型时经常使用的构造体,在其余高级语言中结构体相比于类要简单的多(在结构体内部仅仅能定义一些简单成员),可是在Swift中结构体和类的关系要紧密的多,这也是为何将结构体放到后面来讲的缘由。Swift中的结构体能够定义属性、方法、下标脚本、构造方法,支持扩展,能够实现协议等等,不少类能够实现的功能结构体都能实现,可是结构体和类有着本质区别:类是引用类型,结构体是值类型。

struct Person {
    var firstName:String
    var lastName:String
    
    var fullName:String{
        return firstName + " " + lastName
    }
    
    var age:Int=0
    
    //构造函数,若是定义了构造方法则不会再自动生成默认构造函数
//    init(firstName:String,lastName:String){
//        self.firstName=firstName
//        self.lastName=lastName
//    }
    
    func showMessage(){
        println("firstName=\(firstName),lastName=\(lastName),age=\(age)")
    }
    
    //注意对于类中声明类型方法使用关键字class修饰,但结构体里使用static修饰
    static func showStructName(){
        println("Struct name is \"Person\"")
    }
}

//注意全部结构体默认生成一个全员逐一构造函数,一旦自定义构造方法,这个默认构造方法将不会自动生成
var p=Person(firstName: "Kenshin", lastName: "Cui", age: 28)
println(p.fullName) //结果:Kenshin Cui
p.showMessage() //结果:firstName "Kenshin", lastName "Cui", age 28
Person.showStructName() //结果:Struct name is "Person"

//因为结构体(包括枚举)是值类型因此赋值、参数传递时值会被拷贝(因此下面的实例中p2修改后p并未修改,可是若是是类则状况不一样)
var p2 = p
p2.firstName = "Tom"
println(p2.fullName) //结果:Tom Cui
println(p.fullName) //结果:Kenshin Cui
  1. 默认状况下若是不自定义构造函数那么将自动生成一个无参构造函数和一个全员的逐一构造函数;
  2. 因为结构体是值类型,因此它虽然有构造函数可是没有析构函数,内存释放系统自动管理不须要开发人员过多关注;
  3. 类的类型方法使用class修饰(以便子类能够重写),而结构体、枚举的类型方法使用static修饰(补充:类方法也可使用static修饰,可是不是类型方法而是静态方法;另外类的存储属性若是是类型属性使用static修饰,而类中的计算属性若是是类型属性使用class修饰以即可以被子类重写;换句话说class做为“类型范围做用域”来理解时只有在类中定义类型方法或者类型计算属性时使用,其余状况使用static修饰[包括结构体、枚举、协议和类型存储属性]);

类的实例一般称之为“对象”,而在Swift中结构体也能够有实例,所以对于不少两者均可以实现的功能,在文中称之为实例而没有使用对象的概念。

枚举

在其余语言中枚举本质就是一个整形,只是将这组相关的值组织起来并指定一个有意义的名称。可是在Swift中枚举不强调一个枚举成员必须对应一个整形值(固然若是有必要仍然能够指定),而且枚举类型的能够是整形、浮点型、字符、字符串。首先看一下枚举的基本使用:

//注意Swift中的枚举默认并无对应的整形值,case用来定义一行新的成员,也能够将多个值定义到同一行使用逗号分隔,例如:case Spring,Summer,Autumn,Winter
enum Season{
    case Spring
    case Summer
    case Autumn
    case Winter
}

var s=Season.Spring

//一旦肯定了枚举类型,赋值时能够去掉类型实现简写
s = .Summer

switch s {
case .Spring: //因为Swift的自动推断,这里仍然能够不指明类型
    println("spring")
case .Summer:
    println("summer")
case .Autumn:
    println("autumn")
default:
    println("winter")
}

事实上Swift中也能够指定一个值和枚举成员对应,就像其余语言同样(一般其余语言的枚举默认就是整形),可是Swift又不局限于整形,它能够是整形、浮点型、字符串、字符,可是原始值必须是一种固定类型而不能存储多个不一样的类型,同时若是原始值为整形则会像其余语言同样默认会自动递增。

//指定原始值(这里定义成了整形)
enum Season:Int{
    case Spring=10 //其余值会默认递增,例如Summer默认为11,若是此处也不指定值会从0开始依次递增
    case Summer
    case Autumn
    case Winter
}

var summer=Season.Summer

//使用rawValue访问原始值
println("summer=\(summer),rawValue=\(summer.rawValue)")

//经过原始值建立枚举类型,可是注意它是一个可选类型
var autumn=Season(rawValue: 12)

//可选类型绑定
if let newAutumn=autumn{
    println("summer=\(newAutumn),rawValue=\(newAutumn.rawValue)")
}

若是一个枚举类型可以和一些其余类型的数据一块儿存储起来每每会颇有用,由于这可让你存储枚举类型以外的信息(相似于其余语言中对象的tag属性,可是又多了灵活性),这在其余语言几乎是不可能实现的,可是在Swift中却能够作到,这在Swift中称为枚举类型相关值。要注意的是相关值并非原始值,原始值须要事先存储而且只能是同一种类型,可是相关值只有建立一个基于枚举的变量或者常量时才会指定,而且类型能够不一样(原始值更像其余语言的枚举类型)。 

//相关值
enum Color{
    case RGB(String) //注意为了方便演示这里没有定义成三个Int类型(例如: RGB(Int,Int,Int))而使用16进制字符串形式
    case CMYK(Float,Float,Float,Float)
    case HSB(Int,Int,Int)
}

var red=Color.RGB("#FF0000")

var green=Color.CMYK(0.61, 0.0, 1.0, 0.0)

var blue=Color.HSB(240, 100, 100)

switch red {
case .RGB(let colorStr):
    println("colorStr=\(colorStr)")
case let .CMYK(c,m,y,k):
    println("c=\(c),m=\(m),y=\(y),k=\(k)")
case let .HSB(h,s,b):
    println("h=\(h),s=\(s),b=\(b)")
}

上面提到其实枚举也有一些类型和结构体的特性,例如计算属性(包括类型属性,枚举只能定义计算属性不能定义存储属性,存储属性只能应用于类和结构体)、构造方法(其实上面使用原始值建立枚举的例子就是一个构造方法)、方法(实例方法、类型方法)、下标脚本 。

enum Season:Int{
    case Spring=0 ,Summer,Autumn,Winter
    
    //定义计算属性
    var tag:Int{
        return self.rawValue
    }
    //类型属性
    static var enumName:String{
        return "Season"
    }
    
//    //定义构造方法,注意在枚举的构造函数中则必须保证self有值(正如类的构造方法必须保证其存储属性有值同样)
//    init(prefix:String){
//        switch prefix.lowercaseString {
//            case "sp":
//                self = .Spring
//            case "su":
//                self = .Summer
//            case "au":
//                self = .Autumn
//            default:
//                self = .Winter
//        }
//    }
    //其实上面的构造器有些不合理,那就是default就是Winter,事实上这类构造器可能传任何参数,此时可使用可失败构造函数来解决
    //可失败构造函数返回nil(尽管Swift中构造函数是不返回值的,可是此时约定返回nil表明构造失败)
    init?(prefix:String){
        switch prefix.lowercaseString {
        case "sp":
            self = .Spring
        case "su":
            self = .Summer
        case "au":
            self = .Autumn
        case "wi":
            self = .Winter
        default:
            return nil
        }
    }

    //定义实例方法
    func showMessage(){
        println("rowValue=\(self.rawValue)")
    }
    //定义类型方法
    static func showEnumName(){
        println("Enum name is \"Season\"")
    }
}

var summer=Season.Summer
println(summer.tag) //结果:1
println(Season.enumName) //结果:Season
Season.showEnumName() //结果:Enum name is "Season"
summer.showMessage() //结果:rowValue=1

if let spring = Season(prefix: "au") { //可选绑定,构造函数返回值可能为nil
    println(spring.tag) //结果:2
}

 

之因此没有将枚举、结构体放到上面的数据类型部分介绍一方面Swift中的枚举、结构体和其余语言中有较大差异,另外一方面是由于这个部分的介绍要用到前面的知识。

泛型

泛型可让你根据需求使用一种抽象类型来完成代码定义,在使用时才真正知道其具体类型。这样一来就好像在定义时使用一个占位符作一个模板,实际调用时再进行模板套用,因此在C++中也称为“模板”。泛型在Swift中被普遍应用,上面介绍的Array<>、Dictionary<>事实上都是泛型的应用。经过下面的例子简单看一下泛型参数和泛型类型的使用。

/*泛型参数*/
//添加了约束条件的泛型(此时T必须实现Equatable协议)
func isEqual<T:Equatable>(a:T,b:T)->Bool{
    return a == b
}

var a:Int=1,b:Int=2
println(isEqual(a,b)) //结果:false

var c:String="abc",d:String="abc"
println(isEqual(c,d)) //结果:true

/*泛型类型*/
struct Stack<T> {
    var store:[T]=[]
    
    //在结构体、枚举中修改其变量须要使用mutating修饰(注意类不须要)
    mutating func push(item:T){
        store.append(item)
    }
    
    mutating func pop()->T{
        return store.removeLast()
    }
}

var s = Stack<Int>()
s.push(1)
let t = s.pop()
println("t=\(t)") //结果:t=1


//扩展泛型类型
extension Stack{
    var top:T?{
        return store.last
    }
}

s.push(2)
println(s.top!) //结果:2

上面演示了泛型结构体用法,其实类一样是相似的,这里就不在赘述了,可是若是遇到泛型协议怎么办呢?假设Stack必须遵循一个Stackable协议,此时就必须在协议中引入一个关联类型来解决。 

protocol Stackable{
    //声明一个关联类型
    typealias ItemType
    mutating func push(item:ItemType)
    mutating func pop()->ItemType;
}

struct Stack:Stackable{
    var store:[T]=[]
    
    mutating func push(item:T){
        store.append(item)
    }
    
    mutating func pop()->T{
        return store.removeLast()
    }
}

var s = Stack()
s.push("hello")
s.push("world")
let t = s.pop()
println("t=\(t)") //结果:t=world
相关文章
相关标签/搜索