记录使用Swift的时候遇到的一些奇葩问题。
Swift 踩坑集锦
Optional 的推导问题
1 | var list:[String]? = ["X", "Y"] |
上述代码中dic的Z内存储的值,和value虽然是同一个表达式的计算结果,但是最终的值是不一样的。
Z最终的值为["V": ["X", "Y"]], value为[["V": "X"], ["V": "Y"]]
问题根本原因
在Swift的推导系统中,Z的右值被推导为Optional.map(), value的右值被推导为Array.map()。
这个是Collection的map源码
1 | @inlinable |
这个是Optional的map源码
1 | @inlinable |
可以看到这两个map的函数原型都不一样,Collection.map是返回泛型数组,而Optional.map是返回泛型值。
Collection.map的作用是,对每一个Collection内的元素执行transform闭包,将transform返回的每一个元素merge为最终的数组返回,这个是熟知的Array的map操作。
Optional.map的作用更多的是屏蔽掉Optional对于运算符的影响,避免更多解包的胶水代码出现,官方文档举了个例子:
1 | let possibleNumber: Int? = Int("42") |
Optional.map可以避免Optional类型没有重载*运算符而导致的编译问题。
回过头来分析dic中Z的右值,如果list ?? []被推导为Optional,那么map闭包中的$0就是Optional内的非空值,结果确实就是["V": ["X", "Y"]]。
为什么会出现这个不一致的推导结果?
如果把测试代码中list解包改成这样:
1 | let list_1 = list ?? [] |
或者在定义字典的时候不使用Any, 而是直接明确类型:
1 | var dic:[String : [[String : String]]] = [:] |
dic中Z的值和value就保持一致了。
Optional重载了两个”??”运算符,这两个区别在于”??”右参是否依旧为Optional类型的值。上面的测试代码中右参为[], 很明显是一个数组类型。
这里先推测一下,在左值为Any类型的时候,右值当出现??解包的时候,类型推导系统可能会出错。