类
作为一门面向对象语言,类当然是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
} |
泛型
泛型可以让你根据需求使用一种抽象类型来完成代码定义,在使用时才真正知道其具体类型。这样一来就好像在定义时使用一个占位符做一个模板,实际调用时再进行模板套用,所以在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 |
|