背景
Flutter开发的时候发现iOS原生部分不会,如果写一个Flutter应用,但是只有Android端,哪这样做的意义是什么? 所以掌握一些iOS开发经验是非常有必要的.
目标: 快速掌握Swift语法特性,目标是支撑Flutter开发.
学习方法
- 快速浏览iOS开发.知道怎么导入一个Swift项目,如何依赖,配置开发环境,知道怎么编译,调试.
- 因为不需要开发原生UI,先不学习SwiftUI.
- 直接阅读真实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秒内可能发生内存泄露")
}
}