北京
Swift是苹果公司在WWDC2014 大会上所发布的一门全新的编程语言,用于编写OS X和iOS应用程序。苹果公司在设计Swift语言时,就有意让其和Objective-C共存,Objective-C是苹果操作系统在导入Swift前使用的编程语言。为了帮助读者迅速掌握Swift开发的核心技术知识,笔者特意编写了本书。
Swift的优势
在 WWDC2014 大会中,苹果公司展示了如何能让开发人员更快进行代码编写及显示得到的“Swift Playground”,在Swift 编程环境中,左侧输入代码的同时,在右侧可以实时显示结果。苹果公司表示Swift是基于Cocoa和Cocoa Touch专门设计的。Swift不仅可以用于基本的应用程序编写,如各种社交网络App,同时还可以使用更先进的“Metal”3D游戏图形优化工作。由于Swift可以与Objective-C兼容使用,开发人员可以在开发过程中进行无缝切换。
具体来说,Swift语言的突出优势如下所示。
(1)易学。作为一门苹果独立发布的支持型开发语言,Swift 语法简单、使用方便、易学,大大降低了开发者入门的门槛。同时,Swift 语言可以与 Objective-C 混合使用,对于用惯了高难度Objective-C语言的开发者来说,Swift语言更加易学。
(2)功能强大。Swift 允许开发者通过更简洁的代码来实现更多的内容。在 WWDC2014 发布会上,苹果演示了如何只通过一行简单的代码,完成一个完整图片列表加载的过程。另外,Swift还可以让开发人员一边编写程序一边预览自己的应用程序,从而快速测试应用在某些特殊情况下的效果。
(3)提升性能。Swift语言可以提升程序性能,同时降低开发难度。
(4)简洁、精良、高效。Swift是一种非常简洁的语言。与Python类似,不必编写大量代码即可实现强大的功能,并且也有利于提高应用开发速度。
(5)执行速度快。Swift的执行速度比Objective-C更快,这样会在游戏中看见更引人入胜的画面(需要苹果新的Metal界面的帮助),而其他应用也会有更好的响应性。
(6)全面融合。苹果对全新的Swift语言的代码进行了大量简化,在更快、更安全、交互更好的同时,开发者可以在同一款软件中同时用Objective-C、Swift、C三种语言。
(7)测试工作更加便捷。方便快捷地测试所编写应用有助于开发者更快地开发出复杂的应用。以往,规模较大的应用编译和测试过程极为冗繁,Swift 能在这一方面带来较大的改进,应用开发者将可以更快地发布经过更彻底测试的应用。
本书特色
本书内容十分丰富,实例内容覆盖全面。我们的目标是通过一本书,提供多本书的价值,在内容的编写上,本书具有以下特色。
(1)内容讲解循序渐进。本书从基础语法和搭建开发环境讲起,循序渐进地讲解Swift语言开发的基本语法知识和核心应用技术。
(2)结构合理。从用户的实际需要出发,合理安排知识结构,内容由浅入深,叙述清楚。全书详细地讲解了和Swift开发有关的所有知识点。
(3)易学易懂。本书条理清晰、语言简洁,可帮助读者快速掌握每个知识点。读者既可以按照本书编排的章节顺序进行学习,也可以根据自己的需求对某一章节进行有针对性地学习。
(4)实用性强。本书彻底摒弃枯燥的理论和简单的操作,注重实用性和实战性,通过实例的实现过程,详细讲解各个知识点的具体应用。
(5)内容全面。本书内容全面,无论是搭建开发环境,还是基本语法、面向对象、函数方法,都能在本书中找到解决问题的答案。
源程序下载地址为:www.toppr.net。
读者对象
iOS开发初学者
Swift初学者
大中专院校的老师和学生
毕业设计的学生
iOS编程爱好者
相关培训机构的老师和学员
从事iOS开发的程序员
本书在编写过程中,得到了人民邮电出版社工作人员的大力支持,正是各位编辑的求实、耐心和效率,才使得本书在这么短的时间内出版。另外,也十分感谢我的家人在我写作的时候给予的巨大支持。由于作者水平有限,纰漏和不尽如人意之处在所难免,诚请读者提出意见或建议,以便修订并使之更臻完善。
编者
Swift是苹果公司在WWDC2014 大会上所发布的一门全新的编程语言,用于编写OS X和iOS应用程序。苹果公司在设计Swift语言时,就有意让其和Objective-C共存,Objective-C是苹果操作系统在导入Swift前使用的编程语言。本章将带领大家初步认识Swift这门神奇的开发语言,让读者为学习本书后面的知识打下基础。
Swift 是一种为开发 iOS 和 OS X 应用程序而推出的全新编程语言,是建立在 C 语言和Objective-C语言基础之上的,并且没有C语言的兼容性限制。Swift采用安全模型的编程架构模式,并且使整个编程过程变得更容易、更灵活、更有趣。另外,Swift 完全支持市面上的主流框架——Cocoa 和Cocoa Touch,这为开发人员重新构建软件和提高开发效率带来了巨大的帮助。本节将带领大家一起探寻Swift的诞生历程。
苹果公司的Swift 语言的创造者是苹果开发者工具部门总监Chris Lattner(1978年出生)。Chris Lattner是LLVM项目的主要发起人与作者之一,也是Clang编译器的作者。Chris Lattner 曾经开发了LLVM,这是一种用于优化编译器的基础框架,能将高级语言转换为机器语言。LLVM极大地提高了高级语言的效率,Chris Lattner 也因此获得了首届SIGPLAN奖。
2005年,Chris加入LLVM开发团队,正式成为苹果的一名员工。在苹果公司的9年间,他由一名架构师一路升职为苹果开发者工具部门总监。目前,Chris Lattner 主要负责Xcode 项目,这也为Swift的开发提供了灵感。
Chris Lattner从2010 年7月才开始开发Swift 语言,当时它在苹果内部属于机密项目,只有很少人知道这一语言的存在。Chris Lattner 在个人博客上称,Swift 的底层架构大多是他自己开发完成的。2011 年,其他工程师开始参与项目开发,Swift 也逐渐获得苹果内部重视,直到 2013 年成为苹果主推的开发工具。
Swift 的开发结合了众多工程师的心血,包括语言专家、编译器优化专家等,苹果其他团队也为改进产品提供了很大帮助。同时Swift也借鉴了其他语言(如Objective-C、Rust、Ruby等)的优点。
Swift语言的核心吸引力在于Xcode Playgrounds功能和REPL,它们使开发过程具有更好的交互性,也更容易上手。Playgrounds在很大程度上受到了Bret Victor的理念和其他互动系统的启发。同样,具有实时预览功能的 Swift 使编程变得更简单,学习起来也更加容易,目前已经引起了开发者的极大兴趣。这有助于苹果吸引更多的开发者,甚至将改变计算机科学的教学方式。图1-1是Chris Lattner在WWDC2014 大会上对Swift 进行演示。
在 WWDC2014 大会中,苹果公司展示了如何能让开发人员更快进行代码编写及显示结果的“Swift Playground”,在左侧输入代码的同时,可以在右侧实时显示结果。苹果公司表示,Swift是基于Cocoa和Cocoa Touch 专门设计的。由于Swift可以与Objective-C 兼容使用,因此,开发人员可以在开发过程中进行无缝切换。
具体来说,Swift语言的突出优势如下所示。
(1)易学。
作为一门苹果独立发布的支持型开发语言,Swift语言的语法内容混合了Objective-C、JavsScript和Python,其语法简单、使用方便、易学,大大降低了开发者入门的门槛。同时Swift语言可以与Objective-C混合使用,对于用惯了高难度Objective-C语言的开发者来说,Swift语言更加易学。
(2)功能强大。
Swift 允许开发者通过更简洁的代码来实现更多的内容。在 WWDC2014 发布会上,苹果演示了如何只通过一行简单的代码,完成一个完整图片列表加载的过程。另外,Swift 还可以让开发人员一边编写程序,一边预览自己的应用程序。
(3)提升性能。
Swift语言可以提升程序性能,并同时降低开发难度,没有开发者不喜欢这样的编程语言。
(4)简洁、精良、高效。
Swift是一种非常简洁的语言。与Python类似,不必编写大量代码即可实现强大的功能,并且也有利于提高应用开发速度。Swift可以更快捷有效地编译出高质量的应用程序。
(5)执行速度快。
Swift的执行速度比Objective-C更快,这样会在游戏中看见更引人入胜的画面(需要苹果新的Metal界面的帮助),而其他应用也会有更好的响应性。
(6)全面融合。
苹果对全新的Swift语言的代码进行了大量简化,在更快、更安全、更好的交互、更现代的同时,开发者们可以在同一款软件中同时用Objective-C、Swift、C3种语言,这样便实现了3类开发人员的完美融合。
都说“工欲善其事,必先利其器”,这一说法在编程领域同样行得通,学习 Swift 开发也离不开好的开发工具的帮助。在本节中,将详细讲解搭建Swift语言开发环境的基本知识。
要开发iOS的应用程序,需要一台安装有Xcode 工具的Mac OS X 电脑。Xcode是苹果提供的开发工具集,它提供了项目管理、代码编辑、创建执行程序、代码调试、代码库管理和性能调节等功能。这个工具集的核心就是Xcode程序,提供了基本的源代码开发环境。
Xcode是一款强大的专业开发工具,可以简单、快速而且以我们熟悉的方式执行绝大多数常见的软件开发任务。相对于创建单一类型的应用程序所需要的能力而言,Xcode要强大得多,它的设计目的是使我们可以创建任何想象到的软件产品类型,从Cocoa及Carbon应用程序,到内核扩展及Spotlight导入器等各种开发任务,Xcode都能完成。Xcode独具特色的用户界面可以帮助我们以各种不同的方式来漫游工具中的代码,并且可以访问工具箱下面的大量功能,包括 GCC、javac、jikes和GDB,这些功能都是制作软件产品所需要的。它是一个由专业人员设计又由专业人员使用的工具。
由于能力出众,Xcode 已经被 Mac 开发者社区广为采纳。而且随着苹果电脑向基于 Intel 的Macintosh迁移,转向Xcode变得比以往任何时候更加重要。这是因为使用Xcode可以创建通用的二进制代码,这里所说的通用二进制代码是一种可以把PowerPC和Intel架构下的本地代码同时放到一个程序包的执行文件格式。事实上,对于还没有采用Xcode的开发人员,转向Xcode是将应用程序连编为通用二进制代码的第一个必要的步骤。
Xcode的官方地址是https://developer.apple.com/xcode/downloads/,如图1-2所示。
截止到2014年6 月,市面上最主流的版本是Xcode 5,最新版本是为Swift语言推出的Xcode 6 beta。Xcode 6 beta具有以下几个最突出的特点。
(1)Xcode 6 增加了一个全新的 iOS 模拟器,允许开发者根据设备调整应用尺寸,除了“Resizable iPhone”和“Resizable iPad”之外,还包括iPhone 5/5S、iPad 2/Retina/Air 等具体设备,如图1-3所示。
(2)完全支持Swift 编程。Xcode 6 为开发者引入了一种全新的设计和开发应用的方式,深度支持Swift编程,开发者不仅能使用100%的Swift代码来创建一款崭新的应用,还可以向已存在的应用添加Swift 代码或框架,并在Swift 或Objective-C 中查看文档。“Jump to Definition”、“Open Quickly”等在Swift中均能很好地工作,甚至Objective-C的头定义在Swift语法中也能良好地呈现。
(3)实时的代码效果预览。现在,开发者在使用Interface Builder 设计界面时,能够实时地预览代码效果。当程序运行时,自定义对象将在设计时展现。当开发者修改自定义视图代码时,Interface Builder的设计画布则会自动更新,而无需任何的构建和运行操作。
此外,其所包含的API 还支持向IB Inspector添加参数来快速修改视图,甚至开发者还可以预先填充示例数据视图来让界面更加准确。而支持UIKit大小类的iOS脚本则能够让开发者为所有iOS设备开发单一的通用脚本,不仅能为特定的设备尺寸或方向进行行为选择,还可以保持接口的一致性,且易于维护。
(4)新增View 调试功能。Xcode 6 实现了此前备受开发者期待的View Debuger。现在,调试应用UI就像单击那样简单,开发者可以轻而易举地看到为什么一个视图可能会被裁剪或隐藏,并在 Inspector 中检查和调试约束及其他参数。当然,Xcode 还包含了其他新的调试工具,比如调试Gauge来监控I/O用法、增强版的iCloud Gauge 等,而Debug Navigator 也将显示更有用的信息,包括栈框架记录和块队列等。
其实对于初学者来说,只需安装Xcode即可。通过使用Xcode,既能开发iPhone程序,也能够开发iPad程序。并且Xcode还是完全免费的,通过它提供的模拟器就可以在计算机上测试iOS程序。但是,如果要发布iOS程序或在真实机器上测试iOS程序,就需要花99美元了。
1.下载Xcode
(1)下载的前提是先注册成为一名开发人员,到苹果开发页面主页https://developer.apple.com/,如图1-4所示。
(2)登录Xcode 的下载页面https://developer.apple.com/xcode/downloads/,找到“Xcode 6 beta”选项,如图1-5所示。
(3)如果是付费账户,可以直接在苹果官方网站中下载获得。如果不是付费会员用户,可以从网络中搜索热心网友们的共享信息,以此达到下载Xcode 6 的目的。
注意
可以使用App Store来获取Xcode,这种方式的优点是完全自动,操作方便。
2.安装Xcode
(1)下载完成后单击打开下载的“.dmg”格式文件,如图1-6所示。
(2)双击Xcode下载到的文件开始安装,在弹出的对话框中单击“Continue”按钮,如图1-7所示。
(3)在弹出的欢迎界面中单击“Agree”按钮,如图1-8所示。
(4)在弹出的对话框中单击“Install”按钮,如图1-9所示。
(5)在弹出的对话框中输入用户名和密码,然后单击按钮“好”,如图1-10所示。
(6)在弹出的新对话框中显示安装进度,进度完成后的界面如图1-11所示。
(7)Xcode 6的默认启动界面如图1-12所示。
注意
(1)考虑到很多初学者是学生用户,如果没有购买苹果机的预算,可以在Windows系统上采用虚拟机的方式安装OS X系统。
(2)无论是已经有一定 Xcode 经验的开发者,还是刚刚开始迁移的新用户,都需要对Xcode的用户界面及如何用Xcode组织软件工具有一些理解,这样才能真正高效地使用这个工具。这种理解可以加深读者对隐藏在 Xcode 背后的编程思想的认识,并帮助读者更好地使用Xcode。
(3)建议读者将Xcode安装在OS X的Mac机器上,也就是装有苹果系统的苹果机上。通常来说,在苹果机器的 OS X 系统中已经内置了 Xcode,默认目录是“/Developer/Applications”。
接下来讲解使用Xcode开发环境的基本知识,为读者步入后面Objective-C知识的学习打下坚实的基础。
通过Xcode编写代码,代码的头部会有类似于图1-13所示的内容。
在此需要将这部分内容改为公司的名称或者项目的名称,注意,在 Xcode 3.2.x 之前,需要命令行设置变量。之后就可以通过Xcode的配置项进行操作了,操作步骤分别如图1-14和图1-15所示。
这样如果再创建文件,就会产生如图1-16所示的效果了。
当项目开发一段时间后,源代码文件会越来越多。再从Groups & Files 的界面去点选,效率比较差。可以借助Xcode的浏览器窗口,如图1-17所示。
在图 1-17 所示的搜索框中可以输入关键字,这样浏览器窗口里只显示带关键字的文件了,如只想看Book相关的类,如图1-18所示。
例如,在图1-19所示的界面中,有很多行都顶格了,此时需要进行格式化处理。
选中需要格式化的代码,然后在上下文菜单中进行查找,这是比较规矩的办法,如图 1-20所示。
Xcode没有提供快捷键,当然自己可以设置。我就比较喜欢用快捷键。我的做法是:Ctrl+A(全选文字)、Ctrl+X(剪切文字)、Ctrl+V(粘贴文字)。Xcode会对粘贴的文字格式化。
有的时候代码需要缩进,有的时候又要做相反的操作。单行缩进和其他编辑器类似,只需使用Tab 键即可。如果选中多行则需要使用快捷键,其中 Command+]表示缩进,Command+[表示反向缩进。
使用IDE工具的一大好处是,这一工具能够帮助我们自动补全冗长的类型名称。Xcode提供了这方面的功能。比如下面的输出日志:
NSLog(@"book author: %@",book.author);
如果都自己输入会很麻烦,可以先输入ns,然后使用快捷键“Ctrl+.”,会自动出现如下代码:
NSLog(NSString * format)
然后填写参数即可。快捷键“Ctrl+.”的功能是自动给出第一个匹配ns关键字的函数或类型,而NSLog是第一个。如果继续使用“Ctrl+.”,则会出现比如NSString的形式。以此类推,会显示所有ns开头的类型或函数,并循环往复。或者,也可以用“Ctrl+,”快捷键,比如还是ns,那么会显示全部 ns 开头的类型、函数、常量等的列表。可以在这里选择。其实,Xcode 也可以在输入代码的过程中自动给出建议。比如要输入NSString。当输入到NSStr的时候:
NSString
此时后面的ing会自动出现,如果和我预想的一样,只需直接按Tab键确认即可。也许你想输入的是NSStream,那么可以继续输入。另外也可输入Esc键,这时就会出现结果列表供选择了,如图1-21所示。
如果是正在输入方法,那么会自动完成如图1-22所示的样子。
我们可以使用Tab键确认方法中的内容,或者通过快捷键“Ctrl+/”在方法中的参数间来回切换。
在编辑代码的过程中经常会做查找和替换的操作,如果只是查找则直接按“Command+F”组合键即可,在代码的右上角会出现如图1-23所示的对话框。只需在里面输入关键字,不论大小写,代码中所有命中的文字都会高亮显示。
也可以实现更复杂的查找,比如是否区分大小写、是否使用正则表达式等,设置界面如图1-24所示。
通过图1-25中的“Find & Replace”可以切换到替换界面。
例如图1-26所示的界面将查找设置为区分大小写,然后替换为myBook。
另外,也可以单击按钮决定是否全部替换,还是查找一个替换一个等。如果需要在整个项目内查找和替换,则依次单击“Edit”→“Find”→“Find in Project…”命令,如图1-27 所示。
还是以找关键字book为例,则实现界面如图1-28所示。
替换操作的过程也与之类似,在此不再进行详细讲解。
如果想定位光标到选中文件的行上,可以使用快捷键“Command+L”来实现,也可以依次单击“Edit”→“Go to Line”命令实现,如图1-29 所示。
在使用菜单或者快捷键时都会出现图 1-30 所示的对话框,输入行号和回车后就会来到该文件的指定行。
有时候需要快速打开头文件,例如图 1-31 所示的界面。要想知道这里的文件 Cocoa.h 到底是什么内容,可以用鼠标光标选中文件Cocoa.h来实现。
依次单击“File”→“Open Quickly…”命令,如图1-32 所示。
此时会弹出如图1-33所示的对话框。
此时双击文件Cocoa.h的条目就可以看到如图1-34所示的界面。
使用Eclipse的用户会经常用到TODO标签,比如正在编写代码的时候需要做其他事情,或者提醒自己以后再实现的功能时,可以写一个TODO注释,这样可以在Eclipse的视图中找到,方便以后找到这个代码并修改。其实Xcode也有类似的功能,比如存在一段如图1-35所示的代码。
这段代码的方法printInfomation是空的,暂时不需要具体实现。但是需要记下来,便于以后能找到并补充。那么让光标在方法内部,然后单击鼠标右键,在弹出的菜单中选择“Add to Bookmarks”命令,如图1-36所示。
此时会弹出一个对话框,可以在里面填写标签的内容,如图1-37所示。
这样就可以在项目的书签节点找到这个条目了,如图 1-38 所示。此时单击该条目,可以回到刚才添加书签时光标的位置。
在代码窗口上边有一个工具条,此工具条提供了很多方便的导航功能,如图1-39所示的功能。
它也可以用来实现上面TODO的需求。这里有两种自定义导航条的写法,其中标准写法如下:
#pragma mark
Xcode兼容的格式如下:
// TODO: xxx
// FIXME: xxx
完整的代码如图1-40所示。
此时会产生如图1-41所示的导航条效果。
如果想快速查看官方API文档,可以在源代码中按下“Option”键并用鼠标双击该类型(函数、变量等),比如图1-42所示的是NSString的API文档对话框。
如果单击图1-42中标识的按钮,会弹出完整文档的窗口,如图1-43所示。
最简单的调试方法是通过NSLog 打印出程序运行中的结果,然后根据这些结果判断程序运行的流程和结果值是否符合预期。对于简单的项目,通常使用这种方式就足够了。但是,如果开发的是商业项目,需要借助 Xcode 提供的专门调试工具。所有的编程工具的调试思路都是一样的。首先要在代码中设置断点,此时可以想象一下,程序的执行是顺序的,可能怀疑某个地方的代码出了问题(引发 Bug),那么就在这段代码开始的地方,比如是一个方法的第一行,或者循环的开始部分,设置一个断点。那么程序在调试时会在运行到断点时终止,接下来可以一行一行地执行代码,判断执行顺序是否是自己预期的,或者变量的值是否和自己想的一样。
设置断点的方法非常简单,比如想对红框表示的行设置断点,就单击该行左侧红圈位置,如图1-44所示。
单击后会出现断点标志,如图1-45所示。
然后运行代码,比如使用“Command+Enter”命令,这时将运行代码,并且停止在断点处,如图1-46所示。
可以通过“Shift+Command+Y”命令调出调试对话框,如图1-47所示。
这和其他语言IDE工具的界面大同小异,因为都具有类似的功能。下面是主要命令的具体说明。
Continue:继续执行程序。
step over, step into, step out:用于单步调试,分别表示如下3点说明。
• step over:将执行当前方法内的下一个语句。
• step into:如果当前语句是方法调用,将单步执行当前语句调用方法内部第一行。
• step out:将跳出当前语句所在方法,到方法外的第一行。
通过调试工具,可以对应用做全面和细致的调试。
Xcode是一款功能全面的应用程序,通过此工具可以轻松输入、编译、调试并执行Swift程序。如果想在Mac上快速开发iOS应用程序,则必须学会使用这个强大的工具的方法。接下来将简单介绍使用 Xcode 编辑启动模拟器的基本方法。
(1)Xcode 位于“Developer”文件夹内中的“Applications”子文件夹中,快捷图标如图 1-48所示。
(2)启动Xcode 6 后的初始界面如图1-49 所示,在此可以设置创建新工程还是打开一个已存在的工程。
(3)单击“Create a new Xcode project”后会出现如图1-50 所示的窗口。
(4)在 New Project 窗口的左侧,显示了可供选择的模板类别,因为我们的重点是类别 iOS Application,所以,在此需要确保选择了它。而在右侧显示了当前类别中的模板以及当前选定模板的描述。就这里而言,请单击模板“Empty Application(空应用程序)”,再单击Next(下一步)按钮,窗口界面效果如图1-51所示。
(5)选择模板后单击“Next”按钮,在新界面中,Xcode将要求您指定产品名称和公司标识符。产品名称就是应用程序的名称,而公司标识符是创建应用程序的组织或个人的域名,但按相反的顺序排列。这两者组成了束标识符,它将您的应用程序与其他iOS应用程序区分开来,如图1-52所示。
例如我们将创建一个名为“exSwift”的应用程序,设置域名是“apple”。如果没有域名,在开发时可以使用默认的标识符。
(6)单击“Next”按钮,Xcode将要求我们指定项目的存储位置。切换到硬盘中合适的文件夹,确保没有选择复选框Source Control,再单击“Create(创建)”按钮。Xcode 将创建一个名称与项目名相同的文件夹,并将所有相关联的模板文件都放到该文件夹中,如图1-53所示。
(7)在Xcode中创建或打开项目后,将出现一个类似于iTunes的窗口,您将使用它来完成所有的工作,从编写代码到设计应用程序界面。如果这是您第一次接触Xcode,令人眼花缭乱的按钮、下拉列表和图标将让您感到恐惧。为让您对这些东西有大致认识,下面首先介绍该界面的主要功能区域,如图1-54所示。
(8)运行iOS模拟器的方法十分简单,只需单击左上角的按钮即可,运行效果如图1-55所示。
Swift 是一种适用于 iOS 和 OS X 应用开发的全新编程语言,它建立在C 和Objective-C语言基础之上,并且没有C 语言的兼容性限制。Swift 采用安全的编程模式,增加了现代功能,使编程更容易、更灵活、更有趣。Swift 以成熟且备受宠爱的Cocoa和 Cocoa Touch 框架作为支撑,这是一个重新构建软件开发的机会。本章将带领大家学习Swift这门神奇语言的基本语法知识,为读者步入本书后面知识的学习打下基础。
当苹果公司推出Swift 编程语言时,建议使用Xcode 6 来开发Swift 程序。本节将详细讲解使用Xcode 6 创建Swift 程序的方法。
(1)打开Xcode 6,单击“Create a new Xcode Project”创建一个工程文件,如图2-1 所示。
(2)在弹出的界面中,在左侧栏目中选择“Application”,在右侧选择“Command Line Tool”,然后单击“Next”按钮,如图2-2所示。
(3)在弹出的界面中设置各个选项值,在“Language”选项中设置编程语言为“Swift”,然后单击“Next”按钮,如图2-3所示。
(4)在弹出的界面中设置当前工程的保存路径,如图2-4所示。
(5)单击“Create”按钮,将自动生成一个用Swift语言编写的iOS工程。在工程文件main.swift中会自动生成一个“Hello, World!”语句,如图2-5 所示。
文件main.swift的代码是自动生成的,具体代码如下所示。
//
// main.swift
// exSwift
//
// Created by admin on 14-6-7.
// Copyright (c) 2014年 apple. All rights reserved.
//
import Foundation
println("Hello, World!")
单击图2-5左上角的按钮运行工程,会在Xcode 6 下方的控制台中输出运行结果,如图2-6所示。
(6)重新编辑文件main.swift的内容,编辑后的具体代码如下所示。
import Foundation
func testfunc()
{
var bgen:Bool = true
//分支
if(bgen)
{
println(bgen);
}else
{
println(bgen);
}
var value=123;
switch(value)
{
case 123:
println("1")
fallthrough; //继续执行
case 2:
println("2")
case 3:
println("3")
default:
println("没有匹配的")
}
//switch ()中的值可以是字符串
switch("理想")
{
case "理想":
println("理想")
case "理想2":
println("理想2")
case "理想3":
println("理想3")
default:
println("没有匹配的字符")
}
//case 中可以有多个匹配项
switch("abc")
{
case "123":
println("123");
case "456","abc":
println("123 abc ");
default:
println("没有找到合适的匹配");
}
//比较操作,hasSuffix 函数是判断字符串是不是以其参数结尾
switch("理想 and swift")
{
case let x where x.hasSuffix("swift"):// 注意此时的 x 的值就是switch()中的值 where 额外的判断条件
println("swift");
case "理想":
println("理想");
default:
println("me");
}
var i:Int = 0;
while( i<10)
{
i++;
println(i);
}
do
{
i--;
println(i);
}while(i>0);
//for in
//使用for-in循环来遍历一个集合里面的所有元素,例如由数字表示的区间、数组中的元素、字符串中的字符
for index in 1...5
{
println("index=\(index)");
}
//如果你不需要知道区间内每一项的值,可以使用下划线(_)替代变量名来忽略对值的访问
var num=0;
for _ in 1...5
{
num++;
println("num =\(num)");
}
//遍历字符
for str in "ABCDE"
{
println("str=\(str)");
}
for(var i=0; i<10; i++)
{
println("i=\(i)");
}
}
// 调用函数
testfunc();
重新在Xcode 6 中执行上述文件,单击图2-5 左上角的按钮运行工程,会在Xcode 6下方的控制台中输出运行结果,如图2-7所示。
图2-7所示的完整输出内容如下所示。
true
1
2
理想
123 abc
swift
1
2
3
4
5
6
7
8
9
10
9
8
7
6
5
4
3
2
1
0
index=1
index=2
index=3
index=4
index=5
num =1
num =2
num =3
num =4
num =5
str=A
str=B
str=C
str=D
str=E
i=0
i=1
i=2
i=3
i=4
i=5
i=6
i=7
i=8
i=9
Program ended with exit code: 0
此时,读者无需理解文件main.swift中每一行代码的具体含义,在此只是以此文件为基础,作为本章后面讲解Swift基本语法构成的素材。
在 Swift 语言中,使用关键字“let”来声明常量,使用关键字“var”来声明变量。对于一个Swift常量值来说,在编译时并不需要有一个明确的值,但是只能为这个常量赋值一次。也就是说,可以用常量来表示一个只需要决定一次但是需要使用很多次的值。例如,在本章前面的实例2-1中,如下都是声明常量值的实现代码。
var value=123;
var i:Int = 0;
var num=0;
常量或者变量的类型必须和赋给它们的值一样,在进行声明操作时,可以选择常量和变量的类型。如果在声明的同时进行赋值操作,那么编译器会自动推断类型。例如,在下面的代码中,因为myVariable的初始值是整数,所以编译器会推断出myVariable是一个整数(integer)。
var myVariable = 42
myVariable = 50
let myConstant = 42
如果初始值没有提供足够的信息(或者没有初始值),那么需要在变量后面声明其类型,并且用冒号进行分隔。例如,下面所示的演示代码。
let implicitInteger = 70
let implicitDouble = 70.0
let explicitDouble: Double = 70
在Swift语言中,一个值永远不会被隐式转换为其他类型。如果要把一个值转换成其他类型,需要显式转换操作来实现。例如,下面所示的演示代码。
let label = "The width is"
let width = 94
let widthLabel = label + String(width)
在Swift语言中,还有一种把值转换成字符串的更简单的方法:把值写到括号中,并且在括号之前写一个反斜杠。例如,下面所示的演示代码。
let apples = 3
let oranges = 5
let appleSummary = "I have \(apples) apples."
let fruitSummary = "I have \(apples + oranges) pieces of fruit."
在Swift语言中,可以使用方括号[]来创建数组和字典,并使用下标或者键(key)来访问元素。例如,下面所示的演示代码。
var shoppingList = ["catfish", "water", "tulips", "blue paint"]
shoppingList[1] = "bottle of water"
var occupations = [
"Malcolm": "Captain",
"Kaylee": "Mechanic",
]
occupations["Jayne"] = "Public Relations"
在Swift语言中,要创建一个空数组或者字典,需要使用初始化语法。例如,下面所示的演示代码。
let emptyArray = String[]()
let emptyDictionary = Dictionary<String, Float>()
如果可以在 Swift 语言中推断出类型的信息,就可以用[]和[:]来创建空数组和空字典,就像声明变量或者给函数传参数的时候一样。例如,下面所示的演示代码。
shoppingList = [] // 去逛街并买点儿东西
在本章前面的实例2-1程序中,下面这些都是实现流程控制工作的实现代码。
switch("abc")
{
case "123":
println("123");
case "456","abc":
println("123 abc ");
default:
println("没有找到合适的匹配");
}
var i:Int = 0;
while( i<10)
{
i++;
println(i);
}
do
{
i--;
println(i);
}while(i>0);
for index in 1...5
{
println("index=\(index)");
}
在Swift语言中,使用关键字if和switch来进行条件判断操作,使用for-in、for、while和do-while来进行循环操作。在Swift程序中,可以省略包含条件和循环变量括号,但是必须保留语句体中的大括号。例如,下面所示的演示代码。
let individualScores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in individualScores {
if score > 50 {
teamScore += 3
} else {
teamScore += 1
}
}
teamScore
在if语句中,条件必须是一个布尔表达式——这意味着像if score { ... }这样的代码将报错,而不会隐式地与 0 做对比。
在Swift语言中,有些变量的值是可选的,一个可选的值可能是一个具体的值或者是nil(此时表示值缺失)。在Swift程序中,可以联合使用if和let来处理值缺失的情况,具体方法是在类型后面加一个问号“?”来标记这个变量的值是可选的。参见如下所示的演示代码。
var optionalString: String? = "Hello"
optionalString == nil
var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
greeting = "Hello, \(name)"
}
在 Swift 语言中,如果变量的可选值是 nil,条件会判断为 false,大括号中的代码会被跳过。如果不是nil,则会将值赋给let后面的常量,这样在代码块中就可以使用这个值了。
在Swift语言中,关键字switch可以支持任意类型的数据以及各种比较操作,而不仅仅是整数以及测试相等。例如,如下所示的演示代码。
let vegetable = "red pepper"
switch vegetable {
case "celery":
let vegetableComment = "Add some raisins and make ants on a log."
case "cucumber", "watercress":
let vegetableComment = "That would make a good tea sandwich."
case let x where x.hasSuffix("pepper"):
let vegetableComment = "Is it a spicy \(x)?"
default:
let vegetableComment = "Everything tastes good in soup."
}
在Swift语言中,运行switch中匹配到的子句之后,程序会退出switch语句,不会继续向下运行,所以不需要在每个子句结尾写break。
在Swift语言中,可以使用for-in来遍历字典,需要两个变量来表示每个键值对。参见如下所示的演示代码。
let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (kind, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number
}
}
}
largest
在Swift语言中,可以使用while来重复运行一段代码直到不满足条件为止,循环条件可以在开头也可以在结尾。例如,如下所示的演示代码。
var n = 2
while n < 100 {
n = n * 2
}
n
var m = 2
do {
m = m * 2
} while m < 100
m
在 Swift 语言中,可以在循环中使用“..”字符来表示范围,也可以使用传统的写法来实现,这两种方式是完全等价的。例如,如下所示的演示代码。
var firstForLoop = 0
for i in 0..3 {
firstForLoop += i
}
firstForLoop
var secondForLoop = 0
for var i = 0; i < 3; ++i {
secondForLoop += 1
}
secondForLoop
在本章前面的实例2-1中,testfunc就是一个函数。在Swift语言中,使用关键字func来声明一个函数,使用名字和参数来调用函数,使用“→”来指定函数返回值。例如,如下所示的演示代码。
func greet(name: String, day: String) -> String {
return "Hello \(name), today is \(day)."
}
greet("Bob", "Tuesday")
在Swift语言中,使用一个元组来返回多个值。例如,如下所示的演示代码。
func getGasPrices() -> (Double, Double, Double) {
return (3.59, 3.69, 3.79)
}
getGasPrices()
在Swift语言中,函数可以带有可变个数的参数,这些参数在函数内表现为数组的形式。例如,如下所示的演示代码。
func sumOf(numbers: Int...) -> Int {
var sum = 0
for number in numbers {
sum += number
}
return sum
}
sumOf()
sumOf(42, 597, 12)
在Swift语言中,可以使用嵌套函数,被嵌套的函数可以访问外侧函数的变量,也可以使用嵌套函数来重构一个太长或者太复杂的函数。例如,如下所示的演示代码。
func returnFifteen() -> Int {
var y = 10
func add() {
y += 5
}
add()
return y
}
returnFifteen()
在Swift语言中,一个函数可以作为另一个函数的返回值。例如,如下所示的演示代码。
func makeIncrementer() -> (Int -> Int) {
func addOne(number: Int) -> Int {
return 1 + number
}
return addOne
}
var increment = makeIncrementer()
increment(7)
在Swift语言中,函数可以当做参数被传入到另一个函数中。例如,如下所示的演示代码。
func hasAnyMatches(list: Int[], condition: Int -> Bool) -> Bool {
for item in list {
if condition(item) {
return true
}
}
return false
}
func lessThanTen(number: Int) -> Bool {
return number < 10
}
var numbers = [20, 19, 7, 12]
hasAnyMatches(numbers, lessThanTen)
在Swift语言中,函数实际上是一种特殊的闭包,可以使用“{}”来创建一个匿名闭包。使用关键字“in”将参数和返回值类型声明与闭包函数体进行分离。例如,如下所示的演示代码。
numbers.map({
(number: Int) -> Int in
let result = 3 * number
return result
})
在Swift语言中,有很多种创建闭包的方法。如果已知一个闭包的类型,比如作为一个回调函数,那么就可以忽略参数的类型和返回值。单个语句闭包会把语句的值当做结果返回。例如,如下所示的演示代码。
numbers.map({ number in 3 * number })
在Swift语言中,可以通过参数位置而不是参数名字来引用参数,这个方法在非常短的闭包中非常有用。当一个闭包作为最后一个参数传给一个函数时,这个闭包可以直接跟在括号后面。例如,如下所示的演示代码。
sort([1, 5, 3, 12, 2]) { $0 > $1 }
}
}
在Swift语言中,使用关键字class和类名来创建一个类。在类中声明属性的方法和声明常量、变量的方法一样,唯一的区别就是它们的上下文是类。同样,声明方法和声明函数的方法也一样。例如,下面的演示代码定义了一个名为Shape的类。
class Shape {
var numberOfSides = 0
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
要想在Swift程序中创建一个类的实例,需要在类名后面加上括号,使用点语法格式来访问实例的属性和方法。例如,如下所示的演示代码。
var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()
在上述代码中,类Shape缺少了一个用来初始化类实例的构造函数,此时可以使用init关键字创建一个构造器。例如,如下所示的演示代码。
class NamedShape {
var numberOfSides: Int = 0
var name: String
init(name: String) {
self.name = name
}
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
在上述代码中,self被用来区别实例变量。当创建一个实例的时候,像传入函数参数一样给类传入构造器的参数。每个属性都需要进行赋值操作,无论是通过声明(就像numberOfSides)还是通过构造器(就像name)。
如果要在删除对象之前进行一些清理工作,则需要使用关键字deinit创建一个析构函数。
在Swift语言中,子类的定义方法是在它们的类名后面加上父类的名字,用冒号分隔。创建类的时候并不需要一个标准的根类,所以可以忽略父类。
在Swift语言中,子类需要使用override标记重写父类的方法。如果没有添加override关键字就重写父类方法,则编译器会发出报错信息。另外,编译器也会检测override标记的方法是否确实在父类中。例如,如下所示的演示代码。
class Square: NamedShape {
var sideLength: Double
init(sideLength: Double, name: String) {
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 4
}
func area() -> Double {
return sideLength * sideLength
}
override func simpleDescription() -> String {
return "A square with sides of length \(sideLength)."
}
}
let test = Square(sideLength: 5.2, name: "my test square")
test.area()
test.simpleDescription()
在Swift语言中有getter和setter两种属性,请看如下所示的演示代码。
class EquilateralTriangle: NamedShape {
var sideLength: Double = 0.0
init(sideLength: Double, name: String) {
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 3
}
var perimeter: Double {
get {
return 3.0 * sideLength
set {
sideLength = newValue / 3.0
}
}
override func simpleDescription() -> String {
return "An equilateral triagle with sides of length \(sideLength)."
}
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
triangle.perimeter
triangle.perimeter = 9.9
triangle.sideLength
在上述perimeter的setter中,新值的名字是newValue,可以在set之后显式地设置一个名字。其中类EquilateralTriangle的构造器执行了如下所示的3个操作步骤。
设置子类声明的属性值。
调用父类的构造器。
改变父类定义的属性值。其他的工作,比如调用方法、getters 和 setters,也可以在这个阶段完成。
如果不需要计算属性,但是仍然需要在设置一个新值之前或者之后运行代码,可使用willSet和didSet。
又比如在下面的演示代码中,类TriangleAndSquar可以确保三角形的边长总是和正方形的边长相同。
class TriangleAndSquare {
var triangle: EquilateralTriangle {
willSet {
square.sideLength = newValue.sideLength
}
var square: Square {
willSet {
triangle.sideLength = newValue.sideLength
}
}
init(size: Double, name: String) {
square = Square(sideLength: size, name: name)
triangle = EquilateralTriangle(sideLength: size, name: name)
}
}
var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
triangleAndSquare.square.sideLength
triangleAndSquare.triangle.sideLength
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
triangleAndSquare.triangle.sideLength
在Swift语言中,类中的方法和一般函数的重要区别如下所示。
函数的参数名只在函数内部使用。
方法的参数名需要在调用的时候显式说明(除了第一个参数)。在默认情况下,方法的参数名和它在方法内部的名字一样,不过也可以定义第二个名字,这个名字被用在方法内部。
例如,如下所示的演示代码。
class Counter {
var count: Int = 0
func incrementBy(amount: Int, numberOfTimes times: Int) {
count += amount * times
}
}
var counter = Counter()
counter.incrementBy(2, numberOfTimes: 7)
在Swift语言中,处理变量的可选值时可以在操作(比如方法、属性和子脚本)之前加“?”。如果“?”之前的值是nil,“?”后面的东西都会被忽略,并且整个表达式返回nil;否则,“?”之后的东西都会被运行。在上述两种情况下,整个表达式的值也是一个可选值。例如,如下所示的演示代码。
let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength
在Swift语言中,使用关键字enum来创建一个枚举。就像类和其他所有命名类型一样,在枚举中可以包含方法。例如,如下所示的演示代码。
enum Rank: Int {
case Ace = 1
case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
case Jack, Queen, King
func simpleDescription() -> String {
switch self {
case .Ace:
return "ace"
case .Jack:
return "jack"
case .Queen:
return "queen"
case .King:
return "king"
default:
return String(self.toRaw())
}
}
}
let ace = Rank.Ace
let aceRawValue = ace.toRaw()
在上面的演示代码中,因为枚举原始值的类型是 Int,所以只需要设置第一个原始值即可,剩下的原始值会按照顺序赋值。另外,也可以使用字符串或者浮点数作为枚举的原始值。
接下来可以使用函数toRaw和函数fromRaw在原始值和枚举值之间进行转换,例如,如下所示的演示代码。
if let convertedRank = Rank.fromRaw(3) {
let threeDescription = convertedRank.simpleDescription()
}
在Swift语言中,枚举的成员值是实际值,并不是原始值的另一种表达方法。实际上,如果原始值没有意义,则不需要进行任何设置。例如,如下所示的演示代码。
enum Suit {
case Spades, Hearts, Diamonds, Clubs
func simpleDescription() -> String {
switch self {
case .Spades:
return "spades"
case .Hearts:
return "hearts"
case .Diamonds:
return "diamonds"
case .Clubs:
return "clubs"
}
}
}
let hearts = Suit.Hearts
let heartsDescription = hearts.simpleDescription()
在Swift语言中,有两种方式可以引用Hearts成员:给hearts常量赋值时,枚举成员Suit.Hearts需要用全名来引用,因为常量没有显式指定类型。在switch里,枚举成员使用缩写.Hearts来引用,因为self的值已经知道是一个suit。已知变量类型的情况下可以使用缩写。
在Swift语言中,使用关键字struct来创建一个结构体。结构体和类有很多相同的地方,比如方法和构造器。结构体和类之间最大的一个区别就是结构体是传值,类是传引用。例如,如下所示的演示代码。
struct Card {
var rank: Rank
var suit: Suit
func simpleDescription() -> String {
return "The \(rank.simpleDescription()) of \
(suit.simpleDescription())"
}
}
let threeOfSpades = Card(rank: .Three, suit: .Spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()
一个枚举成员的实例可以有实例值,相同枚举成员的实例可以有不同的值,只需在创建实例时传入值即可。实例值和原始值是不同的,枚举成员的原始值对于所有实例都是相同的,而且是在定义枚举的时候设置原始值。
例如,可以考虑从服务器获取日出和日落的时间,这样服务器会返回正常结果或者错误信息。例如,如下所示的演示代码。
enum ServerResponse {
case Result(String, String)
case Error(String)
}
let success = ServerResponse.Result("6:00 am", "8:09 pm")
let failure = ServerResponse.Error("Out of cheese.")
switch success {
case let .Result(sunrise, sunset):
let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)."
case let .Error(error):
let serverResponse = "Failure... \(error)"
}
注意
如何从ServerResponse中提取日升和日落时间。
在Swift语言中,使用关键字protocol来声明一个协议。例如,如下所示的演示代码。
protocol ExampleProtocol {
var simpleDescription: String { get }
mutating func adjust()
}
在Swift语言中,类、枚举和结构体都可以实现协议。例如,如下所示的演示代码。
class SimpleClass: ExampleProtocol {
var simpleDescription: String = "A very simple class."
var anotherProperty: Int = 69105
func adjust() {
simpleDescription += " Now 100% adjusted."
}
}
var a = SimpleClass()
a.adjust()
let aDescription = a.simpleDescription
struct SimpleStructure: ExampleProtocol {
var simpleDescription: String = "A simple structure"
mutating func adjust() {
simpleDescription += " (adjusted)"
}
}
var b = SimpleStructure()
b.adjust()
let bDescription = b.simpleDescription
在上述演示代码中声明SimpleStructure时,关键字mutating用来标记一个会修改结构体的方法。因为类SimpleClass中的方法经常会修改类,所以,在声明SimpleClass时不需要标记任何方法。
可以使用 extension 来为现有的类型添加功能,比如新的方法和参数。可以使用扩展来改造定义在别处,甚至是从外部库或者框架引入的一个类型,使得这个类型遵循某个协议。例如,如下所示的演示代码。
extension Int: ExampleProtocol {
var simpleDescription: String {
return "The number \(self)"
}
mutating func adjust() {
self += 42
}
}
在Swift语言中,在尖括号里写一个名字来创建一个泛型函数或者类型。例如,如下所示的演示代码。
func repeat<ItemType>(item: ItemType, times: Int) -> ItemType[] {
var result = ItemType[]()
for i in 0..times {
result += item
}
return result
}
repeat("knock", 4)
你也可以创建泛型类、枚举和结构体。
// Reimplement the Swift standard library's optional type
enum OptionalValue<T> {
case None
case Some(T)
}
var possibleInteger: OptionalValue<Int> = .None
possibleInteger = .Some(100)
在Swift语言中,在类型名后面使用where关键字来指定对类型的需求。例如,限定某个类型来实现某一个协议,限定两个类型是相同的,或者限定某个类必须有一个特定的父类。例如,如下所示的演示代码。
func anyCommonElements <T, U where T: Sequence, U: Sequence, T.GeneratorType.Element: Equatable,
T.GeneratorType.Element == U.GeneratorType.Element> (lhs: T, rhs: U) -> Bool {
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
return true
}
}
}
return false
}
anyCommonElements([1, 2, 3], [3])
在Swift语言中,可以忽略where关键字,只在冒号后面写协议或者类名。例如,如下两种格式是完全等价的。
<T: Equatable>
<T where T: Equatable>