从强制解包看 Swift 的设计
从强制解包看 Swift 的设计

不知道大家有没有发现,在一个 Objective-C 和 Swift 混编的 App 中,当把一个 OC 中的参数转到 Swift 时,Swift 会自动把这个变量进行强制解包。举个例子,我在 OC 中定义这样一个变量:
@property (nonatomic, copy) NSString *foo;
它转成 Swift 就变成了这样:
var foo: String!
这样看上去合情合理。Swift 中有 String? 和 String! 两种形式,但 OC 中没有 NSString? 和 NSString! ,当 Swift 无法区分 OC 中的变量是不是 nil 的时候,一律强行转化为非空参数。这样设计体现了 Swift 强安全性语言的特性。
但是这时候问题来了。我们回到上文中的例子,假如 OC 中对 foo的使用如下:
@property (nonatomic, copy) NSString *foo;
- (void)secretFunc {
// 一些诡异复杂的操作
...
self.foo = nil;
}
然后我们在 Swift 中这样调用:
func init() {
objectiveCObject.secretFunc()
}
func calcLen() -> Int {
return objectiveCObject.foo.characters.count
}
上面这段 Swift 代码执行到calcLen()时会崩溃,原因是foo在init()中已经被设成了 nil,而foo在 Swift 中是 foo!。也就是说,因为 Swift 对 OC 变量的强转,导致了程序的崩溃。这是一个很容易忽略的问题,因为强转的时候,Xcode 不会给出任何的警告、报错或是提醒。而我们作为开发者,很容易忽略这样的错误,导致 runtime 的时候直接崩溃。
针对这种情况,我们来讨论下面三个问题。
- Q: 为什么 Swift 要将 OC 中的变量如
foo转为foo!而不是foo?
这是一个有争议的话题。我个人认为强制解包的方式会督促开发者考虑变量是否为 nil 的问题。在 OC 时代,声明变量一般不会考虑是否为空的问题;而在 Swift 时代,因为其是一门强安全性的语言,在变量定义时,必须确定变量是否为空。一般定义为非空有两种以下形式:
// force unwrapping
var foo = "Hello" // implicitly unwrapping
var foo: String!
前者根据初始值强制解包,定义 foo 为非空变量;后者则直接申明 foo 为非空变量。
无论哪种情况,开发者会从一开始就思考处理 nil 时的情况,并在后续开发中一直注意。所以从foo转化为foo!,你就会思考 OC 中代码是否也要处理
nil 的情况;而如果转化为foo?,nil 也无所谓,而实际可能并不是这样,nil 的特殊情况考虑会一直忽略,开发中的隐患一直存在,同时也不符合 Swift 强安全性的设计思路。
- Q: 我就想让 OC 中的变量从
foo转化到 Swift 中变成foo?,有没有办法
请这样在 OC 中定义变量:
// foo -> foo?
@property (nullable, nonatomic, copy) NSString *foo; // bar -> bar!
@property (nonnull, nonatomic, copy) NSString *bar; // qux -> qux!
@property (nonatomic, copy) NSString *qux;
这种事先声明是否为 null 的定义方法,是不是很像 Swift 中的 optional 机制?然而 OC 时代我们几乎不会去管变量是否为 nullable 这件事,由此我们可以体会 OC 和 Swift 在语言设计思路上的差异。
其实nullable和nonnull是 Swift 出来之后才引入 OC 的。所以一开始,OC 中的变量默认都是nullable,转变到 Swift 中,应该就是?。但是这样转化代价太大,我们所有变量都要在 Swift 中用if else或者guard来解包。所以为了写起来方便,Swift 干脆直接强转,故而现在这个机制也是一个历史遗留问题。
- Q: Swift 如此这般导致混编 App 崩溃,没有提示的情况下程序员必须细细检查 nil 导致的 bug,这样设计强制解包的代价是否有点大?
这个 bug 在混编 App 中很容易出现,没有警告确实带来很大困扰。实际上这个问题早就在苹果的开发者论坛上被提出,Swift 组自己也开了个 ticket 要修,可惜最后不了了之。Github 上有人开发出了第三方的工具来解决这个问题。
我个人觉得这个问题苹果不重视的原因在于 Swift 和 OC 混编只是一个暂时的局面。Swift 取代 OC 是一个时间问题,所以解决混编中的问题都显得没有多大意义,在苹果内部也一直是低优先级。毕竟现在所有精力应该放在 Swift 上,随着时间的推移和 OC 的淡出,这个问题也将微不足道。
从强制解包看 Swift 的设计的更多相关文章
- 强制解包看 Swift 的设计
不知道大家有没有发现,在一个 Objective-C 和 Swift 混编的 App 中,当把一个 OC 中的参数转到 Swift 时,Swift 会自动把这个变量进行强制解包.举个例子,我在 OC ...
- Swift中的Optional类型 (可选类型)与强制解包 ? !
我们在swift的开发中会经常遇见?和! ,理解这两个符号深层次的内容对我们的开发是相当有利的: 目前网上对swift3.0的教程还相当的少,如果去搜索会发现早期的说法,在定义变量的时候,swift是 ...
- 关于Python 解包,你需要知道的一切
解包在英文里叫做 Unpacking,就是将容器里面的元素逐个取出来(防杠精:此处描述并不严谨,因为容器中的元素并没有发生改变)放在其它地方,好比你老婆去菜市场买了一袋苹果回来分别发给家里的每个成员, ...
- Python中的可变、不可变对象和赋值技巧序列解包
可变对象和不可变对象 在python中一切皆对象.在Python中不存在所谓的值传递调用,一切传递都是对象的引用,也可认为是传址. python中,对象分为可变(mutable)和不可变(immuta ...
- php归档格式:phar文件详解(创建、使用、解包还原提取)
转载一篇,突然遇到一个冷知识,phar的东东,貌似和jar.war是一种鬼. 重点使用一下下面这个东东,就能解包出来东东了. $phar = new Phar('lib/yunke.phar', 0) ...
- apk 解包 打包
APK应用程序的解包.修改.编辑.汉化.打包及应用 前两讲主要讲玩机的最基本的知识,集中在如何刷机.本讲是进级的内容,来谈谈与apk应用程序有关的知识,内容包括akp文件的解包.打包.反编辑.解析.汉 ...
- Python中的参数解包:`*`表达式和 `**`表达式
目录 1.参数解包:方法调用中的*表达式和**表达式 2.参数解包:方法定义中的*表达式和**表达式 3.在元组,列表,集合和字典中解包 4.Extended Unpacking:赋值表达式左边的*表 ...
- RocketMQ详解(四)核心设计原理
专题目录 RocketMQ详解(一)原理概览 RocketMQ详解(二)安装使用详解 RocketMQ详解(三)启动运行原理 RocketMQ详解(四)核心设计原理 RocketMQ详解(五)总结提高 ...
- MonoTouch 二三事(三)mono mkbundle 打包程序的解包支持
2014.10.06 更新 编写了 IDA Pro 的插件,用来解包和打包 mkbundle程序,请参见 https://github.com/binsys/MKBundleManager 许久以后, ...
随机推荐
- Linux下的二进制兼容性的检测
都知道Linux的二进制兼容与windows相比简直是天差地别,windows的二进制兼容做得特别好,很老的游戏都能在windows 10上跑,写的好的程序,在XP上也不是不可以.但是Linux就不一 ...
- 【图像处理】openCV光流法追踪运动物体
openCV光流法追踪运动物体 email:chentravelling@163.com 一.光流简单介绍 摘自:zouxy09 光流的概念是Gibson在1950年首先提出来的.它是空间运动物体在观 ...
- cmd下查看端口被某程序占用命令
经常,我们在启动应用的时候发现系统需要的端口被别的程序占用,如何知道谁占有了我们需要的端口,很多人都比较头疼,下面就介绍一种非常简单的方法,希望对大家有用 假如我们需要确定谁占用了我们的9050端口 ...
- CocoaAsyncSocket UDP发送数据超过包大小限制(Message too long)
最近在做iOS上,基于UDP传输音视频时遇到的一个问题,这边纪录一下: 由于考虑实时性比较高,所以采用了 CocoaAsyncSocket 的UDP框架来实现,将视频切割成一帧帧的图片发给服务端,不过 ...
- 屏蔽alert弹框下面一层的操作
需求: 给alert框戴个套. 屏蔽下层页面的操作. 搞这个花里胡哨的东西. 还一baidu全都是长得一样的答案. 神魔恋. /** * Tip Message像alert一样 */ function ...
- Python中的两种路径
Java中有两种路径,一种是操作系统的路径path,另一种是类路径classpath. Python中也是如此,一种是操作系统环境变量中的path,另一种是PYTHONPATH. 当import xx ...
- 升级cocoapods1.1.1版本
先切换gem源 gem sources --remove https://rubygems.org/ gem source -a https://gems.ruby-china.org 查看是否切换成 ...
- 使用Cordova搭建Andoid和iOS开发环境
最近在了解cordova ,下面的分享出来 大家可以看看, 我 有空也按照这个写写demo 1.下载node.js,进行安装 https://nodejs.org/en/ 2.安装cordova ...
- mvc 权限验证
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.We ...
- 恢复Ext3下被删除的文件
下面是这个教程将教你如何在Ext3的文件系统中恢复被rm掉的文件. 假设我们有一个文件名叫 ‘test.txt’ $ls -il test.txt 15 -rw-rw-r– 2 root root ...