在工程師的職涯中,最難解的問題不是code寫不出來,而是無法說服其它人採用我的想法。
原因不外乎幾點:
- 沒有想清楚問題
- 無法用對方理解的語言溝通
- 沒有清楚表達想法的核心價值
今天孫治華老師在短短2個小時裡講解pitch的重點以及表達的技巧,這項soft skill值得我們學習
在工程師的職涯中,最難解的問題不是code寫不出來,而是無法說服其它人採用我的想法。
原因不外乎幾點:
今天孫治華老師在短短2個小時裡講解pitch的重點以及表達的技巧,這項soft skill值得我們學習
撰寫程式過程中,免不了需要處理錯誤情形。
如果希望程式碼好維護,會將錯誤偵測和錯誤處理分開來
Swift特別的是enum能和error type結合,產出可讀性高的程式碼
2016-07-21 新增使用程式碼刻UI元件
2016-07-22 新增使用segue
2016-08-10 新增Switch button和Segmented Control, 修改segue用法
*2016-08-23 新增UIPickerView和UISearchController
Closure在Swift有二層意思。第一種是一般認知的global或nested functions,函式中擁有free variable可被nested function存取;或作為變數傳給另一個function
第二種為closure expression,在function的定義後加上簡短表示式。在Swift中,closure通常是指closure expression
在靜態語言中,nil or null的判斷是件重要卻常被忽略的事
當一個object或變數為null而沒被排除時,程式很有可能crash或出現莫明其妙的bug
Swift增加了optional幫助程式設計師避掉可能的crash,語法雖然簡單卻需要時間理解觀念
當資料可能為nil時,我們在宣告變數時加入?。1
2
3
4
5var customerName: String? // nil
var regards = "Thank you! "
if let name = customerName { // skip
regards += name
}
當執行到if let時,因為customerName為nil而判斷式不會成立
沒有給定初始值的變數最好加上optional,在程式剛執行時還未給予值的情況下可避免crash
另一種情況是function的回傳值可能為nil時,使用optional會是合理的選擇1
2
3
4
5
6
7
8
9
10
11
12
13
14func findStore(storeName: String) -> String? {
storeList = ["7-11", "FamilyMarket", "Hi-life"]
for store in storeList {
if store == storeName {
return "Find \(storeName)!"
}
}
return nil
}
if let result = findStore("Poya") {
print(result)
}
findStore的回傳值為optionals,當storeList沒有符合的字串時則回傳nil
上例中result為nil,所以不會印出任何值
變數為optional時,使用!可以強制unwrap並存取資料1
2let result = findStore("7-11")
result! // Find 7-11!
如上例,但此操作不符合官方的建議用法,同時也失去安全性
當使用某些資料型態的值做為變數時,需要資料型態轉換。
如果不確定轉換是否成功,使用as?會回傳optional,如此可避免nil帶來的問題1
2
3
4
5
6
7
8
9
10
11
12
13let playlistDictionary = [
[
"title": "Native",
"artist": "OneRepublic"
]
[
"title": "V",
"artist": "Maroon 5"
]
]
let playlist = playlistDictionary[0]
let title = playlist["title"] as? String
當我們存取playlist的title時,由於不確定回傳的值是否為nil,所以使用as?做型態轉換
參考資料:
Type Casting官方文件
在C/C++中, 使用enum可提升程式碼的可讀性
而Swift的enum擴充原本的功能,使用上有更多的彈性
1 | enum ProgrammingLanguage { |
不同於數C和Objetive-C,Swift的enum並沒有預設的整數值,也就是說case C並不隱含整數0的概念
使用時除了第一個比較傳統的寫法外;你也可以省略enum的型別名稱,這也是Swift的syntax sugar
1 | let secondLanguage = .CPP |
使用switch做判斷時一樣能用簡短的表示法。因為使為enum當參數,compiler會檢查型別,switch也能省去default的值
這是Swift的enum和其它語言不一樣的地方,我們能為enum創造關聯性的值1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16enum CurrencyType {
case USD(Double)
case TWD(Int)
}
var price = CurrencyType.USD(33.99)
func payCheck(price: CurrencyType) {
switch price {
case .USD(let usDollar):
let ntDollar = usDollar * 33
print("總共為: \(ntDollar) NTD")
case .NTD(let ntDollar):
print("總共為: \(ntDollar) NTD")
}
}
CurrencyType包含二種貨幣,美金和新台幣. 而前者需要Double來表示;後者則是Int
在宣告price時,我們給予一個Double的值表示美金的價錢;而switch case需要加上對應的參數
這項特性讓我們使用enum時除了表示狀態,還能給予相對應的值
除了上述的Associated Values, Swift的enum也有C/C++給予初值的特性1
2
3
4
5
6enum HTTPResponse {
case Continue = 100
case Success = 200
case MultipleChoices = 300
case NotFound = 404
}
這個例子中,HTTPResponse一開始就給予raw value. 而raw value只能為字串,字元,浮點數或整數,並且不能重複
假如定義了一組enum含有raw value,則我們能使用raw value查循狀態1
2
3
4
5
6
7
8
9enum HTTPResponse {
case Continue = 100
case Success = 200
case MultipleChoices = 300
case NotFound = 404
}
let statusCode = 300
let httpResponse = HTTPResponse(rawValue: statusCode) // MultipleChoices
將外界傳來status code回應HTTPResponse相對應的狀態;假如沒找到則回傳nil
rawValue回傳的值為optional,即使沒找到也不會讓程式hang住
下一篇接著介紹optional
參考來源:
官方文件
在上一篇介紹Swift Protocol基本的用法和優點。
這篇接著討論Swift使用protocol帶來的設計觀念:Protocol Oriented Programming
在物件導向設計中,繼承(inheritance)是常被誤用的觀念。
當二個”看起來”類似的類別套入繼承的關係後,有些操作便不適合
例如:圓形和橢圓形適合使用繼承的關係嗎? 前者有半徑;後者有長軸和短軸
如果將橢圓形繼承圓形,則初始化時,半徑為無意義的值
所以在重構時,很重要的一點是分㦚二個類別是”Is A”或”Has A”的關係
如同前例,橢圓形並”不是”一種圓形;相反的,跑車”是”車子的一種
於是討論這類問題時,組合(composition)是一個更適合的pattern
由於Swift的class只能繼承單一class,但class能擁有多個protocol
所以在設計class時,我們傾向將各特性分為各種protocol
而protocol能繼承另一個protocol,可以組合各種小功能的protocol完成有彈性的設計1
2
3
4
5
6protocol Movable {
func move()
}
protocol FastMovable: Movable {
func move()
}
Swift語言包含各種protocol,可分為三種類別:”Can Do”, “Is A”, “Can Be”
這三種protocol的命名方式也是Swift的慣例
假如有個class為Car,我們可以利用protocol組合該類別的基本行為1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36enum Direction {
case Up, Down, Left, Right
}
struct Position {
let x: Int
let y: Int
}
protocol Movable {
func move(direction: Direction, distance: Int)
}
protocol VehicleType {
var hoursePower: Int { get set }
var price: Double { get set }
init(position: Position)
}
class Car: VehicleType, Movable {
var hoursePower: Int = 200
var price: Double = 4000.25
required init(position: Position) {
self.position = position
}
func move(direction: Direction, distance: Int) {
switch direction {
case .Up: position.y += distance
case .Down: position.y -= distance
case .Left: position.x -= distance
case .Right: position.x += distance
}
}
使用protocol的組合可以讓class的可讀性增加,並且更有彈性。
初次接觸Swift時,少了pointer的機制,讓人好奇過去function call的call by value, call by reference是否有所不同
而Swift除了class外,其餘資料型態皆為value type。
這篇文章整理Swift Value and Reference type的差異
當copy value type資料時,Swift其實是創造一份獨立的”拷貝”,任何改變原本變數的值不會影響拷貝的資料1
2
3
4
5struct Person { var name: String = "Alex" }
let somePerson = Person()
let otherPerson = somePerson
somePerson = "John"
print("\(somePerson.name), \(otherPerson.name)) // print John, Alex
當somePerson的name被改變時,otherPerson並不受影響
當copy reference type資料時,Swift只是增加一個reference指向原本的資料1
2
3
4
5class Person { var name: String = "Alex" }
let somePerson = Person()
let otherPerson = somePerson
somePerson = "John"
print("\(somePerson.name), \(otherPerson.name)) // print Alex, Alex
由以上例子可知,在使用function時要注意是否需要改變傳入的值
因為大多數的資料型態為value type,傳入function後無法修改原本的值
在multi-thread的環境中,這也一定確保資料的完整性,降低debug的困難度
使用value type:
使用reference type:
參考來源:
Apple官方文件
在design pattern中,interface是一個重要的概念。
它能夠將class之間共通但些許不同的行為提升到更高的層次,解決物件導向裡容易誤用繼承的問題
而Swift提供protocol做為interface和資料型態,讓物件導向設計變得更直觀
1 | protocol ExampleProtocol { |
protocol的property可宣告為gettable或settable,後者同時也可使用get
而function只需要宣告輸入參數和回傳型態,不需要實作
class一旦使用protocol,就必須提供其property和function的實作。
使用protocol在實務上有什麼好處呢?
假如我們有二個class,Employee和HourlyEmployee,分別為正職和計時人員。 以及計算員工薪資的function1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26class Employee {
let name: String
let address: String
init(name: String, address: String) {
self.name = name
self.address = address
}
func pay() -> (salary: Double, bonus: Double) {
return (3000, 400)
}
}
class HourlyEmployee: Employ {
var hourlyWage: Double = 15.00
var hoursWorked: Double = 8
override pay() -> (salary: Double, bonus: Double) {
return (hourlyWage * hoursWorked, 0)
}
}
func payEmployee(employee: Employee) {
let paycheck = employee.pay()
}
假如之後程式修改時,不小心將HourlyEmployee的pay刪除,則呼叫payEmployee時會回傳Employee的結果
這在程式碼增長的情況下是很容易出現的bug
使用protocol可減少此類bug的發生1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22protocol Payable {
func pay() -> (salary: Double, bonus: Double)
}
class Employee: Payable {
//略
func pay() -> (salary: Double, bonus: Double) {
return (3000, 400)
}
}
class HourlyEmployee: Payable {
//略
override pay() -> (salary: Double, bonus: Double) {
return (hourlyWage * hoursWorked, 0)
}
}
func payEmployee(employee: Employee) {
let paycheck = employee.pay()
}
假如HourlyEmployee的pay function被刪,complier會提示error。
protocol不只能當interface使用,也能做type結合array、dictionary使用1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24protocol Flyable {
func fly()
}
class Human {
let name: String
}
class Bird: Flyable {
let name: String
func fly() {
print("I believe I can fly!")
}
}
func makeActions(action: [Flyable]) {
}
let human = Human(name: "John")
let sparrow = Bird(name: "GOT")
let animals: [Flyable] = [human, sparrow] // Error, human is not Flyable
指定animals為Flyable的array時,compiler會檢查array的值是否符合
這也類似有經驗的C/C++ programmer會將使用enum當function的參數,否則各參數都是int時,容易讓使用者誤用
protocol能幫我們減少無謂的bug及提高程式碼的可讀性
在objective-c當中,如果method需要回傳二個以上的值,則可以使用帶有二個property的object或dictionary
而Swift新增tuples此資料型態,可一次回傳多個值
1 | let stockProfolio = (3000, 3.4) |
此為無識別字的tuple,在存取時不方便使用。
我們可透過以下方式給予識別1
2
3let (share, price) = stockProfolio
share // 3000
price // 3.4
上述的使用方式不夠直觀,建議使用named tuples增加可讀性1
2
3let stockProfolio = (share: 3000, price: 3.4)
stockProfolio.share // 3000
stockProfolio.price // 3.4