3 months ago

AWS 官方有教學影片在 YouTube 上,Squid Proxy Server Setup And Authentication
這邊是整理這部影片內的重點步驟,以及實際操作後發現影片沒提到的部分。

目前 AWS EC2 的伺服器服務地區可參考 區域表

<以下假設你以及擁有 AWS 帳號並且了解如何 launch AWS EC2 instance>
<上述教學影片是使用 Ubuntu,以下步驟是使用 Liunx>

  1. Port
    EC2 建立完 instance 後,請確認有開啟 port 3128

  2. Install

    sudo su
    yum install squid
    

    使用 yum 安裝 squid

  3. 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

  4. Start, Restart, Stop

    service squid start
    service squid restart
    service squid stop
    

    更動完 config 後,都必須重新啟動,可以直接使用 restart,會包含 stop 與 start。

  5. Check

    netstat -tulnp | grep squid
    

    啟動後可以使用這行指令來確認 squid 是否已經啟動

  6. Log

    tail -f /var/log/squid/access.log
    

    可以即時顯示目前 proxy server 連線狀況。

最後只需要使用 ip:3128 就完成了

 
8 months ago

關於 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。
有幾點要特別說明。

  1. sendNext 與 sendCompleted 會由最後一個處理的 Signal 送給訂閱對象。
  2. 前一個 Signal 一定要執行 sendCompleted 才會交由下一個 Signal 執行。
  3. 若前一個 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");
}];
 
about 1 year ago

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
 
about 1 year ago

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

  1. Protocol 內宣告的限制權限必定與 protocol 本身相同,無法更改。
  2. Porotocol 內使用的型別限制權不能比 protocol 本身更低。
  3. 實作的限制權限會 follow 原本的 protocol。
  4. 可以修改實作的限制權限,但不能比原本的 protocol 更低。

Extension

建立的任何 extension 都會參照原本的型別限制權限,
也可以讓 extension 有新的限制權限,其內部成員都會跟隨新的限制權限。

使用 extension 實作 protocol 的情況下,不能修改 extension 的限制權限。

 
about 1 year ago

泛型設計主要是可以讓型別一直到實體使用的時候再決定,而不用一開始就宣告,
這樣的方式可以能讓程式碼變得更有彈性也更容易重複利用。

泛型函式

下述是傳統來設計替換兩個變數的函式

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) {
    }
}
 
about 1 year ago

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"
    }
  }
}
 
about 1 year ago

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
 }
}
 
about 1 year ago

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()
 
about 1 year ago

當運行過程中出現非預期或是沒有正確 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
    }
  }
}
 
about 1 year ago

Optional chaining ?

以下述例子說明,當建立了 myClass 的 instance 後要如何取得 myClass2 中的 p。

class MyClass {
    var c2: MyClass2?
}
class MyClass2 {
    var p = 1
}
let c = MyClass()

依照之前學的經驗,需要先對 optionals unwrapping 取出真正的值,才能繼續操作。

c.c2!.p

但上述的程式碼將會造成 error,因為 c2 是 nil,因此需要改成以下的方法,才能安全操作,
以下的方法會回傳 nil。

c.c2?.p

各種 optional chaining

class MyClass {
    var c2: MyClass2?  
}
class MyClass2 {
    var array = [Int]()
    var number: Int {
    return array.count
  }
  func test() {
    print("123")
  }
  var c3: MyClass3?
}
class MyClass3 {
    var p: String?
}

對 property 讀取或是給予值時,都會是 nil 或是失敗。

let c = MyClass()
c.c2?.number
c.c2?.c3 = MyClass3()

呼叫 method 時,會將所有回傳 type 改為 Optional,因此若沒有回傳值,
會把 Void 改為 Void?,皆會取得 nil。

let c = MyClass()
c.c2?.test()

也可以進行多層的 optionals chaining,邏輯與上述相同。

let c = MyClass()
c.c2?.c3?.p