Slug ben sandofsky cover cn

是时候用 Swift 了吗?

开发者们面对新技术的态度就如同飞蛾扑火一般,纵使会吃很多亏,但也无法阻挡他们的步伐。这往往会带来很严峻的问题。让我们静下心来,以客观、公正的态度来对待 Swift,就如同其他新技术一样。您应当如何评估采取新技术所带来的风险?采取新技术前您应当观望多久?您如何让整个团队适应这个新技术?Ben Sandofsky 列举了使用 Swift 来替代 Objective-C 的风险和回报,并且回答一个重要的问题:是时候用 Swift 了吗?


如果您在一家小型企业的话,我在此所公开的相关标准,可能会帮助您加快开发进度;当然,我的意见对您可能并不适用,但是我希望总有一天它会对您有所帮助的。

这一幕似曾相识… (00:32)

今天,我们将要讨论来自 Apple 强力推出的一个全新的、激动人心的运行时机制,能够帮助开发者们使代码运行速度更快、并且更安全。

1998 年引入的运行时:Steve Jobs 谈论了 OS X 是如何通过使用这个令人激动的全新运行时机制,从而成为最好的操作系统的。他们保证这将是运行您 Java 应用的最快方式。

2008 年作为 Cocoa 开发的未来所引入的运行时:MacRuby。这是基于 LLVM 所研发的,通过无缝桥接的运行时机制给 Cocoa 开发带来了现代化的最佳实践。

现在,我们的问题来了:是时候用 Swift 了吗?

是时候用 Swift 了吗? (01:04)

Swift 已经到了所谓的“临界点”。如果您看过 TIOBE 编程语言排行榜的话,您会发现 Swift 已经超过了 Objective-C,仅仅次于 Pascal。Ryan Olsen 从 App Store 中下载了最受欢迎的前 100 个应用,大家猜有多少使用了 Swift?11%!

不要轻信网上所言 (02:54)

去年(2014)七月的时候,一些大公司的架构师明确表示,他们对 Swift 将保持观望的态度。这消息的确令人有点沮丧。我为此列了一个 Tweetstorm,直到三个月后才有人来引用我。不过我建议,还是不要轻信网上的信息。您应当基于客观、公正的分析,平衡风险和回报,再来做出您的决策。

最好的情况是怎样的? (03:35)

有这样一种新技术:代码能够实现自举。不过,并没有人准确测量过,某种特定的语言要显著比其他语言更有效率。在多年以前的论文当中,有人针对某个程序,使用了 80 种语言(C、C++、Perl、Python、Rex、TCL 等等)分别进行实现。确实,对于某个简单的示例程序来说,轻量级的语言比那些重量级的语言(Java、C++、C)来说要快;这些轻量级语言在某方面确实很高效,占用的内存更低。

争论语法是没有意义的,三思而后行 (04:48)

所有的脚本语言普适性并不强,它们只能针对某个平台。而重量级语言就更进一步(因为它们不会随意改变代码的可维护性,因此我觉得 Python 比 Perl 要好用)。但是语法其实并不重要。Python、Ruby、Perl,它们所具备的生产力可能是相仿的。但是,生产力的主要决定权在于你,在于你的权衡过程。比如说你需要有一种具有垃圾回收功能、内存管理很少的语言

Receive news and updates from Realm straight to your inbox

因此,您决定是使用 Objective-C 还是使用 Swift 完全取决于您的需求,思路是相同的。您是否还想要自己管理引用计数呢(而不是使用 GC)?我该怎么使用 UIKit 呢?我该怎么在 Core Animation 框架中卸载动画呢(而不是使用计时器,每 60 毫秒触发一次)?语法发生了变化,但是您应用程序的整体复杂性其实是(大致)相同的。Swift 在提高您工作效率的层次上其实并没有想象中的并没有那么高

您此前可能看到过:某某公司重写了某某语言的某某模块,然后它们非常推荐大家使用这个新的模块!这里的关键是:他们已经重写完了他们的应用,因此他们才有底气说这种话。如果您重新回顾您写过的每一款应用的话,这时候即使您已经知道了所有的业务需求,您要做的实际上还是编写一个新应用,只不过需要更为精简而已。的确,正如一位 Lyft 的工程师所说:“如果我们决定重写应用的话,我们实际上需要搭建一个全新的应用”。

总之,如果您试图使用一个新语言重写您的应用的话,这就会变成一个严肃的问题。保证您的新应用和老应用在功能上没有任何的差异将是一个不小的挑战。不过我认为,一次处理一个模块,是最好的解决办法。

让我们来试一试:找出 BUG (07:26)

