Swift开发实战权威指南(txt+pdf+epub+mobi电子书下载)


发布时间:2020-07-30 07:04:28

点击下载

作者:欧阳坚等

出版社:清华大学出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

Swift开发实战权威指南

Swift开发实战权威指南试读:

前言

2014年互联网和移动互联网创造了一个又一个伟大传奇,苹果市值暴增了53%,一年的时间光是股票的增幅就相当于IBM公司的市值。然后是中国的京东、阿里巴巴上市创造了中国互联网世界的神话,阿里巴巴2014年“双11”一天的营业额就达到了571亿元,其中移动互联网手机端的成交额占到了47%。这些数字显示了移动互联网时代已经来临,一个全新时代已经开启。有人预言,移动互联网会反超互联网。大大小小的IT公司,以及从事传统事业的商家纷纷开始布局移动互联网。互联网时代创造了一个个经济神话,也造就了很多时代英雄。苹果公司显然早已掌握并且预测了移动互联网的蓬勃发展,所以苹果公司也顺势推出了下一代移动开发编程语言Swift,目的是吸引越来越多的开发者进入移动互联网行业。本书的目的也是通过详细例子的讲解,让更多的开发者掌握这一全新的开发语言。

Swift编程开发语言是苹果公司2014年在美国旧金山召开的WWDC 2014(Apple Worldwide Developers Conference,苹果全球开发者大会)上发布的一种新的开发语言。Swift语言专门用来开发苹果桌面OSX应用程序和iOS应用程序。

作为现代高级编程语言,Swift借鉴了很多其他优秀高级语言的特性,比如闭包Closure、操作符重载、泛型、ARC等特性。此外,Swift语言还拥有很多新的特性,比如Playground使得编程更加有趣,更加实时地知道代码的运行效果。对于开发者来说,Swift入门者的学习难度降低了很多,代码产出率也很高;当然它最大的优点是完全兼容Objective-C,可以无缝地使用iOS开发中的Cocoa Touch类库和大量的第三方库,在项目开发中可以毫无障碍地使用Swift开发iOS或者OSX应用程序。

在苹果公司发布了iOS8和OS X 10.10 Yosemite操作系统后。苹果官方正式欢迎开发者提交使用Swift开发的程序。目前,App Store,github已经有大量的App、开源库的提交,相信随着Swift用户群的不断壮大,会有越来越多的开发者加入到Swift开发社区中来。本书的目标读者

本书面向希望为iPhone/iPad以及OSX开发应用程序的开发人员,是一本从入门到精通的开发手册。本书通过大量清晰、完善的实例,迅速引导读者进行iOS开发。

对于Objective-C开发者来说,学习本书的内容可以快速上手开发Swift应用程序。Swift是对Objective-C的一个优雅包装,本书也在很多代码实例中做出了Swift和Objective-C的对比,方便Objective-C开发者迅速上手开发Swift程序。

对于使用C/C++、Java、PHP、C#等其他开发语言的开发者,本书也能帮助其快速地转到Swift开发中来。本书的组织结构

本书从Swift入门开始,由浅入深地介绍iOS App开发的步骤和流程。从基本的语法开始,然后是面向对象、高级语法和Objective-C之间相互调用,最后介绍UI App开发整个流程。

下面概述了本书各章的内容。

第1章:Swift语言介绍

介绍学习Swift语言的目的和要领,从总体上介绍语言的特点和与其他语言的比较,以便从其他语言转至Swift,并对Swift有一个整体的感性认识。

第2章:基础知识

介绍Swift的基本常量、变量的定义声明,及定义数据类型,比如整数、浮点类型、类型的转化、元组类型以及初步的可选类型等。同时介绍基本的运算操作。

第3章:字符串、数组、字典

介绍Swift语言中常用的数据结构——字符串、数组和字典,使用大量实例讲解三者常用的API函数和使用方法以及注意事项。

第4章:控制语句和函数

介绍编程中常见的分支、循环以及函数的定义,包括if、switch、for、while、break、continue以及fallthrough等语句的使用。同时介绍函数的使用方法和Swift特有的函数标签的使用方法。

第5章:枚举和结构体

结构体和枚举和C语言类似,都是面向对象使用的前奏,这里介绍结构体的声明、构造和使用,同时也强调了结构体值拷贝的特性。枚举的声明、使用和C语言结构体的异同。

第6章:类

本章是Swift面向对象初步,介绍类的概念和定义。这里阐明了面向对象概念、类的属性和方法,以及mutating关键字,subscript下标等。本章使用大量例子和概念来讲解类的各种情况,同时对于不同于其他语言的类的特点也做了详细的分析。

第7章:继承

继承是任何面向对象中的一个重要特性,本章内容较多,分析了构造函数、析构函数、方便构造函数、子类、父类构造函数的调用顺序等。还介绍了析构函数调用的方法,对于类中方法的扩展等也都做了详细的讲解。

第8章:自动引用计数

讲解Swift需要注意的内存管理问题。阐述了自动引用计数的工作原理,介绍了弱引用、强引用,在什么情况下会出现循环引用等情况,然后也给出了例子来解决循环引用。

第9章:可选链和类型转换

可选链是Swift特有的语法,本章详细介绍可选链的模型定义,为什么需要可选链、可选链调用属性和方法,可选链调用subscript下标、可选链多层链接、返回值等。同时介绍了各种类型之间的相互转换,Any和AnyObject类型的转换等。

第10章:协议

介绍面向对象中一个重要的知识点——协议,协议是面向对象中重要数据传输的规范,也是作为代理的重要语法基础。本章介绍了协议的定义、规范和使用,以及协议的集成、协议和扩展增加方法、协议的合成、可选协议等。

第11章:闭包和操作符重载

介绍了闭包Closer的语法特性和闭包作为回调函数、反向传值的使用场景。介绍了Swift中的泛型定义和使用,以及高级操作符重载等高级语法特性。还介绍了Swift和Objective-C之间的语法交互和相互调用等特性。

第12章:第一个UI项目

介绍了在Mac下开发iOS程序的步骤,开发环境Xcode的界面布局和使用方法,从UI工程的创建、代码的编写到项目的运行,一步步详细介绍了iOS程序的创建。通过讲解,使Swift开发者有能力开始iOS开发。

第13章:UIView视图

主要介绍在iOS开发中最重要也是最基本的UIView,它是所有iOS控件的父类,也是学习其他控件的基础。本章涉及的内容包括UIView的创建及显示风格、视图的父子关系、动画效果的使用、iOS界面的坐标系统以及颜色的创建方式等。

第14章:iOS中的各种控件

本章介绍了在iOS开发中常用的控件:标签UILabel、按钮UIButton、UIImageView、UITextField等。通过本章的学习,开发者能够掌握基本的界面布局和各种控件的使用方法,为以后的开发做好准备。

第15章:UIViewControler视图控制器

本章讲解的视图控制器是继UIView之后又一项必须掌握的基本知识,它是iOS开发中最常用的界面管理控制器,可以说在iOS程序中所有的界面都是一个视图控制器,可见它的重要性。只有掌握视图控制器的使用,才能做好中大型iOS的APP。

第16章:UINavigationController导航栏控制器

本章讲解的导航栏控制器UINavigationController是iOS中的架构级的控件,是用来管理视图控制器的控件。通过导航栏控制器可以完整实现一个APP,所以导航栏控制器在整个iOS中是非常重要的内容。本章也详细介绍了导航栏NavigationBar、工具栏ToolBar以及导航项NavigationItem的各种使用方法,还讲解了页面之间的关系及各种切换方式。

第17章:界面之间的传值

本章介绍了页面之间的正向传值及反向传值的方法,这两种传值是在项目开发中很常用且不易掌握的知识点。在反向传值中使用了Swift基本语法中提到的协议、代理以及闭包。这两个知识点在将来的项目中会大量使用,所以也是必须掌握的知识点。

第18章:UITabBarController标签栏控制器

标签栏控制器UITabBarController是继导航栏控制器之后的又一个架构级控件,也是目前市场上的APP的一种主流架构方式。本章介绍了标签栏控制器的创建和各种形式的标签的创建,在项目开发中所遇到的各种情况在本章都有提及。除了标签栏控制器,本章还介绍了本地存储NSUserDefaults的使用,它是数据本地化存储中最简单的一种方式。

第19章:UIScrollView滚动视图

本章所介绍的滚动视图也是一个很重要的控件,可以使用它来实现如引导页这样的图片浏览,也可以用它来实现如网页一样的滚动效果。它还是表视图以及网格视图等其他视图的父类。本章详细讲解了滚动视图的相关知识,以及使用它来实现引导页的案例。

第20章:UITableView表视图

介绍了在iOS开发中最常用的控件——表视图(UITableView)。无论是购物类的应用、资讯类应用还是聊天类的应用,表视图都是页面架构的首选控件。还介绍了表视图的创建及数据的显示,表视图的各种代理方法的作用及使用方法。介绍了表视图的编辑模式及索引和搜索的使用方法,表视图的cell自定义以及使用xib进行cell定制的方法。编者

上篇 Swift语言基础篇

第1章 Swift语言介绍

Swift编程开发语言是苹果公司2014年在美国旧金山召开的WWDC 2014(Apple Worldwide Developers Conference,苹果全球开发者大会)大会上发布的一门新的开发语言。Swift语言可以用来开发苹果桌面OSX应用程序和iOS应用程序。

在这之前,苹果操作系统上一直使用Objective-C作为主流开发语言。不可避免,无论是苹果官方还是众多开发者都要对Objective-C和Swift语言进行对比。苹果公司声称新的Swift语言拥有快速、现代、安全、互动等特性,并且性能全部优于Objective-C语言。

从开发者的角度来说,Swift的学习难度和从其他语言转行的难度较小,代码产出率也很高。Swift语言还拥有很多新的特性,比如Playground使得编程更加有趣。它融合了很多现代高级语言的特性,比如闭包Closure、操作符重载、泛型、ARC等特性。当然它最大的优点是完全兼容Objective-C,可以无缝地使用iOS开发中的Cocoa Touch类库和大量的第三方库。使开发者可以毫无障碍地使用Swift开发iOS或者OSX应用程序。

从开发者的角度来说,Swift的学习难度和从其他语言转行的难度较小,代码产出率也很高。Swift语言还拥有很多新的特性,比如Playground使得编程更加有趣。它融合了很多现代高级语言的特性,比如闭包Closure、操作符重载、泛型、ARC等特性。当然它最大的优点是完全兼容Objective-C,可以无缝地使用iOS开发中的Cocoa Touch类库和大量的第三方库。使开发者可以毫无障碍地使用Swift开发iOS或者OSX应用程序。

Swift是一门完全面向对象的语言,它抛弃了和C/C++兼容的历史包袱。哪怕是最基本的Char,Int,Long,Float,Double类型都是一个结构体对象。引入了在Java,C++,Python中大行其道的操作符重载、泛型、名字空间、闭包等特性。它采用了安全编程模式,同时也添加了现代的功能,使得编程更加简单、灵活和有趣。当然它底层是基于苹果之前的Cocoa Touch框架,这也使得它拥有大量的基础库和优秀稳定的代码资源。

在苹果公司发布了iOS8和OS X 10.10 Yosemite操作系统后,苹果官方欢迎开发者提交使用Swift开发的程序。

1.1 Swift语言介绍

Swift语言从2010年开始研发到最终发布仅用了不足4年时间。该语言的创造者为苹果开发者工具部门总监克里斯·拉特纳(Chris Lattner)(图1.1),由于该语言在苹果内部是一个研究项目,所以Swift的底层架构起初由克里斯·拉特纳一个人开发完成。

Swift语言的开发工作从2010年7月开图1.1 克里斯·拉特纳(Chris 始,直到2013年才获得了苹果开发者工Lattner)具部门的重视。Swift早期的大多数架构开发是由克里斯·拉特纳独自完成的,但到了2011年末,苹果一些非常优秀的工程师开始为该项目提供帮助,开始把大量的Objective-C库使用Swift进行改写。这才使得Swift获得了部门的重视。

与其他编程语言一样,Swift吸收了其他编程语言的开发经验,摒除了开发中的不便和缺点。Xcode Playgrounds功能是拉特纳最喜欢的特点,也是Swift为苹果开发工具带来的最大创新。该功能提供了非常优秀的互动效果,能让Swift代码在编写过程中实时地编译和显示。拉特纳强调,Playgrounds的功能很大程度是受到了布雷特·维克多(Bret Victor)的理念、透写光台以及其他一些互动系统的启发。而将编程变得更加平民化和有趣,有助于苹果吸引到下一代的程序员们,甚至让大学重新制定计算机科学专业的课程内容。

拉特纳的宏大目标在苹果全球开发者大会(WWDC)上获得了公司软件工程副总裁克雷格·费德里吉(Craig Federighi)的认可。后者在主旨演讲中向全体开发者传达了苹果的伟大雄心——将公司最为擅长的实用性特点带入到旗下软件开发工具中。

当Swift首度亮相时,全场惊呼,并为之震惊。开发者们立刻对Swift展现出了浓厚兴趣。仅发布后一天,有关该语言的电子书就被下载了37万次以上。

1.2 Swift和Objective-C语言对比

在Swift之前,Objective-C一直是苹果系统的主流开发语言,从2007年苹果发布iOS操作系统和iPhone第一代手机以来,Objective-C开发语言市场占有率节节攀升,目前已经排到前四。

