流程控制
闭区间、半闭区间、单侧区间以及区间实际类型分类
闭区间: a...b表示 a<= <= b, 闭区间类型为CLosedRange类型
半闭区间a..<b 表示 a <= <b, 半闭区间类型为Range 类型
**单侧区间 ...a/a...**表示从无穷小到a/从a到无穷大,如果用于数组,单侧区间的边界就是数组不越界, 单侧区间类型为PartialRangeThrough类型
a和b可以是数值,也可以是变量
let a = 0;
let b = 3;
for i in a...b {
}
var c = 0;
var d = 3;
for i in c...d {
}
应用于数组取值:
let array = ["name", "age", "location"];
for i in array[0...2] {
}
switch的应用
-
swift中的switch和其他语言是不太一致的,**case:后面是不允许大括号的,并且每个case默认都调用了break,**如果想和以前一样,处于叠加的算法,需要在case后面加一个fallthrougn
-
swift中如果switch如果已知参数类型,可以使用.语法直接获取case,例如:
enum Answer {case right, wrong}; let answer = Answer.right; switch answer { case .right: print("right"); case .wrong: print("wrong"); default: break; }
-
switch支持区间匹配和元组匹配
let count = 62; switch count { case 0...3: print("0~3"); default: break; } let point = (1,1); switch point { case (2,2): print("point 为 2,2"); case (-2...2, -2...2): print("正方形 包含point"); default: break; }
值绑定
值绑定模式绑定匹配的值到一个变量或常量,例如
// 将值对应绑定到对应的变量/常量 3->x, 2->y
let point = (3, 2);
switch point {
case let(x, y) :
print("this point is \(x) 和\(y)");
}
where条件语句
where表示条件,用于筛选,常见于switch和for中,例如
// 筛选0~20中的2的倍数
for let i in 0...20 where (i % 2 == 0) {
}
// 在switch值绑定时,筛选条件
let point = (1, -1);
switch point {
case let(x,y) where x == y:
print(x,y);
case let (x,y ) where x > y :
print(x,y);
default:
break;
}
// switch 值绑定时,筛选
let age = 20;
switch age {
case let x where x > 10 :
print("大于10:" + "\(x)");
case let x where x < 10 :
print("小于10:" + "\(x)");
default:
break;
}
标签语句
标签语句类似于goto语句,可以设置代码标签,然后直接使用标签跳转, 但是只适用于循环语句中,例如for和while
outer: for i in 0...3 {
for j in 4...9 {
if j == 6 {
continue outer;
}
else {
print(j);
}
}
}
guard 语句
语句为:guard + 条件 + else {}; 当条件为false时,执行代码,用于退出作用域操作,例如判断条件退出的操作
代码内必须包含退出作用域的关键字,例如return/break/continue等
func check(name:String?,pwd:String?) -> Bool {
guard name != nil else {
print("name 为 nil");
return false;
}
guard pwd != nil else {
print("pwd 为 nil");
return false;
}
print("\(name!)和\(pwd!) 通过验证");
return true;
}
let name : String? = nil;
let pwd : String? = "pwd";
check(name: name, pwd: pwd);
函数
函数注释
和oc中快捷键一样 command + option + /,不同的是OC使用/** */表示,swift使用///表示文档注释
参数标签 (传参时显示参数名称)
类似C++/Dart中的语法,传参时设置参数标签可以显示参数名,默认格式参数名就是参数标签,可以自定义,也可以省略.主要是方便调用明确参数含义
// 默认情况 age 即是参数标签,又是形参
func test (age : Int, name : String) {
print(name, age);
}
test(age: 10, name: "李晨光");
// 设置参数标签,其中 age ,name 为参数标签, a和n为参数名,函数内部使用n和a表示参数
func test2 (age a : Int, name n : String) {
print(n, a);
}
test2(age: 10, name: "李晨光");
// 设置忽略参数标签
func test3(_ age : Int, _ name : String) {
print(name, age);
}
test3(10, "李晨光");
参数默认值
swift允许设置参数默认值,和c++中默认参数值不同的时,c++设置默认参数必须是从右到左设置,不允许间隔,swift允许单独设置特定的参数的值,原因是swift支持参数标签,不会由于设置默认值导致函数传参导致歧义
func test (age : Int, name : String, location : String = "石家庄") {
print(name, age, location);
}
test(age: 10, name: "李晨光");
// 只设置中间也是允许的
func test (age : Int, name : String = "李晨光", location : String) {
print(name, age, location);
}
test(age: 10, location: "石家庄");
可变参数
swift允许设置数量可变的参数,但是 只允许设置最多一个,访问数据使用下标,类似c++中的数组参数
// prift函数的定义 Any表示任意类型的参数, ...表示数量可变
public func print(_ items: Any..., separator: String = " ", terminator: String = "\n")
// 表示多个Int类型参数
func test(params:Int...) {
for i in params {
print("参数" + "\(i)");
}
}
test(params: 1,2,3,4,5);
prift()函数查看源码就是使用可变参数实现的
输入输出参数(inout)
作用类似于地址传递(指针传递),实现原理就是地址传递,不同之处是使用 inout关键字标识.
func test6 (age : inout Int) {
age += 10;
}
test6(age: &age);
函数重载(overload)
函数返回值不同不能成为重载,并且默认参数值和函数重载一起使用时,容易造成二义性,c++会编译报错,swift不会,但是不建议这样使用
内联函数
swift中的内联函数是编译器优化时实现的,不建议主动设置内联函数.c++中也有内联函数的概念,使用inline标识,
swift 通过 **@inline(never)/@inline(__always)**控制是否内联
//
@inline(never) func test() {
}
// 开启编译器优化后,@inline(__always) 可以设置使用内联
@inline(__always) func test() {
}
swift中是不建议开发者直接使用@inline标识修改内联函数的.
函数类型
函数类型是有函数返回值类型和 参数值类型构成的.
常用于函数作为函数参数,或者定义函数变量时使用
// 该函数的函数类型为 (Int->Int)
func test(age : Int) -> Int {
}
// 可以表示成()->Int/void -> Int
func test1(age : Int) -> void {
}
typealias
用于定义别名,例如函数别名,变量别名等
枚举
基本使用
相比其他语言中的枚举,Swift的枚举功能更加强大,支持String Int float,Boolean等类型的值,需要在定义时声明类型即可.不要求必须提供值
enum name {
case xxx
case xxx
}
enum Score : String {
case A
case B
}
枚举的遍历
如果想获取包含枚举所有成员的一个集合,需要遵循CaseIterable协议,生成allCases属性,例如
enum Beverage : CaseIterable {
case coffer, tea, juice
}
for i in Beverage.allCases {
print(i);
}
关联值
枚举成员可以指定任意类型的关联值存储到枚举成员中,例如
// Score 为定义的枚举类型
enum Score {
// num 和 grade是定义的成员名,关联值类型分别是Int和String
case num(score : Int);
case grade(grade : String)
}
// 都可以作为成绩使用
let myscore = Score.num(score: 100);
let myGrade = Score.grade(grade: "A");
// 第二个例子
// 定义一个二维码枚举
enum Barcode {
// upc和qrcode是成员值,关联类型分别是元组和字符串
case upc(Int, Int, Int, Int)
case qrCode(String)
}
// 和值绑定一起使用,
switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case .qrCode(let productCode):
print("QR code: \(productCode).")
}
// 定义所有的值绑定类型都是let, 可以将常量关键字前提,表是整个值绑定的是常量值
switch productBarcode {
case let .upc(numberSystem, manufacturer, product, check):
print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case let .qrCode(productCode):
print("QR code: \(productCode).")
}
关联值经常和值绑定属性一起使用
原始值
枚举成员可以被默认值(称为原始值)预填充,这些原始值的类型必须相同。.rawValue()可以获取原始值
// 设置的默认值称为原始值,
enum ASCIIControlCharacter : Character {
case tab = "\t"
case lineFeed = "\n"
case carriageReturn = "\r"
}
隐式原始值:
Int和String会自动分配默认值,String以case 枚举值的名称作为值,Int类型值是从0开始递增
使用原始值初始化枚举值:
如果在定义枚举类型的时候使用了原始值,那么将会自动获得一个初始化方法,这个方法接收一个叫做
rawValue
的参数,参数类型即为原始值类型,返回值则是枚举成员或nil
。你可以使用这个初始化方法来创建一个新的枚举实例。由于可能返回nil, 返回类型是可选类型
enum Planet: Int { case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune } let possiblePlanet = Planet(rawValue: 7) // possiblePlanet 类型为 Planet? 值为 Planet.uranus let possiblePlanet = Planet(rawValue: 12)// 返回nil
关联值和原始值,在枚举值内存分配上的差异
数据存储位置不同,关联值的值存储在枚举的内存中,原始值不存储在枚举内存中, 使用关联值的枚举类型,内存分配大小和关联的对象有关 使用原始值的枚举类型,内存只分配一字节,和成员值类型无关,因为原始值无论占据多大内存空间,都不是直接存储在枚举内存中的,只是在.rawValue时再获取
// Score 为定义的枚举类型
enum Score {
// num 和 grade是定义的成员名,关联值类型分别是Int和String
case num(chinese : Int, math: Int, english: Int); // 3 * Int64(8) = 24字节
case grade(grade : String) // 1字节
}
let myScore = Score.num(chinese: 100, math: 100, english: 100);
MemoryLayout.stride(ofValue: Int()); // 8字节
MemoryLayout.stride(ofValue: myScore); // 32字节 使用25字节,由于内存对齐格式是8字节,所以分配32字节
MemoryLayout.size(ofValue: myScore); // 25字节 3 * Int + 1字节(标识case 是哪个类型)
MemoryLayout.alignment(ofValue: myScore); // 8字节
enum Score2 : String {
// num 和 grade是定义的成员名,关联值类型分别是Int和String
case num
case grade
}
enum Score3 : Int {
// num 和 grade是定义的成员名,关联值类型分别是Int和String
case num
case grade
}
let myScore2 = Score2.num;
MemoryLayout.stride(ofValue: myScore2); // 1
MemoryLayout.size(ofValue: myScore2); // 1
MemoryLayout.alignment(ofValue: myScore2); // 1
let myScore3 = Score3.num;
MemoryLayout.stride(ofValue: myScore3); // 1
MemoryLayout.size(ofValue: myScore3); // 1
MemoryLayout.alignment(ofValue: myScore3); // 1
递归枚举
递归枚举是一种枚举类型,它有一个或多个枚举成员使用该枚举类型的实例作为关联值。使用递归枚举时,编译器会插入一个间接层。你可以在枚举成员前加上 indirect 来表示该成员可递归。
enum ArithmeticExpression {
case number(Int)
// 可递归成员
indirect case addition(ArithmeticExpression, ArithmeticExpression)
indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
}
// 所有成员可递归
indirect enum ArithmeticExpression {
case number(Int)
case addition(ArithmeticExpression, ArithmeticExpression)
case multiplication(ArithmeticExpression, ArithmeticExpression)
}
let five = ArithmeticExpression.number(5)
let four = ArithmeticExpression.number(4)
let sum = ArithmeticExpression.addition(five, four)
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))
func evaluate(_ expression: ArithmeticExpression) -> Int {
switch expression {
case let .number(value):
return value
case let .addition(left, right):
return evaluate(left) + evaluate(right)
case let .multiplication(left, right):
return evaluate(left) * evaluate(right)
}
}
print(evaluate(product))
// 打印“18”
嵌套枚举
枚举类型是可嵌套的,例如
// 以RPG游戏中的每个角色为例,每个角色能够拥有武器,因此所有角色都可以获取同一个武器集合。而游戏中的其他实例则无法获取这些武器(比如食人魔,它们仅使用棍棒)。
enum Character {
enum Weapon {
case Bow
case Sword
case Lance
case Dagger
}
enum Helmet {
case Wooden
case Iron
case Diamond
}
case Thief
case Warrior
case Knight
}
// 现在,你可以通过层级结构来描述角色允许访问的项目条。
let character = Character.Thief
let weapon = Character.Weapon.Bow
let helmet = Character.Helmet.Iron
参考文章:
可选项
基本定义
可选类型是一种数据类型用于处理值缺失的情况,swift中数据类型的变量是不允许设置nil的,使用可选类型后可以设置
可选类型使用 "?"标识,在数据类型后面添加,表示该数据类型是可选类型,默认值为nil
let a : Int? = 10; // 合法
let a : Int? = 10; // 合法
let a : Int? = nil; // 合法
let a : Int = nil; // 不合法,
let a : Int? = 10; // 等价于
let a: Optional<Int> = 10; // 证明可选类型是一种数据类型
Optional 是一个含有两种情况的枚举,None 和 Some(T),可以理解是通过枚举关联值实现的
源码如下:
enum Optional<T> : Reflectable, NilLiteralConvertible { case None case Some(T) init() init(_ some: T) /// Haskell's fmap, which was mis-named func map<U>(f: (T) -> U) -> U? func getMirror() -> MirrorType static func convertFromNilLiteral() -> T? }
强制解包
由于可选类型是一种对象类型,内部存储的变量值不能直接使用,所以需要解包获取数据. 使用!强制解包可选项包装的对象,解包语法为:可选对象值后添加"!";不允许对nil做解包操作,崩溃
let a : Int? = 10; // a是Optional(10) 可选类型,直接使用会报错
let b = a + 10; // 报错,需要解包
let b = a! + 10; // 解包后获取到值再操作,
自动解析(不常用)
在声明可选变量时使用感叹号(!)替换问号(?)。这样可选变量在使用时就不需要再加一个感叹号(!)来获取值,它会自动解析。但是需要处理为nil的情况
var myString:String!
myString = "Hello, Swift!"
if myString != nil {
print(myString)
}else{
print("myString 值为 nil")
}
可选绑定(Optional binding)
使用可选绑定(optional binding)来判断可选类型是否包含值, 如果包含就把值赋给一个临时常量或者变量 并返回ture, 否则返回false,
常用于条件判断语句if/while中
let name : String? = nil;
if let constantName = name {
print(constantName); // 可以打印可选类型包含的值,条件语句判断时,隐藏了一层解包操作,就是可选绑定
}
else {
// name 为nil 执行
}
可选链
**定义:**通过在想调用的属性、方法,或下标的可选值后面放一个问号(?
),可以定义一个可选链
**作用:**可选链式调用是一种可以在当前值可能为 nil
的可选值上请求和调用属性、方法及下标的方法。如果可选值有值,那么调用就会成功;如果可选值是 nil
,那么调用将返回 nil
// 定义类
class Person {
var residence: Residence?
}
class Residence {
var numberOfRooms = 1
}
let john = Person(); // 实例化
let roomCount = john.residence!.numberOfRooms
// 如果residence为nil,会出发运行时错误,
// 如果通过?调用可选链,当为nil时终端调用并返回nil,
if let roomCount = john.residence?.numberOfRooms {
print("John's residence has \(roomCount) room(s).")
} else {
print("Unable to retrieve the number of rooms.")
}
空合并运算符 ??
形式如 a ?? b 的称为空合并运算符, 语句含义是:如果a==nil,返回b; 否则返回a
要求:
-
a必须是可选类型,b类型不限制
-
a和b的数据类型必须相同,例如都是Int或者String
-
如果b是可选类型,则返回的a也是可选类型,否则返回的a会自动解包,例如
let a : Int? = nil; let b : Int = 10; print(a ?? b); // 10 let a : Int? = 20; let b : Int = 10; print(a ?? b); // 20 let a : Int? = 20; let b : Int? = 10; print(a ?? b); // Optional(20)
总结
初学swift第二天,以笔记整理的方式加深学习过程中的印象.如有理论性的知识错误,欢迎各位多多指教