hashOut.data = hashes + SSL_MD5_DIGEST_LEN;
hashOut.length = SSL_SHA1_DIGEST_LEN;
if ((err = SSLFreeBuffer(&hashCtx)) != 0)
    goto fail;
if ((err = ReadyHash(&SSLHashSHA1, &hashCtx)) != 0)
    goto fail;
if ((err = SSLHashSHA1.update(&hashCtx, &clientRandom)) != 0)
    goto fail;
if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0)
    goto fail;
if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
    goto fail;
    goto fail;
if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0)
    goto fail;

Objective-C 的缺陷:C 是一个有缺陷的语言。如果您对黑科技有所了解的话(例如,switch 语句有用默认的 fallthrough 行为),你可以围绕它做一些神奇的玩意儿。但是,Apple 并不能对 C 进行修改以让其更为安全;因为它们必须要保证与 C 的向后兼容性。

@implementation NSString (Helper)
- (BOOL)containsString:(NSString *) string {
    NSRange range = [self rangeOfString:string options:NSCaseInsensitiveSearch];
    return range.location != NSNotFound;
}
@end

在 iOS 8 中,Foundation 中加入了一个名为 containsString: 的方法。有时我们需要让 Foundation 中拥有不区分大小写的字符串检索功能,而系统提供的方法是区分大小写的。使用 Objective-C 进行扩展的话,你需要给您的类别加上您公司的前缀才能使用。Swift 由于使用了命名空间,因此一般情况下,用 Swift 写代码更简单、更安全、BUG 更少、更快捷,并且这些功能是内置在语言本身的。即使您只是简单将 Swif 桥接到 Objective-C 当中,这些功能都可用。

最坏的情况又是怎样的? (10:14)

1. 添加 Swift 支持将会增加包大小 (10:21)

请一定要确保您支持 Bitcode 功能,还有确保最终打包的二进制包包含了 Bitcode (你必须要导出包,查看其中的某些文件夹以确保成功提供了支持)。

对于一个简单的 Hello World 应用,Objective-C 版本的应用大小在 72K 到 92K 之间,而 Swift 版本的应用大小在 3.1M 到 4.6M 之间。不过这并不是一个大问题(例如,如果您的应用程序已经是 45M 大了,那么增加这么点额外空间并不是影响很大)。不过要记住,在那些发展中国家当中,下载的成本是非常高的

2. 新规范意味着限制 (11:52)

如果您更关注使用 Swift 的新规范的话,您可能会遭遇不少的限制。例如,使用 struct 来替代类,这是一个新的规范。当你想向其中引入 NSCoding 协议的时候,你会得到一个错误:non-class type 'Post' cannot conform to class protocol 'NSCoding’(非类类型的 ‘Post’ 不能够实现类协议 ‘NSCoding’)。我建议如果您打算使用 Swift 的话,放心大胆地使用结构体吧,不要害怕去使用你不清楚的东西(出错了再改就好)。

3. 新的运行时意味着新的 BUG (13:18)

Mike Ash 发现了关于弱引用的一个 BUG。若引用有一个奇怪的条件,可能会触发您应用的神秘崩溃。另外,我向一个内部的数据结构中传递了一些数据,但是实际上调试版本和发行版本的行为是不一样的,最后导致其在启动时崩溃了。

4. Swift 改变的太快了 (14:23)

当然,语言的演变是好事,但是最终都会导致代码的流失(code churn)。可汗学院有一个 30,000 代码的应用。他们花费了一周时间将代码迁移到新的 Swift 2 语法中。一个星期,你实际上可以向用户提供更有价值的功能,而不是在改语法上。

Apple 的 Tyler Fox 承认,Swift 3.0 将会有重大的修改。我们知道,今年将会有更多的 Swift 的改变和变化,因此大家至少需要预算至少两周的时间,浪费在新的 Swift 语法迁移和测试稳定性上。这就是演变所带来的代价。

代码流失使得重建依赖变得痛苦 (15:17)

语言版本和 Xcode 版本捆绑在一起所带来的副作用是,这会导致您掉入到依赖重建的坑当中。当 DropBox 将它们的 2.0 SDK 换用 Swift 实现的时候,人们就不得不将他们的 Xcode 版本也换为最新的版本。因此,这会导致应用进入“维护”模式。比如说去年,我被硅谷的一家电视节目聘为了技术顾问,我写了一个小小的应用,我使用的是 Swift 1.2,而现在由于我 Xcode 版本的限制。我不能随意回滚回去,我必须要证明我将这个版本从 1.2 迁移到 2.0 所花费的一个星期没有任何浪费。

迁移导致协调成本增高 (17:07)

