Swift Tips笔记

“??”操作符可以判断输入并在当左侧的值是非 nil 的 Optional 值时返回其 value,当左侧是 nil 时返回右侧的值.

例:

var level: Int?
var startLevel = 1

var currentLevel = level ?? startLevel
//currentLevel等于默认值1

Swift 中 Selector 只能使用字符串在生成,这样难以重构,并且无法在编译期间进行检查,可以利用柯里化进行封装改造。

代码如下:

protocol TargetAction {
    func performAction()
}

struct TargetActionWrapper<T:AnyObject>: TargetAction {
    weak var target: T?
    let action: (T) -> () -> ()
    
    func performAction() {
        if let t = target {
            action(t)()
        }
    }
}

enum ControlEvent {
    case TouchUpInside
    case ValueChanged
}

class Control {
    var actions = [ControlEvent: TargetAction]()
    
    func setTarget<T:AnyObject>(target: T, action: (T) -> () -> (), controlEvent: ControlEvent) {
        actions[controlEvent] = TargetActionWrapper(target: target, action: action)
    }
    
    func removeTargetForControlEvent(controlEvent: ControlEvent) {
        actions[controlEvent] = nil
    }
    
    func performActionForControlEvent(controlEvent: ControlEvent) {
        actions[controlEvent]?.performAction()
    }
}

写一个可变参数的函数只需要在声明参数时在类型后面加上 “…”, 在同一个方法中只能有一个参数是可变的,可变参数都必须是同一种类型.

例:

“func sum(input: Int...) -> Int {
    return input.reduce(0, combine: +)
}

print(sum(1,2,3,4,5))
// 输出:15”

如果有一个类型 Class 同时实现了 A 和 B接口(Protocol),为了避免冲突,在调用前应该进行类型转换。

例:

protocol A {
    func bar() -> Int
}
protocol B {
    func bar() -> String
}

class Class: A, B {
    func bar() -> Int {
        return 1 
    }
    func bar() -> String {
        return "Hi"
    } 
}

let instance = Class()
let num = (instance as A).bar()  // 1
let str = (instance as B).bar()  // "Hi"    

Swift中常用的原生容器类型有Array、Dictionary、Set,它们都是泛型的,也就是说在一个集合中只能放同一个类型的元素。(OC可以用NSArray放不同类型的元素)

Swift可以考虑使用enum封装(利用enum可以带有值的特性)

代码如下:

enum IntOrString {
    case IntValue(Int)
    case StringValue(String)
}

let mix = [IntOrString.IntValue(1),
IntOrString.StringValue("two"),
IntOrString.IntValue(3)]

mix.map { (obj) -> () in
    switch obj {
    case let .IntValue(i):
        print(i)
    case let .StringValue(str):
        print(str)
    }
}

Lazy修饰符和map、filter这类接受闭包的方法写在一起,让整个行为变成延时进行,在不需要完全运行,可能提前退出的情况下,可以优化性能。

代码如下:

let data = 1...3
let result = data.lazy.map {
    (i: Int) -> Int in
    print("正在处理 \(i)")
    return i * 2
}

print("准备访问结果")
for i in result {
    print("操作后结果为 \(i)")
}

print("操作完毕")

/////// 输出结果
// 准备访问结果
// 正在处理 1
// 操作后结果为 2
// 正在处理 2
// 操作后结果为 4
// 正在处理 3
// 操作后结果为 6
// 操作完毕

Swift内建类型Array和Dictionary都是值类型,也就是在传递和赋值时会进行复制,而Cocoa中的NSMutableArray和NSMutableDictionary是引用类型。因此,在需要处理大量数据并频繁操作(增减)其中元素时,选择NSMutableArray和NSMutableDictionary更好,而对于容器内条目小而容器本身数目多的情况,选择Array和Dictionary更好。


GCD实现延时调用(delay)和取消执行(cancel)

实现代码:

    typealias Task = (cancel: Bool) -> Void
    
    func delay(time: NSTimeInterval, task:()->()) -> Task? {
        func dispatch_later(block:()->()) {
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(time*Double(NSEC_PER_SEC))), dispatch_get_main_queue(), block)
        }
        
        var closure: dispatch_block_t? = task
        var result: Task?
        
        let delayedClosure: Task = {
            cancel in
            if let internalClosure = closure {
                if (cancel == false) {
                    dispatch_async(dispatch_get_main_queue(), internalClosure)
                }
            }
            closure = nil
            result = nil
        }
        result = delayedClosure
        
        dispatch_later { () -> () in
            if let delayedClosure = result {
                delayedClosure(cancel: false)
            }
        }
        return result
    }
    
    func cancel(task:Task?) {
        task?(cancel: true)
    }

调用:

let task = delay(5.0, task: { print("Do sometiong 5 seconds later") }) //延迟5秒调用task
cancel(task) //取消执行task

Swift中如果想对属性进行KVO观察,需要添加dynamic关键字,如果无法修改源码,需要重载相关类。比起改写,更推荐使用swift第三方框架Observable-Swift

“class MyClass: NSObject {
    var date = NSDate()
}

class MyChildClass: MyClass {
    dynamic override var date: NSDate {
        get { return super.date }
        set { super.date = newValue }
    }
}”

Swift实现Associated Object为已有类型添加属性

“class MyClass: NSObject {
    var date = NSDate()
}

class MyChildClass: MyClass {
    dynamic override var date: NSDate {
        get { return super.date }
        set { super.date = newValue }
    }
}”

测试:

func printTitle(input: MyClass) {
    if let title = input.title {
        print("Title:\(title)")
    }
    else {
        print("NO Title")
    }
}

let c = MyClass()
printTitle(c)
c.title = "I'm title"
printTitle(c)

摘录来自: 王巍 (onevcat). “Swifter - 100 个 Swift 必备 Tips (第二版)”。 iBooks.