但即使是如此好的市场前景,苹果公司依然推出了新的Swift语言。如果细致地使用Swift进行开发就会发现,Swift语言是对Objective-C语言的一个优雅的包装。它底层还是使用Cocoa Touch,Foundation框架。只是语言层面上让开发变得容易。

严格来说,Objective-C并不是一个独立的面向对象语言。它只是在C语言上层加上了一个轻度面向对象的外壳。比如,Objective-C并没有严格意义上的构造函数,所以开发者必须使用init开头的系列函数来模拟构造函数;对类型检查不严格;对nil消息的调用调试难以追踪;中括号难以入门和理解。如下面的代码所示。- (void) callFunc:(id)p withParam:(id)p2 withParam:(id)p3 {// 代码}[obj callFunc:p1 withParam:p2 withParam:p3];

当然,在引入ARC后和C/C++混合编程变得相对麻烦。同时,在函数、枚举、变量等命名上由于没有名字空间,所以代码都相对较长。比如:typedef NS_ENUM(NSInteger, UIControlContentHorizontalAlignment) {UIControlContentHorizontalAlignmentCenter = 0,UIControlContentHorizontalAlignmentLeft = 1,UIControlContentHorizontalAlignmentRight = 2,UIControlContentHorizontalAlignmentFill = 3,};

诸如Objective-C的这些问题。苹果公司提出了新的开发语言Swift,从最新Xcode6上可以看出,无论是苹果本地的命令行开发,还是苹果的Cocoa App开发,以及iOS上的开发,都把Swift语言作为首选的语言。从这一点可以依稀看出,苹果公司对于Swift的高度期望和重视,如图1.2、图1.3、图1.4所示。图1.2 Xcode本地命令行中的语言选择图1.3 Xcode本地Cocoa App中的语言选择图1.4 Xcode iOS App中的语言选择

1.3 Swift优秀的特性

Swift语言在吸收诸多优秀语言如Java,C++,Python之后,提供给开发者大量优秀的特性,并把Objective-C语言的优秀特性全部吸收。

下面列出一些优秀特性及其代码,供有基础的读者快速了解Swift。对于初学者可以在读完后续的章节后反过来对比这些优点。1.函数使用经典圆括号和点调用语法

有Objective-C开发经验的开发者大多第一次都会觉得Objective-C的方法调用有别于其他主流的Java,C++,C#等语言,Objective-C称之为消息调用。这也让很多开发者第一次学习Objective-C的时候难度上升。Swift回归于经典的圆括号和点号调用方式。

Objective-C函数声明如下:- (void) callFunc:(id)p withParam:(id)p2 withParam2:(id)p3{// 代码}

Objective-C函数调用如下:NSString *p1 = @"abc";NSString *p2 = @"efg";NSString *p3 = @"hij";[obj callFunc:p1 withParam:p2 withParam2:p3];

Swift函数声明如下:func callFunc(p1 : String, withParam : String, withParam2 : String) {// 代码}

Swift函数调用如下:var p1 = "abc"var p2 = "efg"var p3 = "hij"callFunc(p1, p2, p3)2.函数标签特性

Objective-C中的函数标签也是函数参数的一部分,它避免了参数过多的情况下分不清每个参数的含义。Objective-C的优秀特性被Swift继承下来了。Swift也支持标签。如下面代码所示,可以给参数2、参数3加上标签。func callFunc(p1 : String, withParam p1 : String,withParam2 p2: String){// 代码}var p1 = "abc"var p2 = "efg"var p3 = "hij"callFunc(p1, withParam: p2, withParam2: p3)3.严格的类型检查

Swift抛弃了Objective-C中松散的类型检查方式,进而使用严格的类型检查和转换操作。因为Swift所有的类型都是结构体或者类,没有了基本类型,所以基于值拷贝的转化都是拷贝操作。基于引用的方式是使用as,as?操作来进行的。比如进行值拷贝类型转换的构造函数如下。extension Double {init(_ v: UInt8)init(_ v: Int8)init(_ v: UInt16)init(_ v: Int16)init(_ v: UInt32)init(_ v: Int32)init(_ v: UInt64)init(_ v: Int64)init(_ v: UInt)init(_ v: Int)}

基于引用转换的构造函数如下。// 转换操作var value : Int = 100var valueFloat : Float = 3.14var valurDouble : Double = Double(value)var valurDouble2 : Double = Double(valueFloat)// 类型转化class Movie {// 父类声明}class HotMovie : Movie {// 子类声明}var item = HotMovie();let movie = item as? Movie4.真正的面向对象语言

Swift是完全面向对象的语言。自身具有构造函数和析构函数,构造函数是以init开头的函数,而析构函数是以deinit开头的函数。注意,构造函数在创建对象的时候自动调用,不需要程序员额外主动地调用init函数;析构函数是对象声明周期结束的时候自动调用的。class QFPoint {var x : Float = 10.0;var y : Float = 20.0;init() {}init(x:Float, y:Float) {self.x = x;self.y = y;}init(x:Float) {self.x = x;self.y = 0;}convenience init(y: Float) {self.x = 0;self.y = y;}deinit {println("deinit func");}}5.命名空间

对于Swift来说,命名空间也是其中的一个大特性。在后续的iOS开发中,特别是对于一些枚举类型,完全可以只是访问里面不同的部分,前缀相同的内容可以省略。比如下面的代码访问。enum UIControlContentVerticalAlignment : Int {case Centercase Topcase Bottomcase Fill}

对于上述的声明,定义如下:var value : UIControlContentVerticalAlignment

可以按照下列方式进行赋值:

这是使用“.”表示自动推导出value的类型:value = .Center

当然,如果要使用全称,也可以使用如下方式进行访问和赋值:value = UIControlContentVerticalAlignment.Center

如下代码访问也类似:enum UIControlContentHorizontalAlignment : Int {case Centercase Leftcase Rightcase Fill}

访问方式如下:var value2 : UIControlContentHorizontalAlignment = .Centervalue2 = .Leftvalue2 = UIControlContentHorizontalAlignment.Right

注意,尽管这里和上述都有一个.Center,但是因为Xcode编译器能自动推导出value和value2的具体类型。所以这里不会出现歧义。

对于类的命名空间也类似。class IOS {class student {}}class Android {class student {}}var s = IOS.student()var s2 = Android.student()

虽然student对象都一样,但因为是属于两个类中的。在外部看来就是两个不同的类的使用,这样也避免了名字冲突等问题。6.泛型处理

泛型是Swift的重要特性,也是Swift号称安全、类型严格的体现之一。对于Objective-C中的对象可以存放任何对象,但是对于Swift只能存放指定对象或者指定协议的对象。这样从编译代码的层次就限制了类型不匹配的特性。无论是系统自带的数组,还是字典或自定义的对象,都可以使用泛型来处理。当然泛型还有很多方面,在后续章节还会详细阐述。let arr = Array()let dictionary = Dictionary()7.闭包Closure

闭包是现代语言的特性,简单地说就是子函数可以访问父函数里面的对象。Swift的闭包和Objective-C的Blocks有相似之处。Swift闭包也遵守ARC内存管理。对于对象之间通信、回调函数、反向传值等,闭包都发挥了极大的作用。let manager = AFHTTPRequestOperationManager(baseURL: nil);manager.responseSerializer = AFHTTPResponseSerializer();manager.GET(urlpath,parameters: nil,success: {(request: AFHTTPRequestOperation!, obj: AnyObject!)-> Void inprintln("下载成功");}){(request: AFHTTPRequestOperation!, error: NSError!)-> Void inprintln("下载错误\(error) ");}

上述例子是后续网络下载常见的一个闭包处理函数。既有普通的闭包,也有尾部闭包函数,主要用来做回调Callback使用。

1.4 使用Xcode建立Swift项目

下面是从头开始下载建立一个Swift项目。注意从Xcode 6开始才支持Swift编程语言。这里建议下载最新稳定的Xcode 6.x版本进行开发。Beta版本很多语法并没有完善。所以代码可能会有出错的可能。1.下载Xcode最新的安装包

要开发Swift程序,需要把Xcode升级到6.x以后的版本。在https://developer.apple.com中下载最新的Xcode dmg压缩包,如图1.5所示。图1.5 下载Xcode网址2.安装Xcode压缩包

双击xcode_6.dmg文件并安装,如图1.6所示。图1.6 双击xcode_6.dmg文件,拖曳Xcode到Applications文件夹中3.启动Xcode项目

双击Xcode程序,然后单击创建Xcode项目,就会弹出如图1.7所示的对话框。图1.7 创建项目

在图中单击Create a new Xcode project,进入如图1.8所示的对话框。图1.8 创建第一个Swift项目

上面创建项目使用了命令行模式。这是为了减少一些不必要的代码干扰。在对话框里需要填写如下信息。

Product Name:产品名字,可以用汉字名称。

Organization Name:公司组织名字。一般是公司的全称。

Orgnization Identifier:公司Apple ID的前缀。一般是公司域名的倒写。

Bundle Identifier:公司Orgnization Identifier和Product Name的组合体。

Language:选用的语言。这里选择最新的Swift语言。

然后单击下一步“Next”按钮。4.选择存放项目的路径

在如图1.9所示的对话框中选择存放项目的路径。图1.9 选择项目的存放路径

代码模板如图1.10所示。图1.10 Swift代码模板

1.5 Swift使用Playground

Playground是随着Swift推出的“所见即所写”的编程模式。Playground字面意思是操场、游乐场。也就是在Swift中可以一边写代码一边预览编程效果。这给编程开发者或者入门开发者带来前所未有的编程乐趣和体验。下面就来一步一步地学习如何使用Playground。Playground界面如图1.11所示。图1.11 Playground界面

打开Xcode项目,单击Get started with a playground,创建一个Playground项目,如图1.12所示。图1.12 创建Playground界面

然后输入Playground项目的名称,如图1.13所示。图1.13 输入Playground项目名称

单击存放项目的路径,如图1.14所示。图1.14 项目路径

下面是输入OSX代码的例子。如图1.15所示,右边是代码的实时显示结果,左边是代码输入内容。图1.15 OSX代码实例

在iOS中使用Playground,这里输入了UILabel标签控件的实时显示结果,如图1.16所示。图1.16 UILabel标签控件的实时显示结果

可以看出,Xcode的Playground可以实时地显示项目的界面结果。这样可以增加编程的趣味性和交互性。也便于调试和实时地找出错误所在。

第2章 基础知识

类型是所有程序的基础。类型告诉我们数据代表的是什么意思,以及可以对数据进行哪些操作。

Swift语言作为一门用于开发iOS和OSX应用程序的新语言,提供了几种基本数据类型:比如Int、Long、Float、Double等类型。当然,Swift类型不是传统程序语言的类型,在Swift中,它是一个结构体。Int表示整型,Long表示长整形,Double和Float是浮点型,Bool表示布尔型、String表示字符串类型。此外,Swift还有两个有用的集合类型,Array数组和Dictionary字典。

除了基本类型外,Swift还增加了大多数其他程序语言中没有的类型,比如元组(Tuple)。元组可以创建或者传递一组数据,比如作为函数的返回值时,可以用一个元组返回多个值。这对于程序设计尤其有用。

2.1 常量与变量

常量和变量都是把一个名字和某一个指定类型的值关联起来。其中常量的值一旦确定,就不能修改;而变量的值可以在程序中随意更改。

常量在Swift中使用let表示,变量使用var进行声明。2.1.1 常量和变量的声明

要定义一个常量,必须使用let来声明,而定义一个变量,必须使用var来声明。// 声明一个常量,用来存储班级可以容纳的最大人数let maxNumberOfStudents = 50// 声明一个变量,用来保存当前班级的现有人数var numberOfStudents = 32// 声明一个浮点变量,用来保存pi的值let pi = 3.14// 声明一个不可变的常量let key = "姓名"

这里的maxNumberOfStudents是一个常量,在程序中不能被修改。而numberOfStudents是一个变量,在程序的其他位置,可以修改它的值。

在同一行中可以声明多个常量或者变量,并且常量和变量之间用逗号(,)隔开,如下所示:var girlsNumber = 12 , boysNumber = 202.1.2 常量或变量的类型

在声明一个常量或者变量的时候,可以给常量或者变量指定它的类型,表示它用来存储什么类型的值。格式如下:var name : String

表示定义了一个字符串String类型的变量,它的变量名是name,用来存储String类型的值。在变量或者常量名与类型之间需要以(:)分隔。

如果在声明常量或者变量的时候,没有指定它的类型,而是直接赋值,则编译器会根据赋的值进行推断,得到该常量或者变量的类型。如下代码:var name = "千锋"

在这一行代码中,没有指定name的类型,但是编译器会根据给它赋的值进行判断,得出name变量的类型是String。

但是下列代码是有问题的。var value = 200// 如下代码赋值会出错value = "this is swift demo"

上述代码错误的原因是在var value = 200这里,编译器已经把value定义为Int整型类型,整型是没法直接转化成String类型的。所以上述错误的意思是不能进行类型转换。

这里和弱类型语言不同。在很多弱类型语言中,没有严格的类型概念,赋值会在动态运行时进行类型判断。从这里可以看出Swift是一种强类型语言,只不过外表上看起来像弱类型语言。它是借助于Xcode进行自动推导语言类型。这点和弱类型脚本语言有本质区别。2.1.3 常量和变量的命名

在Swift中可以用任何字符作为常量和变量名,包括Unicode字符:let π = 3.14159let 你好 = "你好Swift"let ꆡ = "dog"

常量与变量名不能包含数学符号(+-*/等)、箭头、保留的(或者非法的)Unicode码位、连线与制表符。

不能以数字开头,但是可以在常量与变量名的其他地方包含数字。

一旦将常量或者变量声明为确定的类型,就不能使用相同的名字再次进行声明,或者改变其存储的值的类型。同时,也不能将常量与变量进行互转。注意:在Swift中,尽量不要用关键字作为常量或者变量名,如果一定要用,也是可以的,不过需要用反引号(`)来包含:。let let = "hello"2.1.4 常量和变量的输出

也可以用println或者print函数来输出当前常量或变量的值:let let = "hello"println(let)

使用println输出的内容会在输出字符串的最后加上一个换行符,println或者print,将输出内容到命令行面板上。print("hello world")println("hello ")println("world")

NSLog进行字符串输出。var name = "this is swift demo"NSLog("name is %@", name)let pi = 3.14NSLog("pi is %f", pi)

输出如下所示:

从上面可以看到,NSLog是带有时间戳的显示。这样便于调试。同时NSLog有可变参数和传统的C语言类型处理,比如%d,%f等。2.1.5 字符串的连接输出

print函数中的参数是一个字符串,这个字符串可以是拼接成的,这种方式叫做字符串的格式化操作。

在Swift中,字符串的插值的语法是:在字符串内部加"\(变量名)"。示例如下:var b = truelet studentName = "张三丰"var studentAge = 23print("name is:\(studentName),age is:\(studentAge) ,b :\(b)")

在格式化操作中,可以将其他类型的变量直接插入字符串中,Swift会自动将这些变量的值取出,拼接成字符串,而不需要指定类型或者做类型转换。2.1.6 注释

注释是为了给程序增加可读性的提示,尤其是复杂的程序,如果没有注释,大型的程序会变得非常难以理解。

注释不会增加程序的大小,因为在程序编译时,所有的注释都会被忽略。

在Swift中添加注释的方式有两种:单行注释和多行注释。

单行注释:以两个正斜杠开始的一整行,如下:var age : Int //年龄var name : String? //姓名//这一行是注释

多行注释:其起始标记为单个正斜杠后跟随一个星号(/*),终止标记为一个星号后跟随单个正斜杠(*/),同时,多行注释也可以嵌套出现,如下:/*这是注释第一行/*这是注释*/这是注释第二行这是注释第三行*/

需要注意的是,在程序中书写多行注释时,为了让程序更加易读,尽量在第一行注释的前面都加上注释的标识:/** 这是注释第一行* 这是注释第二行* 这是注释第三行*/2.1.7 分号

与其他大部分编程语言不同,Swift并不强制要求在每条语句的结尾处使用分号(;),当然,也可以沿用之前的习惯。但是,有一种情况下必须要用分号,即当在同一行内写多条独立的语句时,不加分号和iOS/Cocos2D中的lua开发语言类似。var age : Intvar name : String?age = 21 ; name = "千锋"2.1.8 汉字命名方式

Swift自身就支持Unicode编码的任何变量命名方式。也就是它支持中文、韩文、日本、法语等所有国际语言。当然由于编程语言的原因,使用英文命名方式是主流,但是对于一些专业软件,比如公司OA、财务软件、App、这些汉字命名对于Swift来说就方便一些。比如:let 国家 = "中国"var 姓名 = "千锋"姓名 = "千锋Swift"

2.2 类型定义

和其他编程语言一样,Swift既有基础类型,比如Int,Long,Float,Double等,也有元组Tube,Struct,Enum类型。Swift还加入了特有的可选类型。对于可选类型,可能用起来不如之前的类型方便,但是对于安全编程是尤为重要的。2.2.1 整型

整型类型是指没有小数部分的数字,整型类型从是否有符号的角度可分为:有符号整型和无符号整型。

整型类型从其长度可以分为:8位、16位、32位和64位。

所有的整型数值的值范围如下:let maxValueOfInt8 = Int8.maxlet maxValueOfInt16 = Int16.maxlet maxValueOfInt32 = Int32.maxlet maxValueOfInt64 = Int64.maxprintln("int8:\(maxValueOfInt8)\n" +"int16:\(maxValueOfInt16)\n" +"int32:\(maxValueOfInt32)\n " +"int64:\(maxValueOfInt64)")

各种长度的整型的最大值:let maxValueOfUInt8 = UInt8.maxlet maxValueOfUInt16 = UInt16.maxlet maxValueOfUInt32 = UInt32.maxlet maxValueOfUInt64 = UInt64.maxprintln("uint8:\(maxValueOfUInt8)\n" +"uint16:\(maxValueOfUInt16)\n" +"uint32:\(maxValueOfUInt32)\n " +"uint64:\(maxValueOfUInt64)")

输出如下所示:2.2.2 浮点型

浮点数是有小数部分的数字,比如3.14159,0.1和和273.15。

浮点类型比整数类型表示的范围更大,可以存储比Int类型更大或者更小的数字。Swift提供了两种有符号浮点数类型。● Double表示64位浮点数。当需要存储很大或者很高精度的浮点

数时请使用此类型。● Float表示32位浮点数。精度要求不高的话可以使用此类型。2.2.3 自定义类型typealias

Swift可以更加方便地定义各种类型,这里使用typealias进行别名处理。在现实开发程序的过程中,使用类型定义非常有必要。下面是各种类型定义的例子。typealias QFSize = UInttypealias 整数 = Inttypealias 字符串 = Stringvar value : 整数 = 100var name : 字符串 = "千锋"var size : QFSize = 200

上述可以看出,通过typealias定义了“整数”类型,也定义了QFSize作为无符号的整型。在变量声明时就可以使用新的类型。2.2.4 类型安全及类型推导

Swift语言是类型安全的,如果编译器确定一个变量是某一个类型的,就不能再给该变量赋值为其他类型的值。

要让Swift确定一个变量的类型有两个途径:显式声明和类型识别。

显式声明:var str: Stringstr = "hello "str = 123//Cannot convert the expression's type '()' to type 'String'

上述类型转化会出错。不能从字符串类型String转化成整数类型。

类型识别:根据赋值的类型进行类型的识别。var num = 12num = 12.3var num = 12 + 3.2num = 12.3

上面虽然类型不同,但是类型之间是可以相互转化的,因为Swift支持Float和Int,Double和Float之间可以相互转化。2.2.5 常数和数值进制的表示方法

常量

常数是指数字或者字符串等,表示的就是其值本身。

例如,数字3,指的就是整型数值3本身的意义。

"hello"指的就是内容为hello的字符串。

数值进制

在自然语言中,数字都是按照十进制的方式进行表达的,即逢十进一,而在计算机中,数值类型都是以二进制的方式表现的。

为了方便使用及表达,又分为八进制和十六进制。示例如下:let decimalInteger = 17 // 17称为decimalInteger的字面量let binaryInteger = 0b10001 // 二进制的17以0b作为前缀let octalInteger = 0o21 // 八进制的17以0o作为前缀let hexadecimalInteger = 0x11 // 十六进制的17以0x作为前缀

有时候,可以在字面量中添加额外的"_"和"0"来使得字面量更易读:let num = 000_100_000_114println(num)//输出:100000114let fnum = 12.123_330_000println(fnum)//输出:12.123332.2.6 类型转换

类型转化在Swift中是比较严格的,不同类型之间可以认为是不能相互转化的,只能重新产生一个对象和值,并拷贝一份。

整型数值之间的转换

不同类型的常量或者变量存储的数值是不同的,在进行数学运算的时候,不同长度的数值类型是无法进行运算的,如Int8类型和Int16类型。

这个时候需要对变量或者常量进行类型转换:let int8: Int8 = 12let int16: Int16 = 14 + Int16(int8)//int8是Int8类型的常量,在进行运算时,需要进行类型转换println(int16)//输出:26

在整型数值之间进行类型转换时,由短整型向长整型转换都可以成功。但是,如果是长整型向短整型转换则不一定能成功:let int32: Int32 = 1234let int8t:Int8 = Int8(int32)//无法转换,因为1234这个字面已经超出了Int8类型所能表示的最大值(127)println(int8t)

整型数值和浮点型数值之间的转换

在程序开发中,整型数值与浮点型数值之间的转换是很频繁的。

整型转换成浮点型:let intValue = 1234let floatValue = 1.3 + intValue//此时intValue被自动识别为整型数值,故不能与1.3相加

需要的转换如下:let intValue = 1234let floatValue = 1.3 + Double(intValue)println(floatValue)//输出:1235.3

浮点型转换成整型:let floatValue = -1.3let intValue = 1234 + Int(floatValue)//浮点型被转换成整型后,会被截断,即:小数点后的部分会被去掉println(intValue)//输出:1235注意:在表达式中出现常量或者变量,不同于字面量之间的运算,如果是字面量之间的运算,编译器会在编译过程对字面量进行类型识别。

即如下的代码能被正确编译并执行:let floatValue = -1.3let intValue = 1234 - 1.32.2.7 类型别名

类型别名就是给一个类型取一个别名。

类型别名在程序开发中较为常用,比如要封装一套自己的类库,在自己的类库中定义类库所用到的类型,就可以用类型别名:typealias MyInt = Int16println("MyInt类型的最小值是:\(MyInt.min)")//MyInt类型的最小值是:-32768

类型别名创建完成后,其使用方法和普通类型的使用方法一致。let numOfCourse:MyInt = 200 //此时这里的MyInt就是Int16println("课程数量:\(numOfCourse)")//课程数量:2002.2.8 布尔类型

布尔类型的类型名称是Bool,它只可能有两个情况:true或者false。let b: Bool = falselet b2: Bool = trueprintln("b is: \(b) b2 is: \(b2)")//输出:b is: false b2 is: true

Bool类型的变量多用在条件判断语句中:if (b){println("b表示真")}else{println("b表示假")}//输出:b表示真2.2.9 元组Tube

元组语法允许将多个不同类型的值组合成一个复合值并且赋值给一个变量或者常量。元组语法在函数中作为返回值使用是很好的,可以返回多个类型的值。

元组的定义语法如下:var classInfo = ("iOS1404" , 50)

使用这种方式定义一个元组时,读取其中的元素,可以通过元素在元组中的位置进行读取。位置从0下标开始,如下代码中的0和1表示元组中的第0个和第1个元素,以此类推。var classInfo = ("iOS1404" , 50)println("班级名称:\(classInfo.0) 班级人数:\(classInfo.1)")//输出:班级名称:iOS1404 班级人数:50

定义两个元素的元组时,使用以上方法比较简便,但在定义包含多个元素的元组时,这种方法不能明确分辨每个元素所代表的意义。

为了解决这个问题,可以给每个元素进行命名:var classInfo = (className:"iOS1404",classId:"201405",numberOfStudent:50)

对使用命名进行定义的元组,取值时可以同时使用下标和元素名称:println("班级名称:\(classInfo.className) 班级编号:\(classInfo.classId) 班级人数:\(classInfo.2)")//输出:班级名称:iOS1404 班级编号:201405 班级人数:50

获取元组中的元素还有一种较为方便的方法,即通过对元组的分解,进行元素的匹配获取:var classInfo = (className:"iOS1404",classId:"201405",numberOfStudent:50)var (name, id, number) = classInfo;//相当于同时声明了三个变量:name、id 和 number//这行代码会将字符串"iOS1404"匹配给name变量//将字符串"201405"匹配给id//将数字50分配给numberprintln("班级名称:\(name) 班级编号:\(id) 班级人数:\(number)")//输出:班级名称:iOS1404 班级编号:201405 班级人数:50注意:用这种方式定义元组的元素时,实际上是定义了三个变量,如果写成:let (name,id,number)

则表示声明了三个常量,同时name和id是String类型的,而number是整型的。在后面再声明变量或者常量的时候,需要注意不要和这里面的名称发生冲突。也要注意这里面定义的是常量还是变量。

在进行元组的元素分解的时候,如果只需要其中的某些值,可以在不需要值的地方使用"_"来代替。如下代码所示:var classInfo = (className:"iOS1404",classId:"201405",numberOfStudent:50)let (name, _, _) = classInfoprintln(name)//输出:iOS1404

2.3 可选类型Optional

可选类型是Swift特有的数据格式,在现实编程中,经常使用nil或者0来表示没有对象,但是有时候我们需要表示有和没有数据两种情况,数据0也是有数据的一种,这种情况就需要在Swift中使用可选类型来表示。

可选类型的意思为是否这个对象可以有数据,也可以没有数据。

可选类型是Swift语法的特色之处,而对于编程人员来说,这是一个限制,或者不太方便的地方。从安全编程的角度来看,在所有的Swift语言设计中的任何变量、对象都必须具有初始值(在其他语言中并没有这样的要求,也这是最容易编程犯错的地方)。如果没有初始值,程序员必须有清楚而明确的代码意识来表明一个可选类型。这样可减少调试、运行时的错误。2.3.1 可选类型的声明

可选数据类型是在现有的类型后面加上一个问号“?”,表示不确定有没有对该变量进行赋值。格式如下:注意不能在数据类型和?之间加空格。var str: String?

要验证声明为可选类型的变量是否有值可以直接用if语句进行判断:var str: String?if str!= nil {println(str)} else{println(str)}//输出:nil

此时在if语句中的str等同于一个Bool值。注意:这里的str可以理解为String?类型的,而不是String类型的。2.3.2 可选类型的赋值

对可选类型变量的赋值可以直接使用其变量名进行赋值,也可以直接赋值为nil。str = "123"2.3.3 可选类型的使用

在使用可选变量时,需要在变量名的后面加上感叹号"!"。println(str!.toInt())

因为str是被声明为String?类型的,而toInt()方法是String类型的方法,所以不能使用该方法。

对于可能不被赋值的变量,应该声明为可选类型,而在正式使用该变量的时候,需要确定其已经被赋值。"!"的意思就是确定已经被赋值或者确定已经有值。

如果一个可选类型的变量没有被赋值而被使用,结果会报fatal error: Can't unwrap Optional.None错误,也就是程序会崩溃。当然这也方便iOS程序调试。2.3.4 可选类型nil的使用

在Swift语言中,如果一个可选变量没有对应的值,或者之前的值已经失效,可以将其赋值成nil。注意:这里面是可选变量,不是变量,也不是常量,也就是说,对于一个普通变量或者常量来说,不能被赋值成nil。

另外,如果一个变量被声明为可选变量,同时没有被赋值时,则它默认被赋值成了nil。var n:Int?println(n)//输出:nil

2.4 基本运算符

在任何一门编程语言中,运算符都是最基本并且至关重要的,是必须要掌握好的知识。

和其他语言一样,Swift语言也提供了赋值、算术、比较、逻辑等运算符。不同的是在Swift语言中还提供了区间运算符,用来表示两个临界值之间的区域。2.4.1 赋值运算符

赋值运算符即“=”号:如a = b,表示将b的值赋值给a。

赋值运算符的使用场景一般分为下面三种情况。

1.使用字面量对一个常量或者变量进行初始化。

2.将一个常量或者变量的值赋值给另外一个常量或者变量。

3.对元组进行赋值。//使用字面量进行初始化let a = 12var b = 13//常量或变量之间进行赋值let c = bvar d = a//对元组进行赋值let (e,f) = (14,15)println("a=\(a),b=\(b),c=\(c),d=\(d),e=\(e),f=\(f)")//输出:a=12,b=13,c=13,d=12,e=14,f=152.4.2 算术运算符

算术运算符可以分为一元运算符和二元运算符。1.(+/-)一元运算符

+/-作为一元运算符出现有下面两种情况。

1.直接用于数值的前置,表示正数或者负数。

2.前置于变量或者常量,表示对该变量或者常量取正或负。let g = -1let h = -gvar i = +hprintln("g=\(g),h=\(h),i=\(i)")//输出:g=-1,h=1,i=12.(++)自增和(——)自减运算符

自增和自减运算符是比较特殊的运算符,它们都有下面两种用法。

1.前置,表示变量先进行自增或者自减,然后参与运算。

2.后置,表示变量先参与运算,然后进行自增或者自减。

前置var j = 1var k = ++j + 1//j先进行自增,其值变成2,然后+1,所以k的值是3println("j=\(j),k=\(k)")//输出:j=2,k=3

后置var l = 1var m = l++ + 1//l先进行运算,其值是1,进行+1运算的值是2,并赋值给m//所以m的值是2,而l进行运算完成后,进行自增运算结果是2println("l=\(l),m=\(m)")//输出:l=2,m=2

相对应的,自减运算和自增运算一致。var n = 2var p = ——n - 1println("n=\(n),p=\(p)")//输出:n=1,p=0var q = 2var s = q—— + 1println("q=\(q),s=\(s)")//输出:q=1,s=3注意:自增或者自减运算都是对变量本身进行的+或者-运算,所以只能用于变量:let aa = 2aa——//出错:不能对常量进行自增或者自减运算3.二元算术运算符

Swift语言提供的二元算术运算符有5种。

1.加+

2.减-

3.乘*

4.除/

5.取余%var a = 6var b = 3var c = a + bvar d = a - bvar e = a * bvar f = a / bprintln("c=\(c),d=\(d),e=\(e),f=\(f)")//输出:c=9,d=3,e=18,f=2

也可以使用算术运算符进行四则运算:var g = a + b * c - e / fprintln(g)//输出:24

同时,+运算符还可以进行字符串的拼接:let str1 = "hello"let str2 = " world"let str3 = str1 + str2println(str3)//输出:hello world

另外,/运算符在进行数值相除时,如果两个数都是Int类型,相除后的结果也是Int类型:var a = 6var b = 4var c = a / bprintln(c)//输出:1

虽然从数学的角度看,正确的结果是1.5,但是因为a和b都是整数,所以c也会被取整。如果要得到正确的结果,需要使用浮点数进行运算:var a = 6.0var b = 4.0var c = a / bprintln(c)//输出:1.54.(%)取余运算符

和其他的编程语言一样,Swift也提供了取余运算符:var a = 11var b = 4var c = a % b //表示a/b的余数:11 / 4 = 2 余 3,即2*4+3=11println(c)//输出:3

除此之外,Swift还提供了浮点数的取余:var a = 11.5var b = 4.2var c = a % bprintln(c)//输出:3.12.4.3 复合运算符

为了使书写的代码更加简洁,可以使用复合运算符,用于对变量本身的运算,比如当遇到如下情况:var a = 3a = a + 2println(a)//输出:5

在此代码中,变量a本身的值发生了变化,这个时候,可以使用如下方式进行运算:

这里面的“+=”就是复合运算符的一种,常用的复合运算符有:“+=”,“-=”,“*=”,“/=”,“%=”。var a = 3a += 2println(a)//输出:52.4.4 比较运算符

当需要判断某个变量的值是否满足一定的条件时,需要使用比较运算符(比如年龄age的值不能小于0),比较运算符可以用在if的条件判断中。var age = -10if age < 0{println("年龄的值不合法!")}//输出:年龄的值不合法!

常用的比较运算有以下几种。

1.等于(a == b)

2.不等于(a!= b)

3.大于(a > b)

4.小于(a < b)

5.大于等于(a >= b)

6.小于等于(a <= b)2.4.5 三目运算符

在程序中经常会出现如下代码块的情况:var age = 10if age < 0{println("年龄的值不合法!")} else{println("年龄值合法:\(age)")}

这段代码是对一个条件的判断,条件成立和不成立时执行一行相似的代码,这时,可以使用三目运算符来代替,使得代码更加简洁。println(age < 0 ? "年龄的值不合法!" : "年龄值合法:\(age)")

三目运算符的语法是:“条件”?“成立时的代码”:“不成立时的代码”。

三目运算符多用于不同情况下对变量的赋值。2.4.6 区间运算符

区间运算符表示的是一个范围,常用于循环语句中。区间运算符有两种:

1.闭区间:“…”包含最后一个值。for a in 0……5{print("\(a) ")}//输出:0 1 2 3 4 5

2.半闭区间:“..<”不包含最后一个值。for a in 0..<5{print("\(a) ")}//输出:0 1 2 3 42.4.7 逻辑运算符

在if语句的条件中,如果需要多个条件进行组合判断,需要使用逻辑运算符,逻辑运算符有以下三种。

1.!逻辑非,表示对当前条件取反。let allowedEntry : Bool = falseif!allowedEntry {println("ACCESS DENIED")}

上面的!就是取反的操作。注意只能对于Bool类型的变量进行取反。不能对于可选类型进行取反。所以下面的代码是错误的。var str: String?if!str {println(str)}

2.&&表示逻辑与,只有两个条件均为true时,结果才为true。var sex = "male"if age < 18 && sex == "male"{println("男孩")}

3.||:表示逻辑或,只要有一个条件为true,结果就为true。var name = "张三"if name == "张三" || name == "李四"{println("我的好友")}//输出:我的好友

逻辑运算符的中止属性:

1.在逻辑与&&中,如果前一个条件为false,程序不会去判断后一个条件,因为已经能确定结果一定为false。

2.同样,在逻辑或||中,如果前一个条件为true,则程序也不会去判断后一个条件,因为已经能确定结果一定为true。

这个性质在程序中是需要注意的一点,尤其是如果后一个条件语句中包含有对变量值的操作:var i = 10if i < 20 && i++ < 20 {println(i)}//输出:11

如果逻辑与改成逻辑或:var i = 10if i < 20 || i++ < 20 {println(i)}//输出:102.4.8 断言Assert操作

在程序设计中,很多变量的值都是有一定限制的,应符合实际情况,比如人的身高,体重不可能小于0,年龄不可小于0也不可能大于200,两点之间的距离不可能小于0等。

如果在使用这些变量的时候,这些变量是由前面的程序运算得到的结果,当不能保证这些变量的值是否合法的时候,可以使用断言方法来判断这些变量,如果不合法,则中断程序的执行,并且输出相应的错误信息。这样做有利于在编写程序的过程中及时地发现并解决问题。let distance = -1assert(distance >= 0 ,"两点的距离不能小于0")//输出:assertion failed: 两点的距离不能小于0

assert()方法有两个参数:

第一个参数是正确(合理的)条件,返回一个Bool值,当这个返回值为false的时候(即该条件不成立),程序中断。

第二个参数是如果第一个参数返回的Bool值是false时,输出结果,此参数可省略。

第3章 字符串、数组、字典

字符串String、数组Array和字典Dictionary这是任何高级编程语言都具有的基础数据结构,也是程序设计的复合型数据类型。Swift的三大数据结构提供了大量丰富的操作函数和扩展方法。

在Swift基本类型中,包括数组Array和字典Dictionary。要存储批量数据可以使用数组和字典两种集合类型。

3.1 字符串

字符串String在任何一门编程语言中都占有重要的地位,因为程序本质上是用来处理数据,而数据的组成中字符串占有重要的地位,比如表示人的姓名、书的名字、文章的标题等内容。

Swift为字符串提供了很多方便的处理方法,使得使用字符串更加便捷。3.1.1 字符串字面量

先看一个字符串的例子:var str = "hello world"

这行代码的意思是将hello world这个字符串字的面量赋值给str变量。

在Swift语言中,字符串中可以加入任何语言文字的字符:var str = "Swift,你好"

字符串是字符的集合,所以也可以对字符串进行遍历,取出其中所有的单个字符:var str = "iOS,áÛÉ ÇáÚÑÈíÉ你好にほんご"for ch in str{print(" \(ch)")}/*输出:i O S , á Û É Ç á Ú Ñ È í É 你 好 に ほ ん ご*/

转义字符

在字符串中,不仅可以使用字符,还可以加入转义字符,常用的转义字符有以下几种。● \0(空字符)、\\(反斜线)、\t(水平制表符)、\n(换行

符)、\r(回车符)、\"(双引号)、\'(单引号)。● 单字节 Unicode 标量,写成\xnn,其中nn为两位十六进制数。● 双字节 Unicode 标量,写成\unnnn,其中nnnn为四位十六进制数。● 四字节 Unicode 标量,写成\Unnnnnnnn,其中nnnnnnnn为八位

十六进制数。var ch = "\x56 \u5343\t\u950b \nhello"println(ch)/*输出V 千 锋hello*/3.1.2 字符串的连接

两个字符串连接成一个新的字符串的方法如下:var str1 = "hello "var str2 = "world"var str3 = str1 + str2println(str3)//输出:hello world

注意,如果字符串常量不能被修改:var str1 = "hello "let str2 = "world"str2 += str1println(str2)//错误,因为str2被声明为常量3.1.3 字符串与其他数据类型的拼接

字符串与其他数据类型的拼接,又叫字符串的插值操作。var str1 = "hello "var str2 = "\(str1),我的iOS编程之路"println(str2)//输出:hello ,我的iOS编程之路

在Swift中,字符串的插值操作可以将各种类型的值插入到字符串中。var str1 = "PI"var n: Int = 20var b:Bool = falsevar f:Double = 3.14var str2 = "\(str1),\(f),\(n),\(b)"println(str2)//输出:PI,3.14,20,false3.1.4 字符串相关操作方法

任何编程语言都有一大堆基础库,比如C++的STL,Objective-C的Foundation,或者Java的jar库,都含有大量的各种数据类型和结构的操作。其中字符串占有很重要的角色。1.判断字符串是否为空

在对字符串进行声明时,可以为字符串指定一个初始值,以下两种指定方式效果一样,都是给变量指定一个空字符串:var str = ""var str2: String = String()println("str" + (str.isEmpty ? "没有值" : "有值"))println("str2" + (str.isEmpty ? "没有值" : "有值"))/*str没有值str2没有值*/

要判断一个字符串是否为空,可以使用str.isEmpty来判断。2.计算字符串的字符数量

要计算出字符串的字符个数,可以使用countElements方法来计算。var str = "Swift/iOS开发语言"println("字符串的长度:\(countElements(str))")//字符串的长度:133.比较字符串是否相等

两个字符串相等是指两个字符的长度相等,并且每一位的字符也相同。

判断字符串相等的方法是:==。var str1 = "hello world"var str2 = "hello world"if str1 == str2 {println("str1和str2相等")}//输出:str1和str2相等

字符串比较大小是逐位进行比较的。也就是说,分别取出两个字符串的第一位,比较大小,如果相同则再取出第二位进行比较,依此类推,直到两个字符不相同为止。

最后返回其Unicode编码值的比较结果:var s = "hello"var s2 = "world"println(s > s2)//输出:false4.判断字符串的前缀和后缀

在项目开发中,字符串的前缀和后缀使用的频率较高,比如,要判断一个字符串是不是一个合法的URL地址,或者判断一个字符串是不是一张图片。

判断字符串是否包含某个字符串前缀(hasPrefix)或者后缀(hasSuffix)的方法如下:var str = "http://www.1000phone.com/123.png"if str.hasPrefix("http://"){println("是一个网址")}if str.hasSuffix(".png") || str.hasSuffix(".jpg"){println("是一张图片文件")}/*输出是一个网址是一张图片文件*/5.将字符串转换成大写或者小写

Swift提供了将字符串全部转换成对应小写(lowercaseString)或者大写(uppercaseString)的方法:let normal = "HELLO Swift"let shouty = normal.uppercaseStringprintln(shouty)println(normal.lowercaseString)/*HELLO SWIFThello swift*/3.1.5 与其他类型的转换

将其他的数据类型转换成字符串,只需要使用插值操作就可以。var n:Double = 3.14159var str:String = ("\(n)")

将字符串转换成Int类型的值。var str:String = "12"let n:Int? = str.toInt()println(n)//输出:12

要将字符串转换成其他类型,需要借助其他方法,例如:var str:String = "12.1"let ns:NSString = str;//NSString是OC中的类型let d = ns.doubleValueprintln(d) //输出:12.1

3.2 数组

Swift语言中提供的数组要求所保存的数据类型一致,它们是类型安全的,在使用的时候,能够明确其中保存的数据类型,数组用来存储有序的数据。3.2.1 数组的声明及初始化

数组用来保存同一类型的一组数据元素。在Swift中,数组中所存放的元素的数据类型必须是一样的,而且可以存放基本数据类型,也可以存放类的对象。

数组的元素类型可以在声明中指定,也可以由后面的赋值进行识别。

声明及初始化

声明一个数组的方式有如下几种:var intList = [12,13,54]

该方法声明了一个数组,并且给数组赋初始值,同时,也可以识别出intList中存放的元素都是Int类型的。var intList2: [Int]intList2 = []

该方法声明了一个数组intList2,并且显式指定了数组中存放元素的类型,但是这个数组没有被初始化,所以不能使用,在使用之前需要对数组进行初始化。

初始化时可以没有元素,表示空数组,此时数组初始化完成,并且也指定了数组的类型,就可以使用该数组了。var intList3 = [Int]()

上面声明一个空存放Int的数组,注意这是一种简便的语法方式。下列代码才是标准正规的写法。var intList4 = Array()

上面声明一个空存放Int的数组。这是最标准正规的写法,但是不如上述写法简洁。

从上可见,要使用一个数组,有两个前提:

1.需要对数组进行初始化,在初始化时可以赋值元素,也可以直接使用[]表示一个空数组。

2.必须指定元素类型,可以显式地指定,也可以通过初始化,让编译器识别出类型。

如下方式声明数组是错误的:var doubleList = []//在这个数组的声明中,编译器无法得知该数组元素的类型,所以数组无法使用doubleList.append(12)//错误var strList: [String]//在这个数组的声明中,虽然指定了数组元素的类型,但是没有对数组进行初始化,所以数组无法使用strList.append("hello")//错误

要正确使用数组,可进行如下修改:var doubleList: [Double] = []doubleList.append(12)var strList: [String]strList = []strList.append("hello")

构造方法

除了以上定义数组的方法,还可以使用构造方法进行构造并使用:var list = [Int]()//声明并初始化一个空数组,元素的数据类型为Intlist.append(3)//数组为:[3]

除此之外,还可以在数组的构造方法中指定数组的信息,生成一个带有初始值的数组:var list = [Int](count:5, repeatedValue:4)//参数count:指定初始状态下,该数组元素的个数//参数:repeatedValue:指定初始状态下,每个元素的值for n in list{print(" \(n)")}//输出: 4 4 4 4 4

当然,用这种方法生成一个数组,也可以利用类型识别,使用Array方法:var list = Array(count:3, repeatedValue:"swift")for str in list{print("\(str) ")}//输出:swift swift swift3.2.2 数组元素的访问与修改1.数组元素的访问

要访问数组的元素,可以通过下标来访问,数组中的元素下标是从0开始编号的:var list = ["hello", "Swift"]println("第一个元素:\(list[0])")println("第二个元素:\(list[1])")/*输出:第一个元素:hello第二个元素:Swift*/2.获取数组属性

要想得到数组的元素个数,可以通过数组的只读属性count来获取:var list = ["hello", "Swift"]println("数组共有\(list.count)个元素")//输出:数组共有两个元素

判断数组是否为空,有两种方式://一.通过判断数组的元素个数是否为0来判断var list: [String] = []if list.count == 0{println("数组是空的")}//输出:数组是空的//二.通过数组的isEmpty属性来判断var list: [String] = []if list.isEmpty{println("数组是空的")}//输出:数组是空的3.修改数组的元素(1)修改数组中的单个元素var list = ["欢迎", "学习", "Swift"]list[2] = "iOS开发"println(list)//输出:[欢迎, 学习, iOS开发](2)批量修改数组元素var list = ["刘备","关羽","张飞","赵子龙"]list[0……2] = ["曹操","孙权"]//表示数组的第0个元素到第2个元素变成新的数组中的元素println(list)//输出:[曹操, 孙权, 赵子龙]3.2.3 数组的遍历

对数组的遍历,一般可以采取如下3种方式:1.for循环var list = ["欢迎", "学习", "Swift"]for(var i = 0 ; i3.3 字典

Swift语言中的字典和数组一样,都要求所保存的数据类型一致,它们是类型安全的,在使用的时候,能够明确其中保存的数据类型。数组用来存储有序的数据,而字典是用来存储无序数据的,但是需要给每一个数据都指定对应的key值,也就是说字典中的数据是以键值对的形式存在的。

这里的数组和字典比Objective-C更加安全和严格。这点上和Java里面的语义类似。

字典是用来存放多组键(key)值(value)对的集合类型。在Swift中要求键和值的类型保持一致。

在Swift中,字典的键和值可以是任何类型。但是键必须实现哈希协议Hashable。3.3.1 字典的声明及初始化

在Swift中,可以使用以下几种语法格式进行声明。var dic1 = [1:1, 2:2, 3:3]var dic2:Dictionary = [:]var dic3 = Dictionary()var dic4 = [String:String]()

从以上三种方法中可以看出,要正确使用字典,需要下面的前提条件。

1.键值对的键和值的类型需要明确,可以显式声明,也可以通过类型识别进行确定。

2.声明完字典后,需要初始化。

3.字典的key值的类型必须是可被哈希Hashable的(基本数据类型和可被哈希的类)。3.3.2 字典元素的访问与修改1.字典的元素数量

可通过count属性来获取字典的元素数量。var dic = [1:1,2:2,3:3]println(dic.count)//输出:32.字典元素的访问

可通过key值进行字典元素的取值。var dic = ["key1":"iOS" ,"key2":"Swift" ,"key3":"Android"]var str = dic["key2"]println(str)var strNotExists = dic["key"]println(strNotExists)//输出://Swift//nil

如果取值时,所使用的key值不存在,返回的值为nil。3.增加字典元素

可以通过直接赋值的方式给字典增加元素。var dic = ["key1":"iOS" ,"key2":"Swift" ,"key3":"Android"]dic["key"] = "Objective-C"println(dic)//输出:[key2: Swift, key3: Android, key: Objective-C, key1: iOS]4.字典元素的修改

修改字典元素有下面两种方式。(1)直接修改var dic = ["key1":"iOS" ,"key2":"Swift" ,"key3":"Android"]dic["key1"] = "oc"println(dic)//输出:[key2: Swift, key3: Android, key1: oc](2)通过updateValue(forKey:)方法修改var dic = ["key1":"iOS" ,"key2":"Swift" ,"key3":"Android"]let str = dic.updateValue("Objective-C",forKey:"key2")println(str)println(dic)//输出://Swift//[key2: Objective-C, key3: Android, key1: iOS]

使用这个方法修改元素,返回值是该key值对应的value被修改之前的值。5.字典元素的删除

可以通过removeValueForKey方法删除字典的元素。var dic = ["key1":"iOS" ,"key2":"Swift" ,"key3":"Android"]let str = dic.removeValueForKey("key3")println(str)println(dic)//输出://Android//[key2: Swift, key1: iOS]

用该方法删除字典的元素,返回值是被删除的元素的value值。3.3.3 字典的遍历

字典的遍历有三种情况:1.遍历字典的键值对var dic = ["key1":"iOS" ,"key2":"Swift" ,"key3":"Android"]for (key,value) in dic{println("key:\(key), value:\(value)")}//输出://key:key2, value:Swift//key:key3, value:Android//key:key1, value:iOS2.遍历字典的所有键var dic = ["key1":"iOS" ,"key2":"Swift" ,"key3":"Android"]for str in dic.keys{print(str + " ")}//输出:key2 key3 key1

也可以先取出字典的value的数组:var dic = ["key1":"iOS" ,"key2":"Swift" ,"key3":"Android"]var keys = Array(dic.keys)println(keys)//输出:[key2, key3, key1]3.遍历字典的所有值var dic = ["key1":"iOS" ,"key2":"Swift" ,"key3":"Android"]for str in dic.values{print(str + " ")}//输出:Swift Android iOS

同样,也可以先取出字典所有value值的数组:var dic = ["key1":"iOS" ,"key2":"Swift" ,"key3":"Android"]var values = Array(dic.values)println(values)//输出:[Swift, Android, iOS]

第4章 控制语句和函数

控制语句是实现程序结构和逻辑的重要语法。使用控制语句可以实现复杂的逻辑代码。

和大多数语言一样,Swfit语言也提供了两种重要的控制语句。

1.分支语句:根据不同的条件执行不同的代码。

2.循环语句:根据需要重复执行一部分代码。

4.1 分支结构

分支结构主要用来处理在不同的条件下产生不同行为的情形。分支语句包括if和switch两种语句。在程序开发中扮演了很重要的角色。对于条件判断较少的情况下if有比较强的优势,但是在条件判断很多,判断依据又很相似的情况下,用switch更加合适。4.1.1 if条件语句

简单的if语句的语法如下:var age = 20;if(age > 18){println("成年人")}//输出:成年人

在if语句中,if后面的(age > 18)是条件,当满足该条件后,会执行后面的代码块{ }中的代码。

在Swift中,也可以将条件外面的小括号()去掉:if age > 18 {println("成年人")}//输出:成年人

这几行代码可以翻译为:如果年龄大于18,则打印出“成年人”三个字。

在if语句的后面,也可以加上else语句。var age = 10;if age > 18 {println("成年人")} else {println("未成年")}//输出:未成年

这几行代码可以翻译为:如果年龄大于18,则输出“成年人”三个字,否则输出“未成年”三个字。

根据需要,也可以选择更为复杂的结构:var age = 10;if age > 18 {println("成年")} else if age > 12{println("少年")} else if age > 3{println("童年")} else {println("幼年")}//输出:童年4.1.2 switch语句

switch语句处理的是某个值不同情况下触发的解决方法。比如手机有不同的模式:静音模式、飞行模式、户外模式、会议模式等。在不同的模式下处理来电是不一样的。这时可以选择用switch语句来处理:var mode = "飞行"switch mode{case "户外":println("现在为户外模式,音量最大")case "飞行":println("现在为飞行模式,电话无法接通")case "静音":println("现在为静音模式,没有声音")default:println("其他")}//输出:现在为飞行模式,电话无法接通1.区间匹配

另外Swift语言中的switch比传统开发语言中的更加强大。它可以表示某个范围和条件。case语句中可以使用区间匹配,if-else中的例子也可以改写成以下形式:var age = 1000switch age{case 0……3:println("幼年")case 3……12:println("童年")case 2……17:println("少年")case 18……200:println("成年")default:println("该年龄值不合理")}//输出:该年龄值不合理

在switch-case语句中,需要在所有的case表达式的最后加上一个default:语句,用来处理其他情况。2.元组

在switch-case语句中,还可以使用元组来匹配多个值。let boy = (10, "boy")switch boy{case (0……20, "boy"):println("男孩")case (21……100, "man"):println("男人")default:println("其他")}//输出:男孩3.值绑定

可以在case语句中用变量来接收满足其他条件的元组值,在下面这个例子中,只要boy的第二个参数满足条件,就会被case中的条件接收并将第一个参数赋值给age。

如果前面的条件都不满足,则会进入最后一个case,同时接收两个值,同时,因为有了最后一个case,所以不需要使用default:语句。let boy = (10, "girl")switch boy{case (let age, "boy"):println("男孩:\(age)")case (let age, "man"):println("男人:\(age)")case (let age, let sex):println("\(sex):\(age)")}//输出:girl:104.Where语句

在case语句中,除了以上几种方式外,Swift还提供了更加灵活的条件判断方式:即配合元组和where语句实现复杂的条件:let boy = (50, "man")switch boy{case let (age,sex) where age < 12 && sex!= "boy" && sex!= "man":println("女孩:\(age)岁")case let (age,sex) where age > 12 && sex == "man":println("男人:\(age)")case let (age,sex):println("\(sex):\(age)")}//输出:男人:50

4.2 语句的作用域

作用域是指独立的代码块,即一段用{ }包含起来的代码片段,在代码块中可以使用在这个代码块之前的外部定义的变量:var str = "swift"if true {println(str)//输出:swift}

相反,在代码块中定义的变量,在代码之外是无法使用的:if true {var str = "iOS"}println(str)//错误:str没有定义

另外,在代码块中可以定义与外部变量名相同的变量:var str = "swift"if true{var str = "iOS"println(str)//输出:iOS}

如果是相同的变量名字,那么根据就近原则,将使用本作用域代码块中的变量,而不会使用上一个作用域里面的变量。

4.3 循环结构

很多时候,一个代码块需要重复地运行,比如数的累加:1+2+3+4+...+99+100。依靠循环可以解决计算机程序处理中很多重要的问题。这个时候,就需要借助于循环来解决。

在Swift语言,有两个循环结构:

1.for循环(细分为for循环和forin循环)

2.while循环(细分为while循环和do-while循环)4.3.1 for循环

for循环的结构如下:var sum = 0for var i = 1 ; i <= 100 ; i++ {sum += i}println(sum)//输出:5050

for循环的结构为:for语句1;语句2;语句3{代码块1}

执行顺序如下。

1.语句1:进入循环后执行的第一条语句,同时也只执行一次。

2.语句2:这是一条条件判断语句,如果此条件为true,则继续往下执行。

3.代码块1:语句2判断为true后,执行代码块的内容。

4.语句3:代码块执行完成后,执行语句3的内容。

5.语句2:语句3执行完后,再次进行条件的判断。

……(重复2~4的过程)

循环结构的退出方式有两种:

1.语句2的条件结果为false。

2.在代码中有break语句(具体参考下一节中的break语句)。4.3.2 forin循环

forin循环主要用于遍历一个区间、一个数组或者一个字符串中的字符。1.区间var sum = 0for i in 1……100 {sum += i}println(sum)//输出:50502.数组var strArray = ["Swift","Objective-C","Java"]for str in strArray {print(str + " ")}//输出:Swift Objective-C Java3.字符串var str = "Swift"for ch in str {print(ch + " ")}//输出:S w i f t4.3.3 while循环

while循环,会一直循环执行循环体{ }内的代码,直到while后面的条件返回false。var sum = 0var i = 0while i <= 100{sum += ii++}println(sum)//输出:50504.3.4 do-while循环

do-while循环是while循环的另外一种形式。var sum = 0var i = 0do{sum += ii++} while i <= 100println(sum)//输出:5050

do-while循环和while循环的区别是:

在while循环中,首先判断条件是否满足,如果满足则进入第一次循环,如果不满足,则跳过循环体。

在do-while循环中,不管条件是否满足,都会进入循环体执行第一次循环,然后再进行条件判断。

也就是说,do-while循环无论如果都至少会执行一次循环体。

4.4 跳转语句及块标签

在程序的执行过程中,有时候需要改变代码的执行顺序。比如在循环过程中,遇到了某些特定的情况,需要跳出循环。这时候,需要采用程序跳转语句对程序的结构进行更多的控制。4.4.1 continue语句

continue语句用在循环结构中,作用是结束本次循环,并开始下一次循环。也就是说会忽略掉continue语句之后的代码。var strArray = ["Hello", "Swift", "iOS", "Java"]for str in strArray{if str.hasPrefix("S"){continue}print(str + " ")}//输出:Hello iOS Java

这段代码是遍历数组中所有的字符串,并且过滤掉以“H”开头的字符串。4.4.2 break语句

break语句主要用在循环语句中,作用是中止循环并跳出循环。var strArray = ["Hello", "Swift", "iOS", "Java"]for str in strArray{if str.hasPrefix("S"){break}print(str + " ")}//输出:Hello

这段代码在遍历数组元素的过程中,当遇到以“H”开头的元素后,中止循环,程序会跳转到for循环体以外,继续执行下面的代码。4.4.3 fallthrough语句

Swift语言中的switch-case语句与C语言中的不一样。C语言的case语句具有跟随效应,如果没有break语句,一个case执行完成后会继续执行下一个case。

在C语言中,当某一个case条件为true后,执行相应的代码块,并且会继续向下执行下面的case语句中的代码。如果要中止向下执行,需要手动加入break语句。

在Swift语言中,默认情况下是在每一个case语句的末尾存在一个break语句的。

如果要实现与C语言一样的功能,可以在case的代码块中加入fallthrough语句:var age = 1switch age{case 0……3:println("幼年")fallthrough//case的条件成立,同时,这里有fallthrough语句,所以会继续执行下一个case的代码块case 3……12: //虽然这里的case条件不满足,但是依然会执行其代码块fallthrough//此处有fallthrough语句,所以程序还会继续跳转到下一个case语句的代码块println("童年")//这行代码在fallthrough语句后面,所以不会执行到case 2……17:println("少年")//这个case语句中没有fallthrough语句,所以不会继续往下执行case 18……200:println("成年")fallthroughdefault:println("该年龄值不合理")}//输出://幼年//少年

从这段代码的输出结果可以看出:

1.如果某一个case条件成立,并且在对应的代码块中有fallthrough语句,则无论下一条case语句的条件是否满足,都会向下执行下一个case语句的代码块。

2.同一个case代码块中的fallthrough语句后面的内容将无法执行。

3.所有的case分支中默认存在一个break语句,如果没有fallthrough语句,将退出switch-case语句。

4.5 函数

在程序开发的过程中,某些逻辑处理会被经常使用,或者在处理复杂问题时,需要将独立的功能模块提取出来,供其他地方使用,这种方法就是函数的思想。4.5.1 函数的定义及调用方法

在Swift语言中,函数的定义非常灵活,无论是参数还是返回值都有多种形式。

函数的定义方式如下://定义一个函数,返回值是Int类型func add(a:Int, b:Int) -> Int{return a+b}//调用函数println(add(12,34))//输出:46

定义一个函数需要用“func”关键字修饰。add是函数名,括号“()”内是参数列表,“->”后面是返回值类型。

如果定义的一个函数带有返回值,在函数实现中需要返回值,返回值使用“return”关键字。

当定义好一个函数后,可以在程序的任意位置使用该函数,并且可以嵌套使用函数:var sum = add(34,40)println(sum)//输出:74var num1 = 12var num2 = 34var num3 = 40var sum2 = add(num1,add(num2, num3))println(sum2)//输出86

函数是能够实现特定功能的独立的代码块,参数是这个代码块要处理的数据,而返回值则是这些数据经过代码块处理后的结果。4.5.2 函数的参数

函数的参数是外部向函数体传递的数据。也是函数要处理的数据对象。参数的类型可以是任意类型。1.无参函数

顾名思义,无参函数就是没有参数的函数,比如要显示给用户的警告信息,可以实现一个无参函数。func showWarning(){println("警告")}showWarning()//输出:警告2.带参数函数

当然,显示的警告信息也可以带有一个参数,表示具体要警告的内容:func showWarning(str:String){println("警告:" + str)}showWarning("内存不足")//输出:警告:内存不足3.参数标签

在上面的例子中,定义完一个函数后,在使用时,所有的参数都会以字面量或者实参的方式传递到函数体内,当函数的参数数量过多时,每一个参数的意义将不能明确。比如,需要定义一个函数来输出学生的信息:func showStudentInfo(name:String, age:Int,height:Int, phoneNumber:String){println("学生:\(name) 年龄:\(age) 身高:\(height) 电话号码:\(phoneNumber)")}showStudentInfo("张三",21,75,"13000000000")//输出:学生:张三 年龄:21 身高:75 电话号码:13000000000

在这个例子中,函数调用时,参数21和75并不能明确它的具体意义。虽然在函数的声明中age和height能表明这两个参数表示年龄和身高,但是在大量使用这个函数后,要想知道这两个参数所指的意义,都需要回到函数定义处进行查看,这对于程序开发将是很不方便的。

为了解决这个问题,Swift语言引入了参数标签,即:可以给每一个参数指定一个名字,在函数被调用时,可以通过该标签来获知对应参数的具体含义。func showStudentInfo(sName name:String, sAge age:Int,sHeight height:Int, sPhone phoneNumber:String){println("学生:\(name) 年龄:\(age) 身高:\(height) 电话号码:\(phoneNumber)")}showStudentInfo(sName:"张三", sAge:21,sHeight:75, sPhone:"13000000000")//输出:学生:张三 年龄:21 身高:75 电话号码:13000000000

在以上定义的函数的参数列表中,给每一个参数指定了一个名称:func showStudentInfo(sName name:String, sAge age:Int,sHeight height:Int, sPhone phoneNumber:String)

如第一个参数为sName name:String。

sName表示在外部调用该函数时,需要显式地写出该参数的名称: sName:"张三",name是在函数体内部使用的常量。

另外,如果在外部使用的参数标签与函数体内部使用的常量名称一致时,可以使用"#"号来表示:func showStudentInfo(#name:String, #age:Int,#height:Int, #phoneNumber:String){println("学生:\(name) 年龄:\(age) 身高:\(height) 电话号码:\(phoneNumber)")}showStudentInfo(name:"张三", age:21,height:75, phoneNumber:"13000000000")//输出:学生:张三 年龄:21 身高:75 电话号码:130000000004.5.3 函数的返回值

函数的返回值即函数的参数经过函数体的处理后得到的处理结果。例如,定义一个求两个整数的最大公约数的函数:func greatestCommonDivisor(a:Int, b:Int) -> Int{var localA = avar localB = bvar localC = localA % localBwhile localC!= 0 {localA = localBlocalB = localClocalC = localA % localB}return localB;}println(greatestCommonDivisor(72,68))//输出:4

在这个函数中,a和b是两个待求解的整数(即参数),经过函数的运算后得到这两个数的最大公约数并返回。4.5.4 函数的变量参数

所有以上定义的函数的参数都是常量,即在函数体中不能修改其值。func function(a:Int){a = 12 //错误,a是常量,不能修改}

在函数的运算过程中,经常需要对传递进来的参数进行修改,比如以上的求两个数的最大公约数的函数,因为无法修改参数的值,所以只能在函数的内部定义局部变量来接收参数,这样使得函数体的实现不够灵活。为了解决这个问题,可以在函数的参数前面使用var关键字来修饰,表示该参数是以变量的形式存在于函数体内:func greatestCommonDivisor(var a:Int, var b:Int) -> Int{var c = a % bwhile c!= 0 {a = bb = cc = a % b}return b;}println(greatestCommonDivisor(54,68))//输出:24.5.5 函数的类型

函数和变量或者常量一样,也有类型,也可以当作其他函数的参数。当需要往函数中传递一个函数时,在函数的参数列表中,需要声明一个函数变量,而这个变量的类型必须是函数类型,比如对于两个数的操作有多种,求最大公约数、最小公倍数、约分和基本运算等。而这些操作都有相同的函数结构。func sumFuncName(var a:Int, var b:Int) -> Int

这个方法的声明中,参数列表和返回值构成了这个函数的类型:(Int, Int)->Int

这个类型就是函数的参数类型://求两个数的最小公倍数func leaseCmmonMultiple(a:Int, b:Int) -> Int{return a * b / greatestCommonDivisor(a , b)}/***定义一个函数,并向函数中传入另外一个用于实际运算的函数*funcAction是一个函数,函数类型是:(Int, Int)->Int)**/func getResOf(#a:Int, #b:Int,funcAction:(Int, Int)->Int) -> Int{return funcAction(a, b)}//在调用时,直接传入已经定义的函数名称println(getResOf(a:12,b:16,leaseCmmonMultiple))//输出:484.5.6 函数的嵌套

函数可以嵌套定义,当一个函数内部需要再细分功能模块时,可以使用嵌套的函数进行处理:func someCalcAction(#a:Int, #b:Int, #type:Int) -> Int{func greatestCommonDivisor(var a:Int, var b:Int) -> Int{var c = a % bwhile c!= 0 {a = bb = cc = a % b}return b;}func leaseCmmonMultiple(a:Int, b:Int) -> Int{return a * b / greatestCommonDivisor(a , b)}//根据type的值返回相应的结果,0表示最大公约数,1表示最小公倍数switch type{case 0:return greatestCommonDivisor(a, b)case 1:return leaseCmmonMultiple(a, b)default:return -1;}}println(someCalcAction(a:12,b:16,type:0))//最大公约数:4println(someCalcAction(a:12,b:16,type:1))//最小公倍数:48println(someCalcAction(a:12,b:16,type:2))//不存在,所以返回-1

第5章 枚举和结构体

枚举enum/Enumeration和结构体与C/C++中的类似,用于别名定义一些有限的类型以及一些复杂的数据结构。但是Swift中的枚举除了具有基本的类型限制和别名使用外,还可以进行继承以及遵守协议。

结构体struct/Structure和后续的类在很大程度上是相似的。但是结构体是一个值拷贝的数据类型。主要用来定义数据模型。它具有面向对象的特点,可以进行继承、遵守协议、有构造函数等特点。

5.1 枚举

枚举是一种基本数据类型。它用于声明一组命名的常数,设置变量有几种可能的取值。它用来创建一种新型变量。这种变量能设置为已经定义的一组之中的一个,有效地防止用户提供无效值。该变量可使代码更加清晰。枚举可以描述特定的值。5.1.1 枚举的声明

枚举Enumeration用来定义一组相关信息,比如,说到行进方向可以联想到前、后、左、右4个方向,那么这里可以定义一个方向的枚举,其中保存4个值:前、后、左、右。这时,不管是哪个方向都可以赋值给某一个方向的变量。

声明一个枚举需要使用enum关键字:enum Toward{case Forwardcase Backcase Leftcase Right}

case关键字表示增加一个枚举定义值。另外,所有的枚举值可以被写在一行中:enum Toward{case Forward, Back, Left, Right}5.1.2 枚举的值

枚举元素的值,也要枚举的原始值。枚举只有在指定了类型之后才可能有原始值。

要得到枚举的原始值,可以用toRaw()方法获取:let goTo = Toward.Back //取得枚举中的某个定义的值println(goTo.toRaw()) //错误,因为枚举并没有对每个元素赋相应的值

在以上的定义中,Forward等四个枚举值并没有被赋值具体的值(这与C语言不同)。

如果要给枚举中定义的所有枚举值赋值,需要指定枚举的类型:enum Toward: Int {case Forward, Back, Left, Right}

这时,枚举中的四个元素被默认赋值成从0开始的整数。enum Toward: Int {case Forward, Back, Left, Right}let goTo = Toward.Backprintln(goTo.toRaw())//输出:1

也可以手动给枚举的每一个元素赋值:enum Toward: Int {case Forward = 2, Back, Left = 10, Right}//当给某一个元素赋值后,后面的元素会根据该元素的值依次赋值let goTo = Toward.Backprintln(goTo.toRaw())//输出:3println(Toward.Right.toRaw())//输出:11

在Swift中,枚举可以被指定为其他类型,比如String:enum Toward: String{case Forward = "f", Back = "b", Left = "l", Right = "r"}println(Toward.Back.toRaw())//输出:b注意:如果指定枚举的类型为非Int类型,需要给每一个元素指定值,并且每一个值都必须是唯一的。以下情况是错误的:enum Toward: Double{case Forward = 1.2case Back = 1.2 //错误,因为1.2这个值已经存在case Left = 3.4case Right//错误,因为这个枚举被指定为Double类型,必须给每个元素指定值}

可以通过原始值得到枚举中对应的定义元素,方法是:fromRaw()。enum Toward: Int{case Forward = 2, Back, Left = 10, Right}let toDirect = Toward.fromRaw(11)//这里toDirect的类型是可选的Toward,因为在枚举定义中不一定存在原始值为11的成员//此时toDirect的值为Toward.Right5.1.3 枚举的使用方法

枚举使用的场景较多,但是用法相对单一,一般都是用来表示一组相互关联的情况,或者数值。

在各种软件或者网页中,都存在按钮,按钮的点击事件可以分为很多种:

1.单击(在按钮的范围内按下并弹起)

2.双击

3.按下

……

可以模拟按钮的响应事件,如下://定义一个枚举,每一个元素表示一种事件enum TouchEvent{case TouchUpInSidecase TouchDowncase DoubleClick}//定义一个事件,并指定其类型为TouchEventvar event:TouchEvent//定义event的事件类型为单击event = TouchEvent.TouchUpInSide//处理事件switch event {case .TouchUpInSide:println("按钮被单击")case .TouchDown:println("按钮被按下")case .DoubleClick:println("按钮被双击")}//输出:按钮被单击

在switch-case中必须覆盖所有枚举的成员情况。如果某些情况不需要单独处理,则必须使用default进行处理。switch event {case .TouchUpInSide:println("按钮被单击")case .DoubleClick:println("按钮被双击")default:println("其他情况")}

5.2 结构体

结构体可以用来保存某一事物的一组信息,比如,学生的属性:学号、姓名、性别、联系方式、家庭住址等。这些属性都是学生所拥有的。

与枚举不同,一个变量被声明为某个枚举类型后,它的值仅仅是枚举类型所定义的一组值中的一个。而一个变量被声明为结构体类型后,它包含了结构体中定义的所有值。5.2.1 结构体的声明和定义

可以用struct关键字声明一个结构体:struct Student{var stuId: Int;//学号var stuName: String;//姓名var stuSex: Bool;//性别var stuPhone: String;//联系方式}

在进行结构体声明的时候,也可以不指定属性的类型,而是给每个属性赋予一个默认值,并通过类型识别获取所有属性的类型:struct Student{var stuId = 0var stuName = ""var stuSex = truevar stuPhone = ""}5.2.2 结构体的构造方法

有两种方法可以构造一个结构体实例,这两种构造方法分别如下。1.空参构造方法var zhang = Student();

结构体成员变量必须有初始值,所以若使用以上第一种方法定义一个结构体,则不能使用空参构造方法。因为结构体里面没有初始值,所以编译器会报错。2.全参构造方法var xiaoLi = Student(stuId:12, stuName:"李雷",stuSex:true, stuPhone:"13800000000");

全参构造方法中必须对结构体中的每一个属性进行赋值。注意这里的字段必须按照顺序进行。而且构造函数里面的参数名字和字段名字要完全相同。

不管使用哪一种构造方法,都可以使用全参构造方法,使用全参构造方法会将结构体默认的属性值覆盖。5.2.3 结构体的赋值和取值

可以通过结构体的属性名称对结构体实例的属性进行访问。这里结构和字段之间通过点“.”语法进行访问。println(xiaoLi.stuName)//输出:李雷

在结构体实例生成后,可以对其属性进行赋值和修改:var zhang = Student();zhang.stuName = "张无忌"println(zhang.stuName);//输出:张无忌

对结构体实例的属性进行赋值后,该属性的值会被覆盖成新的值:var zhang = Student();zhang.stuName = "张无忌"zhang.stuName = "张三丰"println(zhang.stuName);//输出:张三丰5.2.4 结构体的嵌套

结构体的定义可以嵌套,比如,学生有一个属性是他所在的班级,而班级是另外一个结构体,每个班级都有一个对应的班主任:

定义一个老师结构体:struct Teacher{var tId = 0var tName = ""}

定义一个班级结构体:struct ClassRoom {var classId = 0var className = ""var classCharge = Teacher() //班主任}

定义学生结构体:struct Student{var classInfo = ClassRoom()var stuId = 0var stuName = ""var stuSex = truevar stuPhone = ""}

此时定义一个学生,并对学生的信息进行赋值:var zhao = Student()zhao.stuName = "赵敏"zhao.classInfo.className = "iOS开发"zhao.classInfo.classCharge.tName = "张三丰"println("学生:" + zhao.stuName + " 所在班级:" + zhao.classInfo.className + " 班主任是:" + zhao.classInfo.classCharge.tName)//输出:学生:赵敏 所在班级:iOS开发 班主任是:张三丰

在结构体的嵌套中,对实例属性的访问使用链式方式进行访问。也就是结构体可以一层一层地进行嵌套读写操作。5.2.5 结构体是值拷贝类型

值类型是相对于引用类型(在后续类中会详细解释)说的,是指两个结构体实例变量之间进行赋值时,是对结构体所有内容的拷贝,也就是说,对其中一个实例的属性进行修改后,不会影响另外一个实例的内容。

例如:var zhu = zhaozhu.stuName = "朱元璋"println(zhu.stuName)println(zhao.stuName)//输出://朱元璋//赵敏

第6章 类

类是面向对象编程的基础,类是对一些具有相同的属性和方法的具体事物的抽象。比如人类、文具类、饮料类、家具类、电脑类……

对象是类的具体实现,比如电脑类的一个具体对象:我的这台电脑。

6.1 类的声明与定义

类的定义与结构的定义方式类似,同样有两种方式,不同的是声明一个类使用class关键字。而结构体使用struct关键字来表示。

不带有默认值

下面类定义了一个学生Student类,里面存放了学生的stuId、stuName和stuAge,分别表示学号、名字和年龄。这里全部使用可选类型。class Student {var stuId: String?var stuName: String?var stuAge: Int?}

带有默认值class Student {var stuId = ""var stuName = ""var stuAge = 0}

与结构体不同的是,无论是哪种定义方法,都可以使用直接生成对象:var xiaoMing = Student()

结构体如果使用不带有默认值的方式声明,在创建对象时,必须使用默认的全值构造方法。当然和结构体一样,字段必须有初始值才可以。6.1.1 类对象的创建

类对象的创建方法如下:var xiaoMing = Student()

这里创建类对象的方法是使用类的无参构造方法,当然这里只是简单地创建对象,关于详细的创建对象的方法和使用会在构造方法中介绍。6.1.2 类的属性的访问

与结构体一样,在类对象创建完成后,可以使用点语法对类对象的属性进行访问,包含访问与赋值:var li = Student()li.stuId = "1234"li.stuName = "李明"li.stuAge = 21println("学生:\(li.stuName) 学号:\(li.stuId) 年龄:\(li.stuAge)")//输出:学生:李明 学号:1234 年龄:216.1.3 类的相互引用

在类中也可以相互引用,如学生类中有一个属性是学生所有的班级属性,而班级属性是班级类的实例:class ClassRoom{var className:String?var classRoomId:Int?}class Student{var stuId = ""var stuName = ""var stuAge = 0var classInfo:ClassRoom?}

在访问嵌套类的属性时,可以使用链式访问方式:var li = Student()li.stuName = "李明"li.classInfo = ClassRoom()li.classInfo!.className = "iOS"li.classInfo!.classRoomId = 1404println("学生:\(li.stuName) 班级名:\(li.classInfo!.className) 班级号:\(li.classInfo!.classRoomId)")//输出:学生:李明 班级名:iOS 班级号:14046.1.4 类的嵌套

和函数一样Swift支持嵌套类定义。也就是在类中定义一个类。这点类似于C++的名字空间或者Java的包的概念。也就是类名只要不在一个类中,是可以重复的,这种就是类的嵌套。当然使用嵌套类和使用基本类没有任何区别。

代码如下所示:class IOS {class Student {var stuId : String?var stuName : String?var stuAge : Int = 0}}class Android {class Student {var stuId : String?var stuName : String?var stuAge : Int = 0}}let stu = IOS.Student()let stu2 = Android.Student()

从上面可以看出,可以通过IOS.Student()来创建Student对象,也可以通过Android.Student()来创建Student对象,两者是截然不同的两个对象。6.1.5 类是引用类型

相对于结构体来说,类是引用类型。结构体是值拷贝类型。类的对象是指针的引用,赋值也只是创建一个指针对象,指向同样的内存区域。而值拷贝是完全不同地拷贝一份对象,和源对象截然不同。

在进行类的对象之间的赋值时,并不是将源对象的属性全部拷贝一份,而是目标对象指向原有的对象空间,即对新对象的属性值进行修改时,会影响源对象的属性。var zhang = lizhang.stuName = "张江"println(li.stuName)//输出:张江6.1.6 恒等操作符(===/!===)

Swift提供了一种新的操作(===/!===),用来判断两个对象是否指向同一个对象:println(li === zhang)//输出:truevar zhao = Student()println(zhao!== li)//输出:true6.1.7 类的哈希

在字典的章节中提到,字典的key值的类型必须是可被哈希的:一般情况下,字典中的key值都是基本数据类型,但是如果要使用类的对象作为key值,则类必须实现hash方法:class SomeObj: NSObject{var name = ""override class func hash() -> Int{return "hello".hash}}var someObj = SomeObj()var some = SomeObj()var dic:Dictionary = [someObj:"hello", some:"hi"]6.1.8 集合类型对象之间的赋值和拷贝

在Swift语言中,字典和数组都是用结构体来实现的,按照结构体的使用规则,结构体在进行赋值操作时,会拷贝出一个全新的结构体。而实际上,字典和数组在进行赋值操作时,比单纯的结构赋值要复杂一些。1.字典的赋值和拷贝

字典对象在进行赋值时,两个字典之间key或者value是否是对同一个对象的引用取决于key或者value本身的值的类型是否是引用类型,存在两种情况:(1)字典中的key值是引用类型,在进行字典之间的赋值操作时,key值指向了同一个对象:在进行赋值后,改变key值的属性会影响另外一个字典中的key值:var someObj = SomeObj()someObj.name = "hello"var dic:Dictionary = [someObj:"hello"]var dic2 = dicfor key in dic.keys{key.name = "newHello"println("\(key):\(key.name)")}for key in dic2.keys{println("\(key):\(key.name)")}//输出://<_TtC5_10__7SomeObj: 0x100504400>:newHello//<_TtC5_10__7SomeObj: 0x100504400>:newHello

可以看到,两个字典中的key是指向同一个内存地址,同时,在修改dic2中的key值的属性后,dic中的key的属性值也发生了相应的变化。(2)字典中的value值是引用类型,key的规则类似:var dic5 = ["key4":someObj]var dic6 = dic5for key in dic5.keys{println("\(key):\(dic5[key])")}for key in dic6.keys{println("\(key):\(dic6[key])")}//输出:key4:<_TtC5_10__7SomeObj: 0x100504400>//key4:<_TtC5_10__7SomeObj: 0x100504400>

另外,如果字典中的key和value都不是引用类型,则都是进行拷贝。2.数组的赋值和拷贝

数组是基于结构体实现的,但是数组在进行赋值时,只有在三种情况下,会发生拷贝行为。

1.数组的长度发生变化。

2.使用数组的unshare()方法。

3.强制进行拷贝,调用数组的copy()方法。

在一般情况下,进行数组的赋值时,不会发生拷贝行为:var a = [2,3,4]var b = avar c = bc[1] = 23println(a)println(c)//输出:[2, 23, 4]//[2, 23, 4]

数组的长度发生变化的例子:a.append(5)c[1] = 12println(a)println(c)//输出:[2, 23, 4, 5]//[2, 12, 4]

使用unshare()方法使数组独立出来:var e = [1,2,3]var f = ee.unshare()f[1] = 12println(e)println(f)//输出:[1, 2, 3]//[1, 12, 3]

使用copy()方法,强制拷贝出一个新的数组:var g = [1,2,3]var h = g.copy()g[1] = 12println(g)println(h)//输出:[1, 12, 3]//[1, 2, 3]

6.2 属性

在面向对象的概念中,最重要的一个就是类,在类中有两个主要内容:属性和方法。

所谓属性,就是所有类的对象所具有的共同特性或者类本身的属性,而方法,则是类的对象所能做出的动作。

在Swift中,在进行类的定义时,可以定义三种属性:对象属性、计算属性和类属性。6.2.1 对象属性

对象属性就是所有的类的对象都拥有的属性,它具有具体的值,比如在学生类中有两个属性:class Student{var stuId = 0var stuName = ""}

对于这个类的理解:这是一个对所有学生的属性的抽象,所有的学生都有两个属性:学号和姓名(当然不只有两个属性,对于属性的抽象具体需要参考在程序的设计中哪些属性是需要使用到的),所以可以在类中抽象出这两个属性。

对于属性的定义,存在以下两种情况。1.常量属性

常量属性在对象被创建出来以后就不能再被修改,比如,在定义班级类的时候,定义班级最大能容纳的学生数为常量,即在生成一个具体班级的实例以后,它能容纳的最大学生数是不能被随意修改的。class Class{var classId = 0let maxOfStudents = 100}var iOS1404 = Class()iOS1404.maxOfStudents = 50//编译错误:不能给maxOfStudents这个属性赋值

常量属性有三种情况:(1)基本数据类型,如以上的例子。(2)结构体或者枚举等值类型实例。struct Class{var classId = 0var className = ""var maxStudent = 100}class Student{var stuName = ""let classInfo = Class()}var lili = Student()lili.classInfo = Class(classId:10,className:"",maxStudent:200)//错误,无法对classInfo进行修改赋值lili.classInfo.classId = 123//错误,无法对常量的结构体对象的属性进行修改赋值

通过这个例子可以看出,结构体或者枚举是值类型的,所以如果将类的属性设置成常量,并且赋值成值类型的对象时,无法修改相应的属性。(3)引用类型实例,如类对象。class Class{var classId = 0var className = ""var maxStudent = 100}class Student{var stuName = ""let classInfo = Class()}var lili = Student()//lili.classInfo = Class(classId:10,className:"",maxStudent:200)//错误,无法对classInfo进行修改赋值lili.classInfo.classId = 123//错误,无法对常量的结构体对象的属性进行修改赋值println(lili.classInfo.classId) //输出:123

如上例所示,如果类的属性被定义为常量,并且设置成一个类的对象,那么这个属性本身无法修改赋值,即无法更改为新的对象,但是它所指向的对象内部的属性是可以修改的。2.常量属性的赋值方法

通过上一节的内容可以看出,常量属性的值在初始化后,是无法修改的,要想设置其值,需要在初始化的时候进行。(1)结构体的常量属性struct Student{let stuId = 0let stuName = ""}var lili = Student(stuId:12, stuName:"李丽")println("学号:\(lili.stuId) 姓名:\(lili.stuName)")//输出:学号:12 姓名:李丽(2)类的常量属性

类没有全局构造方法,要想对常量进行初始化,需要自己实现其构造方法:class Class{let classId = 0let className = "iOS"init(classId:Int, className:String){self.classId = classIdself.className = className}}var iOS2014 = Class(classId:1404, className:"iOS高级班")println("班号:\(iOS2014.classId) 班级名称:\(iOS2014.className)")//输出:班号:1404 班级名称:iOS高级班

常量属性只能赋值一次,以后就不能再次被修改。一般在初始化的时候修改,其他时候访问。3.变量属性

相对于常量属性来说,变量属性的使用要简单得多,变量属性就是用来存储对象的属性值的,可以在任何时候对对象的属性进行修改:class Student{var stuName = ""var stuId = 0}var lili = Student()lili.stuName = "李丽"lili.stuId = 120println("姓名:\(lili.stuName) 学号:\(lili.stuId)")//输出:姓名:李丽 学号:1204.懒加载

懒加载也叫延迟加载,很多时候,类对象的属性不一定是必要的,或者在刚创建的时候,不确定它是否将会被使用。如果在创建对象的时候,就对该对象进行了初始化或者加载,则是对资源的浪费:class CityInfo{//初始化所有城市的信息init(){sleep(10)}}class Metro{var mName = ""var city = CityInfo()}var line1 = Metro()line1.mName = "地铁一号线"println(line1.mName)

以上程序模拟了加载文件的过程,在程序中模拟加载文件需要使用10秒的时间。运行以上程序,在10秒后,才会打印出结果。

为了解决这个问题,Swift提供了懒加载的机制,即只有在正式使用到某个属性的时候,才会加载该属性。懒加载使用关键字:lazy。class CityInfo{//初始化所有城市的信息init() {sleep(10)}}class Metro{var mName = ""lazy var city = CityInfo()}var line1 = Metro()line1.mName = "地铁一号线"println(line1.mName)

运行以上程序,立刻会打印出结果。因为使用了懒加载技术,所以只有使用了city属性,程序才会真正进行CityInfo()的操作。var city = line1.cityprintln("加载城市信息")

在调用了上述的city属性后,将过10秒才会继续执行程序。5.监测属性值的变化

属性监听也成为观察者模式。观察者模式作为设计模式的一种,在程序设计的过程中是很有用的。比如,在当前登录用户的信息发生变化时,可以实时观察到这种变化。

Swift提供了一种很方便的观察者模式的实现方式。当对对象的属性值进行赋值的时候,可以使用willSet和didSet进行对象属性值变化的观察。

willSet和didSet表示变量的即将改变和已经改变通知回调方法。class LoginUser{init(name:String){userName = name}var userName:String = ""{willSet{println("登录用户即将改变,新值:\(newValue)")}didSet{println("登录用户已经改变,旧值:\(oldValue)")}}}

在对属性值添加观察的时候,可以使用默认参数名newValue和oldValue。

在对属性进行修改的时候,会触发相应的观察方法:var user = LoginUser(name:"yang")//使用初始化方法时,不会调用属性值观察user.userName = "huangdl"//在初始化后,对属性值进行修改时,会触发属性值观察//输出://登录用户即将改变,新值:huangdl//登录用户已经改变,旧值:yang

在定义属性值观察的方法中,也可以使用自定义的参数名:class LoginUser{init(name:String){userName = name}var userName:String = ""{willSet(newName){println("登录用户即将改变,新值:\(newName)")}didSet(oldName){println("登录用户已经改变,旧值:\(oldName)")}}}

对类对象属性的观察不能用于已经设置为懒加载的属性。6.2.2 运算属性

Swift提供的运算属性并不是用来存储值的属性,也就是说它不能用来存储实际的数值。而是相当于是函数,只是这个函数被封装成了属性的形式,而且包含有getter和setter两个方法。

运算属性不能用来存储数值,它更多的是用来进行逻辑处理,并且对其他的存储属性进行修改。class Square{var width = 0var round:Int{get{return width * 4}set{width = newValue/4}}}

以上代码定义的是一个正方形的类,它有一个属性是边长,而周长属性是一个计算属性,它只依赖于边长就可以计算出结果,而对周长进行赋值的时候,也是通过计算去修改边长的值。

在使用的时候,用法和普通的属性使用方法一致。var s = Square()s.width = 12println(s.round)//输出:48s.round = 24println(s.width)//输出:66.2.3 类属性

类属性不依赖于具体的实例,它是属性类的共有属性,比如定义一个班级类,班级能容纳的最大学生人数有限制。这里的类属性指的是结构体的静态变量和方法以及类的类方法。struct ClassRoom {static var maxNumberOfStudents = 0}

在使用类属性时,使用类名进行访问:ClassRoom.maxNumberOfStudents = 120println(ClassRoom.maxNumberOfStudents)//输出:120

6.3 方法

类主要由两部分内容组成,即属性和方法。属性是用来描述类的对象所具有的特性,是用来存储与类的对象相关的数组的;而方法用来描述对象所具有的动作的能力。

比如,在游戏开发中,定义一个人物类,这些人物被具体创建出来以后,有相关的属性,如:姓名、身高、体重、攻击力、防御力等,而这些对象还具备一些动作的能力,如:攻击他人或者被他人攻击等,这些动作就是在类中定义的方法。

游戏人物类如下:class Person{var name = ""var power = 0var life = 0var defense = 0}6.3.1 对象方法

对象方法和对象属性相似,它依赖于具体的对象,比如当一个游戏人物在进行攻击时,需要根据它自身的属性攻击力,及攻击目标对象的属性防御力才能计算出具体的攻击后的结果,即攻击目标对象所损失的生命值。1.方法定义和调用方式

对象方法的定义方式与函数的定义方式相同,比如定义一个攻击的方法:class Person{var name = ""var power:Double = 0.0var life:Double = 0.0var defense:Double = 0.0func attack(target:Person){target.life -= self.power / target.defense * 10var intLife = Int(target.life)target.life = Double(intLife)}}

在该定义的攻击方法中,每次攻击都会让攻击目标的生命值减少,减少的依据是自己本身的攻击力和攻击目标的防御力。

在定义好这个类的属性及方法之后,可以使用该类进行对象的创建。var cao = Person()cao.name = "曹操"cao.power = 100cao.life = 1000cao.defense = 30var liu = Person()liu.name = "刘备"liu.power = 80liu.life = 80liu.defense = 60

当创建出两个对象后,让其中一个对象对另一个对象进行攻击。cao.attack(liu)println(liu.life)//输出:63.0cao.attack(liu)println(liu.life)//输出:46.0cao.attack(liu)println(liu.life)//输出:29.0

从结果中可以看出,实际上在使用该方法的时候,方法的执行是依赖于两个对象本身的具体属性值的。2.方法的参数命名规则

虽然在类的内部定义方法的方式和定义函数的方式相同,但是使用参数名称的规则却有所区别。

再定义一个类,用来表示在人物攻击时所使用的技能:class AttackMethod{var power:Double = 0.0}

如果在攻击的方法中再添加一个参数,用来表示攻击时所使用的技能,如下:func attack(target:Person, withAttack:AttackMethod){target.life -= (self.power + withAttack.power)/ target.defense * 10var intLife = Int(target.life)target.life = Double(intLife)}

再调用这个方法时,有如下的规则:(1)第一个参数的参数名缺省不需要写出来。(2)从第二个参数开始,所有的参数名称,需要在进行方法调用时写出来。cao.attack(liu, withAttack: am)println(liu.life)//输出:60.0

可以理解为,从第二个参数开始,所有的参数名称的前面有一个“#”号。“#”用来表示局部参数名和外部参数为同一个名称。

当然,在进行方法定义的时候,也可以指定参数的外部参数名:func attack(attckTo target:Person,attack withAttack:AttackMethod){target.life -= (self.power + withAttack.power)/ target.defense * 10var intLife = Int(target.life)target.life = Double(intLife)}

如果在定义中已经指定了外部参数名,则在调用时,必须使用指定的外部参数名:cao.attack(attckTo:liu, attack: am)println(liu.life)3.self属性

self属性在类的方法定义中用来表示当前的类对象,在上面的代码中,攻击方法中就使用了self属性。

在执行cao.attack(attckTo:liu, attack: am)时,该方法中的self属性所指代的就是这个方法的所有者,也就是调用者cao。

所以这里面的self.power实际就是指曹操的攻击力:100。4.mutating关键字

对于值拷贝类型的复合类型,这里主要是结构体和枚举两种,在其对象方法中是无法对对象属性进行修改的,如以下代码:struct Student{var stuName = ""var stuAge = 0func resetInfo(name:String,age:Int){stuName = name//错误:Cannot assign to 'stuName' in 'self'stuAge = age//错误:Cannot assign to 'stuAge' in 'self'}}

为了能够进行修改操作,Swift提供了mutating关键字,在上述代码中的方法定义前面添加该关键字,就可以进行对象属性的修改了:struct Student{var stuName = ""var stuAge = 0mutating func resetInfo(name:String,age:Int){stuName = namestuAge = age}}6.3.2 类方法

与类属性相似,类方法是属性类的方法,并不依赖于具体的对象的属性。如在软件开发过程中,会需要各种各样的工具类,而这些工具类只需要一个实例,这也是设计模式中的单例模式。

单例模式的使用是利用类方法的一个典型案例。1.类方法的定义和调用方式

要定义一个类方法,需要在普通的方法定义前面加上关键字class。class School{class func schoolName() -> String{return "千锋教育";}}

调用类方法不需要创建类的对象,只需要使用类名+点语法操作符就可以。println(School.schoolName())//输出:千锋教育2.类方法实现单例设计模式

单例是iOS开发中经常用到的一种设计模式,它可以用来共享数据,在整个程序运行过程中只会创建一个对象,可以有效地节约资源。创建一个单例的方法如下:class Tool{var toolName = ""struct ToolParams{static var tool:Tool? = nil}class func sharedTool() -> Tool{if(!ToolParams.tool){ToolParams.tool = Tool()}return ToolParams.tool!}}

以下代码是对单例的测试:var tool = Tool.sharedTool()tool.toolName = "压缩图片尺寸"var tool2 = Tool.sharedTool()println(tool2.toolName)//输出:压缩图片尺寸

因为调用了这个类方法后,返回的实例都是同一个实例,所以修改了属性以后,再次调用这个单例,返回的实例属性是修改后的结果。

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载