如果您在大公司工作(Facebook、Twitter),很可能团队之间也是独立工作的。您需要与您公司中的所有分团队进行沟通协调。或者说,您的主应用中有两个不同的团队在负责(例如,一个团队负责构建基础框架和网络协议栈,另一个团队负责构建面向用户的功能),您在协调共同上将会花费大量的时间和成本。

所以我的建议是什么? (17:56)

如果您在一个小团队当中的话,由于协调成本很低,因此您完全不必被要花费两周时间来迁移代码这个消息给吓坏,直接上就行。如果您对 Objective-C 很擅长,或者应用依赖很复杂,亦或者是忙不过来的话,那么我还是建议您等待 Swift 3.0

Swift 3.0 之后,他们的重点将放在 ABI 稳定性上(您可以轻松地使用不用版本的 Swift 库;它可能会嵌入到 iOS 系统当中,因此这可以减小您应用的大小)。在 Swift 3.0 中,看看 Foundation 和 UIKit 框架中将会有些什么新东西是一件很有意思的事情。我们非常期待框架的更新,这样我们就可以拥有更多的经验。他们将会深入到框架内部,尝试用结构体、枚举来替换那些遗留产物。你会发现有新的模式来取代 NSNotificationCenter。随着 Swift 3.0 的发布,我们都将拥有更合适的经验。

谢谢大家!问题时间到! (20:00)

问:Apple 是不是计划在未来的某一时刻,淘汰掉 Objective-C 呢?最近,某些库当中所返回的数据类型是 Swift 的,而不是 Objective-C 的。您知道 Objective-C 会在什么时候被淘汰掉么?

Ben:(我没有在 Apple 工作,因此我可以畅所欲言)关于此曾经有个采访,Apple 的工作人员说他们很喜欢 OS X Carbon 中的 Objective-C API。我认为他们应该还是会继续支持 Objective-C 的(虽然他们总是一直告诉开发者不要继续使用了)。他们只是不赞成使用 Objective-C 而已,但是他们并不会将其移除。我认为为 Objective-C 应该会一直支持的。这些 API 应该会一直存在,直到 Objective-C 跟不上时代的发展,没有任何使用意义的时候,才可能会被抛弃。这就是为什么我觉得太过关心不赞成的 API 是毫无意义的,我对此完全没有任何压力。

问:希望这美好的想法不会被打破!

Ben:这个问题实际上 iOS 也有。我曾听说过一个传闻:Apple 是那种,东西一旦建好,就再也不会改它,这种类型的公司,因为它已经可以用了!OS X 仍然还有 Carbon 的踪影,因为 Carbon 仍然可以用。如果他们抛弃了 Objective-C,那么 iOS 应该是首当其冲的。Apple 最重要的目的是向后兼容,因此这么做很可能会损害他们维护的 UIKit 的向后兼容性。因此,我一点也不担心 Objective-C 会被抛弃。

问:有没有某种切实可行的方法,能够将 Objective-C 应用一块块地转换为 Swift,而不是重写呢?

Ben:我认为他们在 Swift 中做的最美妙,也是最聪明的事情是:你可以混编 Objective-C 和 Swift,这样你可以一个文件文件的来修改。如果有人拿着一款运行良好、完美的 MVC 机构的应用来找我,我会说:先把现有的代码放一边,剩余的新工作用 Swift 就可以了,确保注释完善、桥接配置良好即可。如果代码可以正常工作的话,那么最重要的部分就是提出一个完善的过渡计划了。你应该强硬地认定:“从这里开始,除非有特殊的理由,否则你都应该全部使用 Swift 来写应用”。

例如,我曾经在许多应用中看到不同的解决方法,某个大团队中的一些开发者决定摒弃原有的单元测试风格的 TDD 开发方式,转用行为驱动开发。他们开始了将他们的测试迁移到 BDD 框架中(不过由于他们并没有太多时间,到现在这项工作还未完成)。最糟糕的状况是新的开发者当中,一部分写的是 Objective-C,而另一部分写的是 Swift,他们没有明确的方向。这对构建系统的资源安排来说是非常不好的,并且在使用 BDD 系统的情况下,当他们开始搭建持续集成服务器的时候,所有的管理员都必须要搭建两套不同的服务器,以便解析两种不同的报告,这会导致事倍功半。因此,重要的是您需要有一个迁移计划,说明从现在开始我们只能使用 Swift,不能够浪费时间回归到 Objective-C 中去寻求代码整洁。

