001-Swift1.0
一、简介和语法特点
1.简介
1)Swift是苹果公司在2014年WWDC上发布的全新编程语言
2)在中国被译为“雨燕”
3)跟Objective-C一样,可以用来开发Mac和iOS应用程序
4)苹果公司从2010年7月开始设计Swift语言,耗时4年时间其核心技术由一位工程师全力打造完成
2.语言特点
1)从它的语法中能看到Objective-C、JavaScript、C#、Python等语言的影子
2)借鉴了Objective-C、JavaScript、C#、Python等语言的优点
3)可以说Swift同时具备编译型语言的高效性和脚本语言到的灵活交互性
编译型语言:必须保证每一行代码都能编译通过,程序才能运行,运行高效。例如:OC、C#
脚本语言:代码交互灵活。比如:JavaScript、Python
4)语法简单、代码简洁、使用方便
5)可以轻松地与Objective-C、C语言混合使用(相互调用)
3.Swift语言推出的目的
1)取代Objective-C
2)使我们的程序更加简洁、使我们程序员编写代码更加高效
4.使用Swift开发的要求
1)Xcode版本 >= 6.0
2)Mac系统版本 >= 10.9.3
5.相关数据
Swift自发布之后,备受开发者关注,发布当天(1天时间内):
1)Xcode 6 beta下载量突破1400万次
2)官方发布的电子书《The Swift Prograrmming Language》下载量突破37万次
3)一位国外开发者已经用Swift实现了Flappy Bird游戏(上手Swift4小时,编程加上休息接近9个小时)
6.相关人物
Swift的首席架构师:Chris Lattner
7.性能
1)在进行复杂对象的排序时:
Objective-C的性能是Python的2.8倍,Swift是Python的3.8倍
2)在进行复杂算法时:
Objective-C的性能是Python的127倍,Swift是Python的220倍
以上是苹果官方的数据,实际来讲,Objective-C的运行效率要比Swift高,因为Objective-C更接近底层,Swift是更高级语言,封装了很多东西。
8.语法须知
1)Swift的源文件是.swift
2)Swift不需要编写main函数(Swift是从上往下执行,所以最前面的就被自动当做程序的入口)
3)不需要在每一条语句后面加上分号,除非同一行有多条语句时,必须加分号隔开
4)注释:单行注释与OC的单行注释一致,多行注释不同于OC的多行注释,Swift的多行注释是可以嵌套使用
5)用let来声明常量,用var来声明变量
例如:let radius = 10 // 声明一个常量
var age = 20 // 声明一个变量
var x = 0.0, y = 0.0, z = 0.0 // 声明三个变量
二、常量与变量
常量与变量的命名
1)基本上可以使用任何字符作为常量和变量名
例:let π = 3.1415926
let 网址 = "http://iOS.itcast.cn"
let 😓 = "流泪"
emoji表情是一种特殊的Unicode字符,弹出emoji键盘的快捷键:control + command + 空格
2)注意点
a.不能包含数学符号(比如:+ - * \)
b.不能包含箭头(比如:→)
c.不能包含非法无效的Unicode字符(比如:⚽️)
d.不能是关键字
e.不能以数字开头
f.不能是单独的一个下划线(比如:var _ = 10)
3)语法
a.常量用let来定义:let age = 20 // 定义并初始化一个常量age=20
b.变量用var来定义:var str = "123" // 定义并初始化一个变量str="123"
c.Swift严格要求变量在使用之前必须进行初始化,否则程序报错
三、数据类型
1.Swift中的数据类型(首字母大写)
Int、Float、Double、Bool、Character、String
Array、Dictionary、元祖类型(Tuple)、可选类型(Optional)
2.如何指定变量、常量的数据类型
例:let a:Int = 20 // 定义并初始化一个指定的Int类型的常量
let age:Int // 定义一个指定的Int类型的常量
var str:String = "123" // 定义并初始化一个指定的String类型的变量
但是,开发中我们一般不去指定变量或是常量的数据类型,因为Swift具有自动推断数据类型的功能
例:let age = 20 // Swift会根据20这个值去自动推断出常量age为Int类型
3.整数Int
1)整数分为2种类型
有符号整数(signed):正、负、0
无符号整数(unsigned):正、0
2)Swift提供了8、16、32、64位的有符号和无符号整数
Int8:8位有符号整数 UInt8:8位无符号整数
Int16:16位有符号整数 UInt16:16位无符号整数
Int32:32位有符号整数 UInt32:32位无符号整数
Int64:64位有符号整数 UInt64:64位无符号整数
3)最值
可以通过min和max属性来获取某个类型的最小值和最大值
let minValue = UInt8.min // 获取8位无符号整型的最小值
let maxValue = UInt8.max // 获取8位无符号整型的最大值
4)Int与UInt
Swift除上面的8、16、32、64位的有符号和无符号类型之外,还提供了特殊的Int和UInt类型
Int\UInt的长度取决于当前系统平台
在32位系统平台上,Int和UInt的长度就是32位
在64位系统平台上,Int和UInt的长度就是64位
建议:
a.在定义整型时,别总是在考虑有无符号、数据长度的问题
b.在整型时,尽量使用Int,这样可以保证代码的简介、可复用性
5)每一种Int\UInt类型都有自己的存储范围,存储的数值不可以超出这个范围,否则就编译就会报错
例:Int8的存储范围是-128~127
let num:Int8 = Int8.max + 1 // 数值超出num的存储范围,编译报错
所以讲,Swift是非常安全的语言
6)整数的表现形式
十进制:没有前缀 例:let i1 = 10 // 10
二进制:以0b为前缀 例:let i2 = 0b1010 // 10
八进制:以0o为前缀 例:let i3 = 0o12 // 10
十六进制:以0x为前缀 例:let i4 = 0xA // 10
4.浮点数Float、Double
1)浮点数:小数,Swift提供了两种浮点类型
Double:64位浮点数,当浮点值非常大或需要非常精确时使用此类型(Double至少精确到15位小数)
Float:32位浮点数,当浮点值不需要使用Double的时候使用此类型(Float至少精确到6位小数)
如果我们在没有明确指明浮点类型时,默认是Double类型
例:let f = 2.13 // 默认为Double类型
let f:Float = 2.13 // 指定为Float类型
2)浮点数的表现形式
十进制:没有前缀,可以无指数,也可以有指数
无指数:let f1 = 12.5 // 12.5
有指数:let f2 = 0.125e2 // 0.125 * 10^2 = 12.5
MeN = M * 10^N
十六进制:以0x为前缀,且一定是有指数的
let f3 = 0xC.8p0 // (12 * 16^0).(8 * 16^(-1)) * 2^0 = 12.5 * 1 = 12.5
0xMpN = M * 2^N
5.Bool类型
1)Bool就两种取值:true:真、false:假;在OC中真就是非0值,假就是0,在Swift中没有这一说法
2)在Swift中if语句的条件判断必须是Bool类型的值(ture,false)
例:if (ture) {
println("条件成立")
}
3)比较运算符/逻辑运算符会返回Bool类型的值
ture:真,6>5 (7>6)&&(9!=7)
false:假,6<5 (7>6)&&(9==7)
4)三目运算符的条件判断必须是Bool类型
错误写法:
let a = 10
let c = a ? 100 : 200 // 条件a的值必须是Bool类型的值(ture、false)
正确写法:
let a = 10
let c = a != 0 ? 100 : 200 // a!=0返回ture,所以c=100
6.元祖类型
1)概念
a.元祖类型由N个任意类型的数据组成(N>=0)
b.组成元祖类型的数据可以成为元素
例:let position = (x: 10.5, y: 20)
// 元祖position中有两个元素
// x、y是元素的名称,而10.5、20是元素的值
// x、y都是常量,其值不可改变;但是x是Double类型,而y是Int类型
let person = (name: "Frank")
let data = () // 空元祖
2)元祖的访问
例:var point = (x: 10.5, y: 20)
a.元素名称访问
point.x
point.y
b.元素位置访问
point.0
point.1
3)修改元祖中元素的值
如果用let定义元祖,那么元祖里面的所有元素都是常量,无法修改其元素的值
如果用var定义元祖,那么元祖里面的所有元素都是变量,其元素值是可以改变的
例:var point = (x: 20, y: 30)
point.x = 30
point.y = 50
4)元祖的输出
例:var point = (x: 20, y: 30)
a.直接打印
println(point)
// 输出:(20,30)
b.打印输出指定的元素
println(point.x)
println(point.y)
println(point.0)
5)元祖的使用细节
a.可以省略元素名称
例:let point = (10, 20)
b.可以明确指定元素的数据类型(没有明确指定,系统会自动推断)
例:var person:(Int, String) = (25, "Frank")
// 明确指定:person的第一个元素为Int类型,第二个元素为String类型
在指定元素类型时,必须省略元素名称。
例:var person:(Int, String) = (age: 25, name: "Frank") // 报错:元素名称与元素类型不可共存
c.元祖的各元素不可以随意修改其数据类型,只能存储原始指定的或是系统推断出的类型
例:var person = (25, "Frank")
person.0 = "Kin" // 报错:系统推断person的第一个元素是Int类型
d.可以使用多个变量去接收元祖数据
例:var (x, y) = (10, 20) // x=10 y=20
var point = (x, y) // point是由2个元素组成,分别是10和20
e.可以将元素分别赋值给多个变量
例:var point = (10, 20)
var (x, y) = point // x=10 y=20
f.可以使用下划线忽略某个元素的值
例:var person = (25, "Frank")
var (_ , name) = person // 只想知道name
7.数字格式
1)数字可以增加一些额外的格式,是它们更容易阅读
a.可以增加额外的0
例:let money1 = 001999 // 1999
let money2 = 001999.055100 // 1999.0551
b.可以增加下划线,以增强可读性
例:let money3 = 100_0000 // 100万
let money4 = 1_000_000 // 100万
8.类型转换
1)在Swift语言中,对于数据类型要求是比较严格的,不同类型之间不可以直接相互运算
例:let num1 = 3 // num1是Int类型
let num2 = 0.14 // num2是Double类型
let num3 = num1 + num2 // 这样就会报错
报错原因:num1是Int类型,而num2是Double类型,类型不同,不能相加
解决方案:类型强转
let num = 3 + 0.14 // 不会报错,因为3和0.14都没有被指明数据类型(3和0.14先相加,然后再根据结果推断出Double类型)
2)typealias关键字
与C语言中typedef作用类似
例:typealias MyInt = Int // 定义一个变量类型MyInt,与Int的用法一致
四、运算符
1.Swift支持的运算符
赋值运算符:=
复合赋值运算符:+=、-=、*=、/=
算数运算符:+、-、*、/
求余运算符:%
自增、自减运算符:++、--
比较运算符:==、!=、>、<、>=、<=
逻辑运算符:&&、||、!
三目运算符:?:
范围运算符:..<、...
溢出运算符:&+、&-、&/、&%
2.赋值运算符
1)1对1赋值
例:var a = 5
var b = 6
2)N对N赋值
例:let (x, y) = (1, 2) // x=1, y=2且均为常量
3)Swift中的赋值运算符没有返回值
例:在OC中,可以这样:
int a = b = 20; // b=20会返回20给到a
但是,在Swift中是不可以这样写的,Swift这样规定是可避免因==少写一个=而造成的错误
例:let a = 15
let b = 20
if (a = b) { // 这里a==b写成a=b
println("a==b")
} else {
println("a!=b")
}
在OC中:
第一步:将b的值20赋给a,即为a=20
第二步:a=20会返回20
第三步:对于判断语句而言,返回的20不为0,所以判断条件为真
第四步:程序执行println("a==b")
在Swift中:
第一步:将b的值20赋给a,即为a=20
第二步:a=20不会返回任何值
第三步:对于判断语句而言,无值,就自认也就不是判断语句啦,报错
3.求余运算符
%在Swift中叫“求余运算符”,也有语言叫“模运算符”
1)求余结果的正负取决于%左边数值的正负
例:9 % 4 // 1
-9 % 4 // -1
-9 % -4 // -1
2)与OC不同的是,Swift中的%支持浮点数的取余
例:8 % 2.5 // 0.5
4.范围运算符
1)闭合范围运算符:a...b // 表示:[a, b]
2)半闭合范围运算符:a..<b // 表示:[a, b)
例:for i in 0..<5 {
println(i)
}
// 输出:0 1 2 3 4 (当然啦,输出打印之间格式回车换行)
5.溢出运算符
由于每一种整型都有自己的取值范围,默认情况下,一旦赋值超出取值范围,程序就会编译或运行错误。于是Swift推出溢出运算符,专门针对这类问题,并且专门用于整型
溢出对于有符号整型和无符号整型都是一样的
1)上溢出
例:let x = UInt8.max // x=255
let y = x &+ 1 // y=0
// 1111 11111 &+ 0000 0000 = 1 0000 0000 --> UInt8取8位:0000 0000
2)下溢出
例:let x = UInt8.min // x=0
let y = x &- 1 // y=255
// 0000 0000 &- 1111 1111 = 1111 1111 --> UInt8取8位:1111 1111
3)除0溢出:默认情况下,某一个数除以0,或者对0求余,编译直接报错,但是&/和&%不会
例:let x = 10
let y = x &/ 0 // y=0
let z = x &% 0 // z=0
五、流程控制语句
1.Swift支持的流程控制语句
循环结构:for、for - in、while、do - while
选择结构:if、switch
注意这些流程控制语句的后面一定要跟上大括号{};在OC中如果后面只跟一条语句,是可以省略大括号{}
2.for - in
1)for - in与范围运算符
例:for i in 1...4 {
println(i)
}
// 按顺序从范围中取值并赋给i,每取一次值,就执行一次循环体
// 范围的长度就是循环体执行的次数
2)如果不需要用到范围中的值,可以使用下划线_进行忽略
例:for _ in 0..<3 { // 用下划线_忽略
println(******)
}
// for循环中的i为常量,我们在循环体中不需要取i的值时,就没必要去浪费定义常量i,用下划线_忽略
3.switch
1)与OC中的switch的区别
a.OC中,switch的条件判断语句只能是int或Char类型;而在Swift中,switch的条件判断语句是任意类型
b.OC中,每一个case的结尾没有break,程序就会执行完该case后,接着执行下一个case或是default;而在Swift中,执行完一个case后,程序会默认退出switch
c.OC中,switch的条件判断语句必须放在小括号()内;而在Swift中,switch的条件判断语句不需要小括号()
d.OC中,switch中的case后面可以没有代码;而在Swift中,每一个case后面必须要有代码可以执行
e.OC中,可以不用考虑是否是所有条件都有所考虑;但是在Swift中,必须考虑所有条件,也就是说这个default在情况罗列不全的时候必须加上
例:let age = 2
switch age { // 判断条件语句不用()
case 0: // 每一个case后面必须有代码可执行
println("age=0") // 每一个case后面不必加break
case 1:
println("age=1")
case 2:
println("age=2")
defalut: // 情况罗列不全,default必须加上
println("age为其他数")
}
2)多条件匹配和范围匹配
a.多条件匹配:一个case后面可以填写多个匹配条件,条件之间用逗号隔开
例:let score = 95
switch score/10 {
case 10, 9: // 10或9
println("优秀")
case 8, 7, 6: // 8或7或6
println("及格")
default:
println("不及格")
}
// 输出:优秀
b.范围匹配:case后面可以填写一个范围作为匹配条件
例:let score = 95
switch score {
case 90...100: // [90, 100]
println("优秀")
case 60..<90: // [60, 90)
println("及格")
default:
println("不及格")
}
// 输出:优秀
3)case匹配元祖
例:判断某一个点是否在某一个矩形框内
let point = (1, 1)
switch point {
case (0, 0):
println("这个点在原点上")
case (_ , 0): // 使用下划线忽略元祖中某个元素
println("这个点在x轴上")
case (0, _):
println("这个点在y轴上")
case (-2...2, -2...2):
println("这个点在x=-2、x=2、y=-2、y=2四条直线包围的矩形框内")
default:
println("这个点在其他位置")
}
4)case的数值绑定
在case匹配的同时,可以将switch中的值绑定一个特定的常量或是变量,以便在case后面的语句中使用
例:let point = (10, 0)
switch point {
case (let x, 0): // 如果右边的值匹配成功,就会执行该case
println("这个点在x轴上,x的值是\(x)")
case (0, let y):
println("这个点在y轴上,y的值是\(y)")
case (let x, let y): // 可以写成 case let (x, y)
println("这个点的x的值为\(x),y的值为\(y)")
}
// 这里可以省略default,原因是(let x, let y)包含了所有点
5)关键字where
switch语句可以使用where来增加case的判断条件
例:判断某一个点是否在y=x或y=-x这条直线上
var point = (10, -10)
switch point {
case let (x, y) where x == y:
println("这个点在y=x这条线上")
case let (x, y) where x == -y:
println("这个点在y=-x这条线上")
default:
println("这个点在其他位置")
}
6)关键字fallthrough(贯穿)
a.执行完当前的case后,会接着执行fallthrough后面的case或是default语句,相当于OC中的没有配套break的case
例:let num = 20
var str = "\(num)是个"
switch num {
case 0...50:
str += "0~50之间的"
fallthrough
default:
str += "整数"
println(str)
}
// 输出:20是个0~50之间的整数
b.需要注意的是,fallthrough后面的case不可以定义变量或常量
例:var point = (10, 10)
switch point {
case (0...10, 0...10):
println("这个点的x值和y值都在0到10之间")
fallthrough // fallthrough后面的case不可以定义变量或常量
case let (x, y) where x == y: // case中不可以定义常量(x, y)
println("这个点x==y")
default:
println("这个点在其他位置")
}
4.标签
使用标签的作用就是可以指定跳出某个循环
例:group:
for _ in 0...3 {
for item in 0...4 {
println("做一个俯卧撑")
if item == 2 {
break group; // 退出外部循环
}
}
println("休息一会儿")
}
// 输出:做一个俯卧撑
六、函数
1.格式
1)函数定义格式
func 函数名(形参列表) -> 返回值类型 {
// 函数体...
}
2)形参列表的格式
形参名1: 形参类型1, 形参名2: 形参类型2, ...
例:求两个数的和
func sum(num1: Int, num2: Int) -> Int {
return num1 + num2
}
2.无返回值的函数
1)func 函数名(形参列表) -> Void {
// 函数体...
}
2)func 函数名(形参列表) -> () {
// 函数体...
}
3)func 函数名(形参列表) {
// 函数体...
}
3.无形参的函数
函数如果没有形参,函数名后面的小括号也不能省略
func 函数名() -> 返回值类型 {
// 函数体...
}
4.返回元祖的函数
例:func getPoint() -> (Int, Int) {
return (20, 30)
}
func getStudent(idNum: Int) -> (name: String, age: Int) {
if idNum > 0 {
return ("Frank", 25)
} else {
return ("nobody", 0)
}
}
5.外部参数名
一般情况下,通过形参名,就能推断出这个参数的含义和作用
例:func addStudent(name: String, age: Int, no: Int) {
println("添加学生:name=\(name) age=\(age) no=\(no)")
}
// 这个在函数内部一看就知道这三个参数的含义和作用,但是在调用函数的时候有可能就不清楚每个参数的含义
// 比如调用时:addStudent("Frank", 25, 15)
// 如果不看函数的具体实现部分,你是没办法知道"Frank"、25、15三个形参的含义
为了解决上面这个问题,Swift提供了外部参数名语法。外部参数名可以在调用函数时提醒每个参数的含义
1)外部参数名的定义格式:
外部参数名写在形参名前面,并且与形参之间用空格隔开
func 函数名(外部参数名 形参名: 形参类型) -> 返回值类型 {
// 函数体...
}
例:func addStudent(name: String, stu_age age: Int, stu_no no: Int) {
println("添加学生:name=\(name) age=\(age) no=\(no)")
}
调用的时候:
addStudent("Frank", stu_age: 25, stu_no: 15)
// 一旦定义了外部参数名,在调函数的时候必须加上外部参数名
2)外部参数名的简写
使用#能简化外部参数名,此时的形参名与外部参数名一致
func 函数名(#形参名: 形参类型) -> 返回值类型 {
// 函数体...
}
6.默认参数值
1)可以在定义函数时,给形参指定一个默认值;调用函数时,就可以不用给这个形参传值
例:func addStudent(name: String, age: Int = 20) {
println("添加一个学生:name=\(name), age=\(age)")
}
调用:
addStudent("Frank")
// 输出:添加一个学生:name=Frank, age=20
2)带有默认参数值的形参,Swift会自动给它生成一个跟形参名相同的外部参数名,我们在调用的时候可以给它再次赋值
例:func addStudent(name: String, age: Int = 20) {
println("添加一个学生:name=\(name), age=\(age)")
}
调用:
addStudent("Frank", age: 25)
// 输出:添加一个学生:name=Frank, age=25
3)带有默认参数值的形参前面加一个下划线代表忽略Swift自动给它生成的外部参数名
例:func addStudent(name: String, _ age: Int = 20) {
println("添加一个学生:name=\(name), age=\(age)")
}
调用:
addStudent("Frank", 25)
// 输出:添加一个学生:name=Frank, age=25
7.常量和变量参数
1)常量参数
默认情况下,函数的形参都是常量参数,在函数内部是不可以修改
例:func test(num: Int) {
num = 20
}
// 以上程序编译报错:num默认为常量,不可修改其值
相当于:
func test(let num: Int) {
num = 20
}
2)变量参数
例:编写函数在某个字符串的尾部拼接N个其他字符串
a.不使用变量参数
func append(srcStr: String, subStr: String, num: Int) -> String {
var str = srcStr
for _ in 0..<num {
str += subStr
}
return str
}
b.使用变量参数
func append(var srcStr: String, subStr: String, num: Int) -> String {
for _ in 0..<num {
str += subStr
}
return str
}
七、输入输出参数inout
1.基本使用
1)在C语言中,利用指针可以在函数内部修改外部变量的值
例:void change(int *a) {
*a = 10;
}
int main(int argc, const char *argv[]) {
int num = 20;
change(&num); // int *a = # ---> 指针a指向了num
print("num = %d", num);
return 0;
}
// 输出:num = 10
2)在Swift中,利用输入输出参数,也可以在函数内部修改外部变量的值
例:func change(var num: Int) {
num = 10
}
调用:
var a = 20
change(a)
println("a=\(a)")
// 输出:a=20 (改变的只是num的值,对于外部变量a是没有改变)
例:交换外部两个变量的值
func swap(inout num1: Int, inout num2: Int) {
let tempNum = num1
num1 = num2
num2 = tempNum
}
调用:
var a = 20
var b = 50
swap(&a, &b) // 传入的参数前面必须加上&
println("a=\(a), b=\(b)")
// 输出:a=50, b=20
面试题:不使用第三方变量来交换两个变量的值
第一种方法:直接逻辑运算
func swap(inout num1: Int, inout num2: Int) {
num1 = num1 + num2
num2 = num1 - num2
num1 = num1 - num2
}
第二种方法:利用异或
func swap(inout num1: Int, inout num2: Int) {
num1 = num1 ^ num2
num2 = num1 ^ num2
num1 = num1 ^ num2
}
2.注意事项
1)传入参数时,必须在实参前面加上&
2)传入参数时,不能是常量或是字面量(比如10),因为它们都不可以被更改
3)输入输出参数不可以有默认参数值
4)输入输出参数不可以是可变参数
5)输入输出参数不能再使用let、var来定义(inout与let、var不能共存)
3.输入输出参数的价值
1)可以实现函数的多返回值(其实让函数返回元祖类型)
例:函数实现返回两个参数的和与差
第一种方法:直接通过元祖(sum, minus)返回
func sumAndMinus(num1: Int, num2: Int) -> (sum: Int, minus: Int) {
return (num1 + num2, num1 - num2)
}
调用:
let result = sumAndMinus(30, 20)
let sum = result.0
let minus = result.minus
println("sum=\(sum) minus=\(minus)")
第二种方法:通过输入输出参数
func sumAndMinus(num1: Int, num2: Int, inout sum: Int, inout minus: Int) {
sum = num1 + num2
minus = num1 - num2
}
调用:
var sum = 0
var minus = 0
sumAndMinus(30, 20, &sum, &minus)
println("sum=\(sum) minus=\(minus)")

浙公网安备 33010602011771号