Functional programming
以 function 為單位,一個 function 的 input 可以是一個 function 或是 value,
而 function 的 output 也可以是一個 function 或是 value 而作為下一個 function 的 input,
這樣透過一連串的計算函式設計來得到結果,並且限制無法使用變數或是狀態來避免造成 side effect。
Reactive programming
以 data streams / data flow 為概念作為設計,
reactive programming 將所有的東西當作 data,包含 click event 或 http request 等等,
並且透過 functional programming 的設計將所有 data 串為一個 stream,
且這些 data 可透過 asynchronize 產生,
因此可以理解成,當一個 data,如 click event 執行相對應的 function,
這個 function 會產生下一個 function 的 input,下一個 data,如 pop alert 執行相對應的 function,
就會是下一個 function。
RxSwift
RxSwift - 與 Swift 溝通的 framework
RxCocoa - 透過 reactive 的方式來使用 Cocoa API 的 framework
Observable - 發出改變的被觀察對象
Observer - 監聽 observable 的觀察者
Disposable - 管理觀察者的記憶體
建立 Observable
Observable 可透過事件發出改變值或特定信號給觀察者,事件有 onNext, onError 與 onCompleted。
若發出 onCompleted 或 onError 事件,會使得整個 stream 終止並解除觀察者的監聽。
let observableSample: Observable<Int> = Observable.create { (observer) -> Disposable in
observer.onNext(1)
return Disposables.create()
}
建立 Observer
建立方法有兩種。
1 初始化 observer
let observerSampleA: AnyObserver<Int> = AnyObserver { (event) in
}
observableSample.subscribe(observerSampleA)
2 使用 closure
observableSample.subscribe { (event) in
}
Observable type
RxSwift 提供不同類型的 observable 可以直接呼叫使用。
- Single
> 只能發出一個改變值 "或" 一個 error
- Completable
> 不發出任何改變值,只能發出一個 completed "或" 一個 error
- Maybe
> 只能發出一個改變值 "或" 發出一個 completed "或" 一個 error
- Driver
Driver 保證不會產生 error,且會在 main thread 執行加上改變的狀態共享。
這種類型的 observable 可以安全的與 UI 事件做結合,
確保執行到 UI 相關的 function 時會在 main thread 執行且不會因為產生 error 而解除觀察。
let observableSample: Observable<String> = Observable.create { (observer) -> Disposable in return Disposables.create() } let driver = observableSample.asDriver(onErrorJustReturn: "error") driver.drive(label.rx.text)
Observer type
- Binder
除了前面提到的 AnyObserver 外,Binder 用來特別處理 UI 事件,
不會處理 error 也可以在特定的 thread 上執行,default 為 main thread。
let observer: Binder<String> = Binder(label) { (view, text) in view.text = text }
AWS 官方有教學影片在 YouTube 上,Squid Proxy Server Setup And Authentication
這邊是整理這部影片內的重點步驟,以及實際操作後發現影片沒提到的部分。
目前 AWS EC2 的伺服器服務地區可參考 區域表
<以下假設你以及擁有 AWS 帳號並且了解如何 launch AWS EC2 instance>
<上述教學影片是使用 Ubuntu,以下步驟是使用 Liunx>
Port
EC2 建立完 instance 後,請確認有開啟 port 3128
-
Install
sudo su yum install squid
使用 yum 安裝 squid
-
Edit config
nano /etc/squid/squid.conf
編輯 squid 的設定檔,一開始預設拒絕所有外部連線,因此影片一開始的教學是允許所有連線,
將 "http_access deny all" 修改成 "http_access allow all"。
如果你不想讓自己的 proxy server 在網路上被掃描到的話,可以加上,
via off
forwarded_for off
follow_x_forwarded_for deny all
forwarded_for delete -
Start, Restart, Stop
service squid start service squid restart service squid stop
更動完 config 後,都必須重新啟動,可以直接使用 restart,會包含 stop 與 start。
-
Check
netstat -tulnp | grep squid
啟動後可以使用這行指令來確認 squid 是否已經啟動
-
Log
tail -f /var/log/squid/access.log
可以即時顯示目前 proxy server 連線狀況。
最後只需要使用 ip:3128 就完成了
關於 ReactiveCocoa 的介紹,網路上已經有許多資源可以參考,
想要特別提醒的是,目前在 ReactiveCocoa 的官方 GitHub 中,
有分 ReactiveCocoa, ReactiveSwift, ReactiveObjC 等 repo,
原因是從 ReactiveCocoa 3.0 開始便只支援 Swift 語法。
所以如果想使用單純 Objective-C 的版本,目前最穩定的版本為 2.5。
以下的範例都是使用 2.5 版本的 ReactiveObjC。
Signal
Signal 是 ReactiveCocoa 的主要基本概念,
創建 Signal 以及訂閱 Signal ,而資料的流向會以 Signal 發送至訂閱對象。
Create Signal & Subscribe Signal
以下為創建 Signal 以及訂閱 Signal 的範例,
其中 createSignal 可以回傳一個 RACDisposable 的 block,
這個 block 會在此 Signal 結束或取消後執行。
sendNext 會將 value 傳送至訂閱對象的 subscribeNext block 中。
RACSignal *skSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"123"];
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{
}];
}];
[skSignal subscribeNext:^(id x) {
}];
RAC()
RAC 可將 object 的 property 訂閱 Signal,透過 Signal 最後回傳的 value 來取代此 property。
RACSignal *skSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"123"];
[subscriber sendCompleted];
return nil;
}];
NSLog(@"%@",self.value);
RAC(self,value) = skSignal;
NSLog(@"%@",self.value);
RACObserve()
RACObserve 會產生一個 Signal,此 Signal 可直接綁定某個 Object 的 property。
RACObserve(self, self.value);
配合 RAC(),就可以讓某個 object 的 property 去訂閱另外一個 object 的 value。
RAC(object1, object1.value) = RACObserve(object2, object2.value);
Signal Flow
可以串起多個 Signal 成為一個可處理複雜資料的程序。
- then
當 Signal 處理完成後,可以使用 then 在後面接上另外一個 Signal。
有幾點要特別說明。
- sendNext 與 sendCompleted 會由最後一個處理的 Signal 送給訂閱對象。
- 前一個 Signal 一定要執行 sendCompleted 才會交由下一個 Signal 執行。
- 若前一個 Singal 執行 sendError,便會停下程序,並傳送 error 給訂閱對象。
RACSignal s = [[RACSignal createSignal:^RACDisposable (id<RACSubscriber> subscriber) { [subscriber sendNext:@"1"]; [subscriber sendCompleted]; return nil; }] then:^RACSignal { return [RACSignal createSignal:^RACDisposable (id<RACSubscriber> subscriber) { [subscriber sendNext:@"2"]; [subscriber sendCompleted]; return nil; }]; }];
- doNext
doNext 的 block 會在 Signal sendNext 前先被執行。
且 sendNext 的 value 也會被帶到 doNext 的 block 中。
RACSignal *s = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"2"];
[subscriber sendCompleted];
return nil;
}] doNext:^(id x) {
NSLog(@"123");
}];
- map
map 的功能是在 Signal sendNext 時先將 value 帶到 map block 中,
最後回傳一個新的 value。
RACSignal *s = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"2"];
[subscriber sendCompleted];
return nil;
}] map:^id(id value) {
return @"4";
}];
- flattenMap
flattenMap 基本流程與 then 相同,差別在於可直接將前一個 Signal 的結果傳遞給下一個 Signal 來處理。
RACSignal *s = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"2"];
[subscriber sendCompleted];
return nil;
}] flattenMap:^RACStream *(id value) {
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"2"];
[subscriber sendCompleted];
return nil;
}];
}];
- initially
初始化的 block 可以在執行 Signal 先被執行。
RACSignal *s = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"2"];
[subscriber sendCompleted];
return nil;
}] initially:^{
NSLog(@"123");
}];
Objective-C 屬於動態程式語言,可以在程式執行的同時改變程式結構,
這次要介紹的就是 iOS在 run time 時改變 method 實作。
首先說明 iOS 內 method 的結構,在 class 內宣告的 method 都是 struct。
typedef struct objc_method *Method;
typedef struct objc_ method {
SEL method_name;
char *method_types;
IMP method_imp;
};
SEL 表示 method 的名稱,
types 表示 method 的參數,
IMP 是一個 function pointer 用來指出 method 真正實作的 address。
在 compile 時會將每個 class 的 method 列成一份表格,
objc 是一個動態程式語言,意指這份表格在 compile 完成且進入 runTime 時是可以隨時修改的,
若要在動態時修改表格,首先要先抓到要修改哪個 class 的哪個 method,
並且先攔截下這個 method,再進行修改。
先定義要改變實作的目標。
@interface TestClass : NSObject
@end
@implementation TestClass
- (void)test
{
NSLog(@"test");
}
@end
首先建立一個 NSObject,主要用來模擬 method 的結構,
並將想要置換的 method 原封不動的 copy 到這個結構中。
@interface TestMethod : NSObject
@property (assign, nonatomic) SEL selector;
@property (assign, nonatomic) IMP implementation;
@property (assign, nonatomic) Method method;
再來就是攔截目標 method,
在 compile 時期對於 class 的每份 method 都會註冊一個簽名,
這個簽名記錄了這個 method 的所有資訊。
因此先透過 class 以及 SEL 取得 NSMethodSignature,
TestClass *c = [[TestClass alloc] init];
Class inClass = [c Class];
SEL inSelector = @selector(old);
NSMethodSignature *methodSignature = [inClass instanceMethodSignatureForSelector:inSelector];
透過這份簽名,將原本 method 的一些資訊 copy 至模擬的 method object。
TestMethod *method = [[TestMethod alloc] init];
method.selector = inSelector;
method.hasReturnValue = [methodSignature methodReturnLength] > 0;
method.methodSignature = methodSignature;
method.returnValueLength = [methodSignature methodReturnLength];
透過 class 以及 SEL 去抓 IMP。
method.method = class_getInstanceMethod(inClass, inSelector);
method.implementation = class_getMethodImplementation(inClass, inSelector);
這時候將原本的 method 實作替換成攔截訊號。
method_setImplementation(method.method, (IMP)_objc_msgForward);
這時候在原本的 class 內動態新增一個新定義的 method。
SEL newSelector = NSSelectorFromString(@"New");
class_addMethod(inClass, newSelector, method.implementation, method.typeEncoding);
最後來呼叫原本的 function。
[c old];
因為剛剛已經將原本的實作替換成攔截訊號,因此原本的 class 會收到一連串的 delegate 來處理。
第一個收到的 delegate 是要確認這個 method 主要負責的對象是誰。
- (id)forwardingTargetForSelector:(SEL)aSelector
第二個收到的 delegate 會要要求一份 method 的簽名。
在這個時間點,原本的 method 已經無法再使用了,
但剛剛有事先將實作保存在模擬的 method object 內,可以回傳這個 method 的簽名。
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
第三個收到的 delegate 會給予一份實作的 invocation。
如果剛剛有回傳事先保留的 method,這裡的 invocation 便會是 method 的資訊,
雖然原本的 method 已經無法使用,但剛剛在原本的 class 有定義一個新的 method,
所以可以這邊將 invocation 的 selector 換成新的名稱來執行。
- (void)forwardInvocation:(NSInvocation *)anInvocation
Swift 中的 access control 主要區分兩種環境,分別是 module 跟 source file,
在 Xcode 中,一個 target 或是一個 library 可以識別成一個 module,
source file 指的就是單一的 swift 檔案。
Open && Public
代表權限可以在任何 module 的 source file 使用。
module A
open class MyClassA {
}
module B
class MyClassB {
let c = MyClassA()
}
兩者的差別會出現在使用 classes 的時候,
是使用 public 的 class 無法在其他的 module 繼承,但 open 可以,
class 的 property 或是 function 等等也是相同的情況。
module A
open class MyClassA {
}
public class MyClassA2 {
}
module B
class MyClassB: MyClassA {
}
上述如果繼承 MyClassA2 會出現 error。
Internal
限制只能在所屬的 module 內的 source file 使用。
module A
internal class MyClassA {
}
class AnotherMyClassA {
let c = MyClassA()
}
module B
class MyClassB {
let c = MyClassA()
}
上述的 MyClassB 會有 error。
File-private
限制只能在此 source file 使用。
fileprivate class MyClass {
}
let c = MyClass()
class MyClass2 {
let c = MyClass()
}
上述中的 MyClass2 無法建立 MyClass 型別,因為在不同的檔案內無法使用。
Private
限制在所屬的程式區塊。
class MyClass {
private var num = 123
}
let c = MyClass()
c.num = 456
上述無法操作 num,因為 num 限制在 MyClass 的區塊內使用。
自定義限制權限
決大部分的型別,若不額外設定,在宣告時便會將 internal 當作預設權限,
自訂時,要注意其自定義的型別與其內部成員的關係。
例如,若將一個 class 設定為 fileprivate,那麽內部成員預設就會變成 fileprivate,
且針對內部成員不能設定超過 fileprivate 的權限,也就是只能設定 fileprivate 或 private。
fileprivate class MyClass {
var num = 123
private func myFunc() {
}
}
Tuple
在 tuple 內,如果有兩個不同限制權限的型別,會以最低 level 的當成此 tuple 的限制。
比如說有 internal 與 private,那麼此 tuple 就會是 private。
Function
若 function 的參數或回傳值是有其他的權限,必須對 function 改變限制權限來符合其參數或是回傳值。
class MyClass {
private class MyClass2 {
}
private func() -> MyClass {
}
}
Enumeration
沒辦法對 enumeration 內的 case 單一設定不同的限制權限。
如果是設定初始值,初始值型別的限制權限不能超過 enumeration 本身的權限。
Subclass
Subclass 的權限不能比 sueprclass 還高,但是內部成員在某些情況下是允許的。
public class MyClass {
fileprivate func myFunc() {
}
}
internal class MyClass2: MyClass {
override internal func myFunc() {
super.myFunc()
}
}
上述的例子,MyClass2 的權限並沒有比 MyClass 來的高,
但是 override 的 fuction 權限卻比 superclass 還高,
因為在 MyClass2 內呼叫 super myFunc, 而 MyClass 跟 MyClass2 因為在同一個 source file,
所以是可以正常被呼叫到的,如果 MyClass2 寫在跟 MyClass 不同的 source file 就無法正常執行。
變數,常數,property 與 subscript
這些的限制權限都不能高過於本身的型別。
Getter & Setter
與變數常數相同的規則,比較不同的是,可以額外針對 set 做較嚴格的限制權限。
class MyClass {
private(set) var num = 0
var numP: Int {
didSet {
num = numP
}
}
}
Initializer
Initializer 基本上會跟著所屬的 class 或是 structure 的限制權限而改變,
但如果有在 class 或是 structure 使用 public 或是 open 的話,
都必須對 initializer 宣告 public 才能使用。
Protocol
- Protocol 內宣告的限制權限必定與 protocol 本身相同,無法更改。
- Porotocol 內使用的型別限制權不能比 protocol 本身更低。
- 實作的限制權限會 follow 原本的 protocol。
- 可以修改實作的限制權限,但不能比原本的 protocol 更低。
Extension
建立的任何 extension 都會參照原本的型別限制權限,
也可以讓 extension 有新的限制權限,其內部成員都會跟隨新的限制權限。
使用 extension 實作 protocol 的情況下,不能修改 extension 的限制權限。
泛型設計主要是可以讓型別一直到實體使用的時候再決定,而不用一開始就宣告,
這樣的方式可以能讓程式碼變得更有彈性也更容易重複利用。
泛型函式
下述是傳統來設計替換兩個變數的函式
func swapTwo(_ a: inout Int, _ b: inout Int) {
let temp = a
a = b
b = temp
}
使用泛型的目的在於,此函式的參數不再侷限於 Int。
func swapTwo<T>(_ a: inout T, _ b: inout T) {
let temp = a
a = b
b = temp
}
T 代表的是型別參數,可隨意命名。
泛型類別
可以在 classes, structures 或是 enumerations 宣告泛型類別,並且在建立實體時宣告類別。
class MyClass<T> {
var pro: T?
func myFunc() -> T? {
return pro
}
}
let c = MyClass<Int>()
extension 可以使用泛型類別。
class MyClass<T> {
var pro: T?
}
extension MyClass {
var getPro: T {
return pro
}
}
泛型類別限制
某些情況下函式會需要限制參數類別,比如說屬於某個 class 或是 protocol 類別。
class MyClass {
}
protocol MyProtocol {
}
func MyFunc<T: MyClass, E: MyProtocol> (c: T, p: E) {
}
有某些關鍵字可以來代表屬於哪種限制。
func MyFunc<R: Equatable>(para: R, para2: R) {
if para == para2 {
}
}
關聯類別
在 protocol 內先定義某個泛型類別,再由實作對象來決定真正類別,關鍵字為 associatedtype。
protocol MyProtocol {
associatedtype newType
var pro: newType { get }
}
class MyClass: MyProtocol {
typealias newType = Int
var pro: Int {
get {
return 1
}
}
}
限制子句
加上 where 針對此泛型做額外限制。
class MyClass<T> where T: Equatable {
}
若將 protocol 當作 type 做傳遞時,此 protocol 內有 associated type,
也可以使用 where 來針對此泛型做限制。
protocol MyProtocol {
associatedtype newType
}
func myFunc<T: MyProtocol> (_ para: T) -> Void where T.newType: Equatable {
}
可以在 extension 再加上 where 限制,extension 的 function 必須要滿足限制才能使用。
class MyClass<T> {
func(para: T) {
}
}
extension MyClass where T: Equatable {
func(para: T, para: String) {
}
}
Protocols 提出 methods, property 等等的需求,本身不需要實作,
實作交由實際要滿足這些要求的對象。
基本宣告與實作對象
protocol MyProtocol {
}
class MyClass: MyProtocol {
}
若有繼承,要將 protocol 放在 superclass 後面。
protocol MyProtocol {
}
class MyClass {
}
class MyClass2: MyClass, MyProtocol {
}
Property 需求
定義 property 時必須要加上 get, set,若是 readonly,只需加上 get。
protocol MyProtocol {
var pro: Int { get set }
var pro2: String { get }
}
class MyClass: MyProtocol {
var pro: Int {
get {
return 123
}
set {
print(newValue)
}
}
var pro2: String {
return "123"
}
}
Methods 需求
protocol MyProtocol {
func myFunc() {
}
}
class MyClass: MyProtocol {
func myFunc() {
print("func")
}
}
若有其他類型的 method,例如 static 或是 mutating,必須在 protocol 先定義。
protocol MyProtocol {
static func myFunc() {
}
mutating func myFunc(para: int) {
}
}
class MyClass: MyProtocol {
static func myFunc() {
}
func myFunc(para: Int) {
}
}
Initializer 需求
在 protocol 可以定義 designated initializer 或是 convenience initializer,
實作對象都必須要加上 required,除非 class 被定義為 final。
protocol MyProtocol {
init(para: Int)
}
class MyClass: MyProtocol {
required init(para: Int) {
}
}
final MyClass: MyProtocol {
init(para: Int) {
}
}
Protocol 類型
Protocol 可以被當作類型,因此可以當作 function 的參數類型或是 array 的 value 類型,
符合 protocol 類型的就是實作的類別。
protocol MyProtocol {
func test()
}
class MyClass: MyProtocol {
func test() {
print("class")
}
}
class MyClass2: MyProtocol {
func test() {
print("class")
}
}
func myFunc(para: MyProtocol) {
para.test()
}
myFunc(para: MyClass())
let array: [MyProtocol] = [MyClass(), MyClass2()]
Delegation
可以使用 protocol 來實現 delegation 的 design pattern。
protocol MyProtocol {
func willStart()
func didStart()
func endStart()
}
class MyClass {
var delegate: MyProtocol?
func myFunc() {
delegate?.willStart()
}
}
class MyClass2: MyProtocol {
func willStart() {
}
func didStart() {
}
func endStart() {
}
}
let c = MyClass()
c.delegate = MyClass2()
With Extension
可以在 extension 內實作 protocol。
protocol MyProtocol {
}
class MyClass {
}
extension MyClass: MyProtocol {
}
若類別裡面已經實作了 protocol 的需求,可以在 extension 加上空實作。
protocol MyProtocol {
func myFunc()
}
class MyClass {
func myFunc() {
}
}
extension MyClass: MyProtocol {}
繼承 Protocol
Protocol 之間可以互相繼承。
protocol MyProtocol {
func myFunc()
}
protocol MyProtocol2: MyProtocol {
}
class MyClass: MyProtocol2 {
func myFunc() {
}
}
Class only Protocol
定義此 Protocol 只有 classes 能夠使用。
protocol MyProtocol: class {
}
多種 protocol 類型
將 protocol 當作類型時,可以合併多種的 protocol,使用 & 關鍵字。
protocol MyProtocol {
}
protocol MyProtocol2 {
}
class MyClass: MyProtocol, MyProtocol2 {
}
func myFunc(para: MyProtocol & MyProtocol2) {
}
func myFunc(MyClass())
檢查 protocol
使用 is 跟 as 。
protocol MyProtocol {
}
class MyClass {
}
let c = MyClass()
if let result c as? MyProtocol {
}
Optional 要求
在 protocol 內的要求都必須要實作,但也可以宣告 optional 代表不一定要滿足的要求,
但宣告時前面一定要加上 @objc,意思是與 objc 的 class 互通使用,
因此無法使用在 structure 或是 enumeration 上。
@objc protocol MyProtocol {
@objc func myFunc()
}
class MyClass: MyProtocol {
}
也適用之前提到的 optional chaining。
@objc protocol MyProtocol {
@objc var pro: Int {get}
}
class MyClass {
var delegate: MyProtocol?
}
class MyClass2: MyProtocol {
}
let c = MyClass()
c.delegate = MyClass2()
c.delegate?.pro
在 optionals protocol 中定義 property 的時候只能夠宣告 get,無法使用 set。
Protocol extension
額外在 extension 加上實作。
protocol MyProtocol {
var pro: String { get }
}
extension MyProtocol {
func myFunc() -> String {
return pro
}
}
在 extension 內加上預設實作,若有預設實作,則滿足對象不一定要實作,
若有滿足對象有實作,則會覆蓋掉 extension 預設的實作。
protocol MyProtocol {
var pro: String { get }
}
extension MyProtocol {
var pro: String {
get {
return "123"
}
}
}
class MyClass: MyProtocol {
var pro: String {
get {
return "456"
}
}
}
Extensions 可以對已存在的 class, structure, enumeration 或是 protocol 新增功能,
但要注意不能複寫原有的功能。
基本宣告
class MyClass {
}
extension MyClass {
}
新增運算 property
class MyClass {
var num = 123
}
extension MyClass {
var test: Int {
get{
return num
}
set {
num += newValue
}
}
}
新增 initializers
需要注意的是 Class 的 extension 只能新增 convenience initializer。
Class MyClass {
}
extension MyClass {
convenience init(para: Int) {
self.init()
}
}
新增 Methods
class MyClass {
}
extension MyClass {
func test {
print("test")
}
}
新增 mutating methods
struct MyStruct {
var test = 123
}
extension MyStruct {
mutating func mu(para: Int) {
self.test = para
}
}
新增 subscripts
class MyClass {
}
extension MyClass {
subscript(para: String) -> String {
return para
}
}
新增巢狀類型
class MyClass {
}
extension MyClass {
enum MyEnum {
case a
case b
}
}
Type Casting
用以檢查 instance 屬於哪種型別或是轉成指定型別。
is & as
用 is 來確認型別,只會回傳 true or false。
subclass 可以被確認成其 superclass 的型別,
但 superclass 無法被確認其 superclass 的型別。
class MyClass {
}
class MyClass2: MyClass {
var c2 = 123
}
class MyClass3: MyClass {
var c3 = 456
}
let array = [MyClass2(), MyClass3()]
for v in array {
if v is MyClass2 {
print("MyClass2")
}
else if v is MyClass3 {
print("MyClass3")
}
}
用 as 可以將 instance 來轉型至指定型別,回傳 optionals,
因此可以使用 as? 或是 as! 來處理。
subclass 可以被轉成成其 superclass 的型別,
但 superclass 無法被轉成其 superclass 的型別。
class MyClass {
}
class MyClass2: MyClass {
var c2 = 123
}
class MyClass3: MyClass {
var c3 = 456
}
let array = [MyClass2(), MyClass3()]
for v in array {
if let c = v as? MyClass2 {
print(c.c2)
}
else let c = v as? MyClass3 {
print(c.c3)
}
}
Any & AnyObject
Any 可表示 instance 的任何型別包含 function 型別,AnyObject 只能表示任何 class 的型別。
class MyClass {
}
func myFunc() -> Int {
retur 0
}
var array1 = [Any]()
array1.append(MyClass())
array1.append(myFunc)
var array2 = [AnyObject]()
array2.append(MyClass())
配合 switch case
class MyClass {
}
func myFunc() -> Int {
retur 0
}
var array = [Any]()
array1.append(MyClass())
array1.append(myFunc)
for v in array
{
switch v {
case is Int:
print("Int")
case let c as MyClass:
print("MyClass")
case let f as () -> Int:
print("Func")
default:
print("??")
}
}
Nested Types
Enumerations, classes 與 structure 都屬於巢狀類型。
class MyClass {
struct MyStruct {
class MyClass2 {
enum MyEnum {
struct MyStruct2 {
}
}
}
}
}
引用方法
let c = MyClass.MyStruct.MyClass2.MyEnum.MyStruct2()
當運行過程中出現非預期或是沒有正確 output 時,會需要知道實際發生了什麼問題,
Error handling 便是提供了一個能知道錯誤原因的方法。
Error enum
所有的 error handling 都只能 handle 實作 Error protocol 的 enum。
enum MyError: Error {
case errorA
case errorB
case errorC
}
透過 function 丟出 error
enum MyError: Error {
case errorA
case errorB
case errorC
}
func myFunc(para: Int) throws {
if para == 0 {
throw MyError.errorA
}
else if para == 1 {
throw MyError.errorB
}
}
do try catch
在 function 前面加上 try 來 handle error,若沒有 throw error 會繼續走下去,
這裡有一個地方要注意,因為必須要 handle 到全部的 error,所以至少要有一個 default catch 情況,
enum MyError: Error {
case errorA
case errorB
case errorC
}
func myFunc(para: Int) throws {
if para == 0 {
throw MyError.errorA
}
else if para == 1 {
throw MyError.errorB
}
}
do {
try myFunc(para: 1)
print("pass")
} catch MyError.errorA {
print("a")
} catch MyError.errorB {
print("b")
} catch {
print("default")
}
Errors to optional
使用 try? 的話,若有 throw error,便會轉成 nil。
enum MyError: Error {
case errorA
case errorB
case errorC
}
func myFunc(para: Int) throws {
if para == 0 {
throw MyError.errorA
}
else if para == 1 {
throw MyError.errorB
}
}
try? myFunc(para:1)
若肯定不會 throw error 可以使用 try!來處理。
enum MyError: Error {
case errorA
case errorB
case errorC
}
func myFunc(para: Int) throws {
if para == 0 {
throw MyError.errorA
}
else if para == 1 {
throw MyError.errorB
}
}
try! myFunc(para:2)
defer
Defer 這個關鍵字能包起一個區段,這個區段會在 function 結束後才會被執行。
func myFunc() {
defer {
print("clean")
}
for index in 0...5 {
print(index)
if index == 0 {
break
}
}
}