问:我在一个拥有庞大 Objective-C 代码库的项目中工作,我觉得现在还不是迁移到 Swift 的好时机。不过有一件事我开始做的就是,当我创建新的类的时候,我都确保我的所有指针都使用了 nonnull 以及 nullable 关键字。这是不是一个好的策略呢,可以帮助最后彻底迁移到 Swift?

Ben:当然,Swift 代表的是未来,就像你的代码开始看起来像 C++ 一样。如果我只是独自一人,为自己的应用工作的话,那么我可以随意放弃旧的代码,不过一般而言我会懒得这么做。但是,如果你准备看向未来的话,那么我可以保证现在是一个里程碑。

问:您此前提到可从 Objective-C 迁移到 Swift 的计划,是否还有其他看起来与之类似的迁移计划呢?就是您是否在其他团队,或者其他应用,或者整个组织当中,看到他们成功完成了迁移呢?回顾 35 年的软件开发历史,是否还有其他的相关例子呢。这种迁移已经不是第一次发生了。谁做的最好,谁做的最差呢?

Ben:这可能有点超出我的知识范围了,不过我觉得从 Carbon 迁移到 OS X 应该是一个不错的例子。还有几年前,从手动管理引用计数 (MRC) 过渡到自动引用计数 (ARC)。我坚持观望一段时间后再迁移到 ARC 当中,但是我们团队中有一个强烈的意愿表示:“我们在 Apple 中就用着这个了,它能够减少崩溃。”这不是让一个特定的工程师回去,改变所有权模式(在一个房间里面花费几周时间,然后一点点修改以让其能够运行)就好了的。我们需要一个平滑的迁移操作,因此我们一个文件一个文件的来,并且这种特殊的所有权模式中的特定元素确实非常烦人,我们就只让这个文件脱离 MRC 即可,而不是浪费一个星期来调试以让其完美运行。

不幸的是,我能想到最接近的例子就是当工程师们选择了一个非 Apple 的 iOS 框架的时候,一旦框架发生改变,它们就需要从头开始重写。这非常痛苦。我会说最好的办法就是进行规划,一点一点来,并且不能够丧失激情。如果您打算进行完全的重写的话,那么要改变的不是语言,而是架构,你要将通用的 MVC 换用为公司中更深层次的结构。在我进行 iOS 开发之前,我曾经遇到过一次重写任务,我们将最好的开发者们放在一个房间里写了好几个月,他们对应用进行了完全的重构。但是当他们开始加入更多的开发者之后,由于代码审查的缺失,导致了相同的问题(架构混乱),这就导致了恶性循环的产生,这是我们应该要避免的。

问:关于开发新应用我有个问题:您是否会直接使用 Swift 进行开发呢?我指的是一个全新的应用,一切都是从头开始的那种。

Ben:对我来说,那么肯定就是一个选择:使用 Swift!这样很棒,一旦有朝一日这款应用火爆起来之后,也就是 Swift 3.0 出来之后,我或许会将它卖给 Facebook,我觉得可以卖个一百万美元。我曾经和友人讨论过这个,对他们来说 SDK 是战略的重要组成部分,他们觉得使用 Swift 弊大于利。一旦您开始涉及到为公司和投资者负责这一个高度上面的话,我倾向于保守态度;我想让事情变得简单一点,平滑一点,不对他人造成伤害,因为他们给我们公司钱,他们才是上帝。

问:Swift 中使用 NSNotification 会出现问题?

Ben:是的。NSNotificationCenter 需要 NSObject 或者其引用类型:你没法对 Swift 值类型建立观察。我不是说通知不是一个好的 API,我是说为了实现 Swift 值类型的观察,我需要想出一个新的方法来实现。我想说这让我有点心烦。我们非得需要构建自己的通知么,比如说,构建一个注册中心以替代 NSNotificationCenter 么?一旦你开始涉足一个不熟悉的领域,那么你的实际进度就会变得非常非常缓慢,效率低下。但是我没有想到,因为虽然我知道这个特定模式基本是百分百成功的,但是一旦我改变行为方向,那么这个模式就没法工作了……这是一个恶性循环,因此我不得不重塑这些对我来说需要死记硬背的代码。

问:您刚才已经提出了一个关于新产品和新公司的想法了:通知中心的替代品。我觉得您可以成为下一个 Realm。

Ben:借您吉言,希望如此吧!

About the content

This content has been published here with the express permission of the author.

Ben Sandofsky

Ben Sandofsky builds apps, advises startups, and teaches with CodePath. He has shipped software for over a decade, from tiny startups to giant enterprise companies. He spent over four years at Twitter; among other projects, he was tech lead for Twitter for iPhone, iPad, and Mac. Last year he was a technical consultant for HBO’s Silicon Valley.

4 design patterns for a RESTless mobile integration »

close