从上周末开始到周三,除了学习老师教的观察者模式(Observer Pattern)和几种排序方法外,其他的时间多数在利用Xcode的Playground,来练习Swift的基本语法。此外也将专案做了版本控制,和拆分了大致的专案资料夹架构(用MVVM的模式)。
以下附上目前整理完成的Swift笔记:
基本资料型态大致有 Int 、 Double 、 Float 、 Bool
查看型别:
var str
str = "abc"
print(type(of:str))
Optional 本质是一个 enum 型别,里面定义了两种状况。
任何型别只要有加上可选型别(optional type
)都可以设置成nil
。使用方法为在型别标注後面加上一个问号?
,如下:
// 在宣告变数时 型别标注後面加上一个问号 ?
var someScore: Int? // 因为目前尚未指派 所以目前 someScore 会被设置成 nil , 也就是没有值的状态
someScore = 100
print(someScore!) //如果确定可选型别的变数内有值,加上惊叹号可取出值
someScore = nil
如果没有经过这个步骤,不能直接把变数设成 nil , 比如以下这样写会报错:
var someScore: nil
guard let 和 if let 的作用大同小异,同样可让 let 後的常数名和 optional 同名,主要的差别在以下几个地方:
// newName 可在 guard let else { } 後继续使用
func showName(name: String?) {
guard let newName = name else { return }
print("my name is \(newName)")
}
//newName 不可在 if let else { } 後继续使用
func showName2(name: String?) {
if let newName = name {
print("my name is \(newName)")
}
print("my name is \(newName)")
}
App 的表单输入页面很适合搭配 guard let 检查栏位。
guard let 也可以串接多个 optional,以下为检查表单栏位新增资料的例子:
func createBook(title: String?, price: Double?, pages: Int?) {
guard let title = title, let price = price, let pages = pages else {
return
}
// 以下区块可加入新增 Book 的程序码
print("\(title) costs $\(price) and has \(pages) pages.")
}
字串相加可以用 + 号 ,也可以用 .append()
字串相等判断用 ==
用 == "" 或属性 .isEmpty判断是否为空字串
属性 .count取得字串长度
分割字串 : split(separator) ,分割後以阵列型态传回
let str = "ab,cd,ef"
let arr = str.split(separator:",") //用逗号分割
var a = "a";
//字串中用 " \(变数、常数或表达式) " 来串接其他型态变数,其他型态与字串不能直接用加号串接!
var b = "ddd\(a)";
print(b) // 印出 ddda
var c = "cc"
var e = "e"
print(c+e); //印出cce
// u{24}的值为$这个符号, 代表 Unicode 纯量 U+0024
let dollarSign = "\u{24}"
let str2 = "What a lovely day !"
print(str2.count) // 印出字元数量:19
if str4.hasPrefix("It is") { //如果前缀字串相同
print("Prefix")
}
if str4.hasSuffix("Sunday") { //如果後缀字串相同
print("Suffix")
}
()
前後包起来,每个值以逗号分隔,范例如下: let myInfo = ("Kevin Chang", 25, 178.25)
let myHeight = myInfo.2 //这边依照顺序的索引值,取出178.25
// 将上面宣告的 myInfo 分解成三个常数
let (myName, myAge, myRealHeight) = myInfo
print("My name is \(myName) .") // 印出:My name is Kevin Chang .
//把不需要的用底线 _ 标记
let (_, _, myTrueHeight) = myInfo
print("My height is \(myTrueHeight) .") // 印出:My height is 178.25 .
//在宣告元组时就个别给里面的值一个名称
let herInfo = (name:"Jess", age:24, height:160.5)
print("Her name is \(herInfo.name) . ") // 印出:Her name is Jess .
元组比较大小会依序由左到右逐个比较,直到有两个值不相等为止。而如果所有值都相等,则会将这一对元组称为相等的。
Swift 在比较元组的成员时,限制最多只能比较六个成员,如果有七个或七个以上成员则无法比较。
// true 因为 3 等於 3,但是 apple 小於 bird (依字串的各字元由左至右逐个比较)
(3, "apple") < (3, "bird")
Swift 提供三种基本的集合型别:Array
、Set
、Dictionary
来储存集合资料。
Array 阵列:按顺序储存资料。
Set 集合:没有顺序、不能重复储存资料。
Dictionary 字典:没有顺序,键值对 key : value
,也就是可以经由唯一的识别键找到需要的值。
var a = 1.2
var b = Int(a) // b强转後等於1
即使变数或常数是 nil
也能变出预设值
a ?? b , 先判断a
是否为nil
,如果a
有值,不是nil
,就会解析a
并返回,但如果a
为nil
,则返回预设值b
let defaultColor = "red"
var userDefinedColor: String? // 未指派值 所以预设为 nil
var colorToUse = userDefinedColor ?? defaultColor // 未指派值给 userDefinedColor ,所以会返回 defaultColor
print(colorToUse) // 这边即印出:red
闭区间运算子: a...b,定义一个包含从a
到b
(包括a
和b
)的所有值的区间。b
必须大於等於a
。
半开区间运算子: a..<b,定义一个从a
到b
但不包括b
的区间。b
必须大於等於a
,但当a
等於b
时,则不会有值。
单侧区间运算子: a... 或 ...a,代表一个只有以一边a
为起点或终点,另一边则是无限延伸的区间
let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names[2...] { // 从阵列索引值为 2 的值开始依序印出 Brian Jack
print(name)
}
var arr3 = [Int]() // 宣告一个型别为 Int 的空阵列
var anotherArr: [Int] = [] // 宣告另一个型别为 Int 的空阵列
var arr : Array<Int> = Array<Int>()
合并阵列内容
var threeInts = Array(repeating: 0, count: 3) // repeating是指定预设值,这边代表阵列为 [0, 0, 0]
// 接着再创建一个 [2,2,2] 的阵列
var anotherThreeInts = Array(repeating: 2, count: 3)
// 最後将两个阵列合并
var SixInts = threeInts + anotherThreeInts
// 会变成 [0,0,0,2,2,2]
新增 / 插入 / 删除阵列内容
var arr6 = ["Apples", "Eggs"]
arr6.append("Milk") //新增。变成 ["Apples", "Eggs", "Milk"]
arr6.insert("Rice" ,at: 0) //插入。变成 ["Rice" ,"Apples", "Eggs", "Milk"]
arr6.remove(at:1) //移除。变成 ["Rice", "Eggs", "Milk"]
用 for in 遍历 Array 中的值(类似for each)
var arr7 = ["Rice" ,"Apples", "Eggs", "Milk"]
for item in arr7 {
print(item)
}
//当同时也需要获得阵列值时 可以使用 enumerated() 方法
for (index, value) in arr7.enumerated() {
print("Item \(index + 1): \(value)")
}
// 会依序印出:
// Item 1: Rice
// Item 2: Apples
用 sort 排序。 sort() 会影响原本的阵列内容; sorted() 则会将排序结果回传成另一阵列,不影响原内容。
arr.sort() //[1,2,5,7]
用 sort(by:) 设定排序方式。
arr.sort(by:>) //[7,5,2,1]
用 reverse()、 reversed() 将阵列反置。
arr.reverse() //[1,2,5,7]
用 contains(_:) 检查某元素是否存在於阵列中,并回传 boolean 值。
arr.contains(2) // true
key(键) 必须是唯一且不可重复
用来储存多个相同型别的值。每个值(value
)都属於一个唯一的键(key
),键作为字典中这个值的识别符号,所有键的型别也必须相同(键与值的型别不一定要相同)。
字典内的值没有顺序,所以需要根据这个键(key
)来找到需要的值(value
)。宣告字典型别时,使用 Dictionary<Key, Value>
// 宣告一个字典型别
var someDict: Dictionary<String, String>
// 或是这样也可以
var someAnotherDict: [String:String , String:a, String:b]
guard else跟 if else一样,後面都是接结果为 true 或 false 的条件,但 guard 却有以下几点不同之处:
guard 喜欢依赖别人,不能没有 else。
guard 後专门描述我们希望成立的条件。当条件成立时,程序将离开 guard 搭配的 else { },继续往下执行我们希望条件成立时做的事。
当 guard 的条件不成立时,将执行 else { } 的程序。
else { } 的程序执行後,必须离开 guard 所在区块,如此才不会继续往下执行条件成立时要做的事。就像刚刚的例子,我们利用 return 离开 function motherSay。
guard age > 18, weight > 40 else { //多重条件
print("年纪不够大,体重太轻,只能乖乖念书")
return
}
使用for-in
遍历一个集合内的所有元素,像是一个数字区间、阵列、字典中的值或是字串内的字元,例子如下:
for index in 1...3 {
print(index)
}
swift 的 switch case 不需要写break,执行完第一个成功的case後,就会自动跳出。如果在特殊情况下需要执行紧接着的下一个case
内的程序,就要用到 fallthrough。加上 fallthrough後进入到的下一个case,不会对其条件做比对,而是直接执行其内的程序。 比如以下程序码:
let number7 = 5
var str4 = ""
switch number7 {
case 2,3,5,7,11,13,17,19:
str4 += "It is a prime number. "
fallthrough
case 100,200:
str4 += "Fallthrough once. "
}
print(str4) //印出 It is a prime number. Fallthrough once.
case
中比对的情况也可以是一个区间:
let number5 = 0
var str3: String
switch number5 {
case 0...10:
str3 = "几"
case 11...100:
str3 = "很多"
default:
str3 = "超级多"
}
print("我有\(str3)颗苹果") //印出:我有很多颗苹果
当不同版本的作业系统提供的API不一样时,在程序中必须判断作业系统版本,确保app可以顺利运作,程序码范例如下:
//平台名称可以是iOS、macOS或watchOS。版本号可以是大版本号像是iOS 10,或是较小的版本像是macOS 10.12
if #available(平台名称 版本号, ..., *) {
// 在某个平台或版本下使用特别的 API
} else {
// 而其他的平台或版本则使用其他的 API
}
if #available(iOS 10, macOS 10.12, *) {
// 在 iOS 使用 iOS 10 的 API
// 在 macOS 使用 macOS 10.12 的 API
} else {
// 使用先前版本的 iOS 和 macOS 的 API
}
从宽松到严格依序为:
open : 不同的模组可以继承也能够存取。例如一个定义在framework中的open类别,可以在 APP 中 import framework 然後继承、覆写它
public: 模组内可以继承、覆写;模组外可以看到与使用,但不能继承、覆写。
internal (预设) : 模组内可以继承、覆写;模组外看不到。
fileprivate: 同一个档案内可以存取。
private: 同一类可以存取。
swift 4 开始,允许 extension 中的程序可以存取原本类别中的 private 等级变数或函式,但 extension 必须与原类别在同一个档案中
func
关键字,函式格式如下: func 函式名称(参数: 参数型别) -> 返回值型别 {
内部执行的程序
return 返回值
}
实际写成如下:
func addOne(number: Int) -> Int {
// number 即为被指派参数的常数 只能在函式内部范围内使用
let n = number + 10
print(n)
return n
}
addOne(number: 12) // 呼叫函式传入整数 12 印出 13
如果在函式呼叫时,不想要加上 argument label,可以用 _ 代替。范例如下:
// ... 宣告一个参数数量不固定的函式,传进函式中的参数被转为阵列型态
// func addOne(_number: Int ...)
// = 後为呼叫时没给值时的预设值
// func addOne(_number: Int = 5)
func addOne(_number: Int ) -> Int {
// number 即为被指派参数的常数 只能在函式内部范围内使用
let n = number + 10
print(n)
return n
}
addOne(12)
// inout关键字,传址, 取出时变数前加 &
swift中的enum可以写function,容易实现状态模式
列举使用case
关键字定义成员值,例子如下:
enum CompassPoint {
case north
case south
case east
case west
}
var directionToHead = CompassPoint.west
// 这时已经可以自动推断这个变数的型别为 CompassPoint
directionToHead = .south // 如果要再指派新的值 可以省略列举的型别名称
switch directionToHead {
case .north:
print("Lots of planets have a north")
case .south:
print("Watch out for penguins") // 这行会被印出
case .east:
print("Where the sun rises")
case .west:
print("Where the skies are blue")
}
value type
)与参考型别(reference type
)。值型别会储存实际的值,而参考型别只是储存其在记忆体空间中配置的位置。本周剩下的时间会继续练习後面的语法,并且开始摸索 ARKit 的使用方式,因为我们的 APP 将会使用到 AR 的技术。
<<: Day 10 - 主动学习 Active Learning
访问控制(RAM)是什麽? 访问控制(Resource Access Management,RAM)...
相信写过 javaScript ES6 的大家一定使用过,high order function, ...
前言 在上一张中我们介绍了使用callback function的目的与缺点,虽然可以帮助我们处理非...
终於来到第30天了,每天写下来不知不觉就一个月了,记得第一课还在自我介绍, 转眼间已经要第三十课。这...
撰写这篇时,其实心情已经平复了不少 从 2014 年加入新创团队至今也超过七年了,过程中从未有过长假...