Flutter开发 Swift语法学习

1,643 阅读4分钟

背景

Flutter开发的时候发现iOS原生部分不会,如果写一个Flutter应用,但是只有Android端,哪这样做的意义是什么? 所以掌握一些iOS开发经验是非常有必要的.

目标: 快速掌握Swift语法特性,目标是支撑Flutter开发.

学习方法

  1. 快速浏览iOS开发.知道怎么导入一个Swift项目,如何依赖,配置开发环境,知道怎么编译,调试.
  2. 因为不需要开发原生UI,先不学习SwiftUI.
  3. 直接阅读真实iOS线上代码,读得懂的地方直接跳过,读不懂的地方,根据问题去找Kotlin/Dart中的对应场景.

知识点

guard let

var str = "Hello, playground"

var reason:String? = ""
func test(){
    guard let _ = reason
    print(str)
}

作用是做空安全,Kotlin没有guard但是可以用?:和申明顶层函数来实现. 顶层函数的方式:


// 申明
inline fun <T> T.guard(func: T.() -> Unit): T {
    if (this == null) {
        func()
    }
    return this
}

// 使用
    var reason: String? = ""

    fun test() {
        val _reason = reason.guard { return }
        Log.d(TAG, "test: ")
    }

?:的方式

    var reason: String? = ""
    fun test2() {
        reason ?: return
        Log.d(TAG, "test2: ")
    }

if let

if let可以做空安全,但是比起guard let会多处理一部分else的逻辑

                    let reason: String
                    if let err_ = error as? SKError {
                        reason = Subscribe.shared.description(for: err_.code)
                    } else {
                        reason = "Network Failed"
                    }

对应koltin if..else..

        val reason: String = if (error is Error) "errortype1" else "errortype2"

闭包

这一部分相对复杂些,参考了这位同学的科普:Swift之你真的知道为什么使用weak吗

内存拷贝 [] in

func delay(_ duration: Int, closure: @escaping () -> Void) {
    let times = DispatchTime.now() + .seconds(duration)
    DispatchQueue.main.asyncAfter(deadline: times) {
        print("开始执行闭包")
        closure()
    }
}

func captureValues() {
    var number = 1

    delay(1) {
        print(number)
    }

    number = 2
}

captureValues()

执行结果:

开始执行闭包

2

原因是因为闭包持有的是内存引用. 为了解决这个问题需要在执行闭包之前做一次内存拷贝.[]的作用就是做内存拷贝.于是上面的代码修改成

func captureValues() {
    var number = 1
    
    delay(1) {[number=number] in
        print(number)
    }
    
    number = 2
}

这个场景在Java中很常见,外部传递一个临时变量给另外一个线程的时候需要申明为final就是这个目的.在Kotlin里面可以这样写:

    val coro = CoroutineScope(Dispatchers.Main);

    fun test() {
        var a = 1
        // 拷贝一份到不可变类型,防止外部再次修改a的值
        val tmp = a
        coro.launch {
            delay(1000)
            Log.d(TAG, "a is ${tmp}")
        }
        a = 2
    }

顺便提下,Kotlin推荐用上诉方式开辟协程,然后用这个协程绑定组件的生命周期. 另外两种方式一个是全局的 一个是阻塞的不适用.

逃逸闭包 @escaping

当一个闭包作为参数传到一个函数中,而这个闭包在函数返回之后才被执行,这个闭包就被称为逃逸闭包. 简单来说就是逃逸闭包在函数返回后才执行. swift规定这种操作必须采用@escaping的固定写法.

func delay(_ duration: Int, closure: @escaping () -> Void) {
    let times = DispatchTime.now() + .seconds(duration)
    DispatchQueue.main.asyncAfter(deadline: times) {
        print("开始执行闭包")
        closure()
    }
    print("方法执行完毕")
}

对应Kotlin

    val coro = CoroutineScope(Dispatchers.Main);
    fun testEscaping() {
        coro.launch(Dispatchers.IO) {
            delay(100)
            Log.d(TAG, "testEscaping: 开始执行网络请求")
        }
        Log.d(TAG, "testEscaping: 开始执行")
    }

延迟求值 @autoclosure

先看这个栗子

func isTrue()->Bool{
    print("求值..")
    return true;
}

func test(isTrue:Bool){
    print("普通函数调用")
    print("isTrue is \(isTrue)")
}

test(isTrue: isTrue())

打印如下

求值..

普通函数调用

isTrue is true

先判断函数参数的值,然后逐步调用函数打印. 如果我想函数func test(isTrue:Bool){的参数求值放在需要的时候做就需要用到@autoclosure

func autoTest(_ closure: @autoclosure () -> Bool) {
    print("autoclosure函数被调用")
    if closure() {
        print(true)
    } else {
        print(false)
    }
}

func isTrue()->Bool{
    print("求值..")
    return true;
}

autoTest(isTrue())

执行结果

autoclosure函数被调用

求值..

true

这样就实现了函数闭包求值的懒加载. 对应Kotlin可以使用lazy属性实现:

    fun autoTest(closure: Lazy<Boolean>) {
        Log.d(TAG, "lazy函数被调用")
        if (closure.value) {
            Log.d(TAG, "autoTest:true")
        } else {
            Log.d(TAG, "autoTest: false")
        }
    }

    fun isTrue(): Boolean {
        Log.d(TAG, "求值..")
        return true;
    }
    
    // 调用
    fun testClosure() {
        autoTest(lazy { isTrue() })
    }

循环引用 weak self

A对象持有B,B对象又持有A的引用叫做循环引用. 在Android中这个场景常见于Handler持有Context,为了解决这个问题使用WeakReference(弱引用),我理解weak self就是在swift里解决这个问题的方式.

class Person {
    var name: String
    lazy var printName: () -> Void = { [weak self] in
        print("\(self?.name)")
    }
    
    init(name: String) {
        self.name = name
    }
    
    deinit {
        print("\(name) 被销毁")
    }
    
    func delay(_ duration: Int, closure: @escaping () -> Void) {
        let times = DispatchTime.now() + .seconds(duration)
        DispatchQueue.main.asyncAfter(deadline: times) {
            print("开始执行闭包")
            closure()
        }
    }
}

let person = Person.init(name: "小明")
person.delay(2, closure: person.printName)

那么Kotlin的栗子:

    fun test() {
        val context = WeakReference(this@MainActivity);
        coro.launch {
            delay(1000)
            context.get()?.mTvMessage?.setText("1秒后调用,1秒内可能发生内存泄露")
        }
    }