写点什么

Swift 和 Objective-C 的运行时编程

2017 年 7 月 10 日

本文要点

  • 运行时编程是 Objective-C 编程人员的重要工具,它提供了一些系统框架的基础。
  • 尽管运行时编程的确移除了不少的样板文本(Boilerplate),使开发人员可以编写更为精简的程序,但它是一把双刃剑,可能会导致软件缺陷难以被发现。
  • Swift 早就提供了强大的工具去解决一些在 Objective-C 中要使用运行时编程的问题,这些工具体现为不同的方法,例如闭包、泛型和协议等。
  • Swift 核心团队早已着手为 Swift 添加强大的动态特性,该工作在 Swift 3 中就打下了基础。
  • Swift 社区正在努力提供强大程序库,方便 Swift 开发人员静态地解决在 Objective-C 中被动态处理的问题。

几个月前,在 Objective-C/Swift 开发人员社区中开展了一场辩论。当时辩论的内容涉及Swift 中动态特性的缺失,以及纯Swift 开发环境所难以复制的运行时编程特性在Objective-C 中的重要地位。

Cocoa 基础设计模式就是一个典型的例子,其中包括了 Responder Chain NSUndoManager KVC/KVO/Bindings Core Data 等。为达到易于编程和高度解耦,这些设计模式深度借鉴了 Objective-C 的动态特性。

辩论不仅局限于 Swift 是否是一种动态语言,其中关注的问题还涉及:对于运行时编程,看上去 Swift 并未提供任何可比的特性;以及看上去 Apple 从未讨论过 Swift 中需要或值得具备哪些特性,以使用 Objective-C 的动态性可解决的问题在 Swift 中同样可以得到解决。

为获取更多的深层信息,InfoQ 访谈了 Chris Eidhof 和 Drew Crawford。Chris Eidhof 是《Functional Swift and Advanced Swift》一书的作者。Drew Crawford 是 Deckset 和 Scenery 的创立者,也是一名 Swift 开发人员,是首个静态链接 Swift/Linux 程序的开发者。

InfoQ:你们能介绍一下动态特性在 Objective-C 和 Cocoa 编程中的重要性吗?

Chris Eidhof:动态编程是一个被过度使用的词,即便仅限于 Objective-C 和 Cocoa 环境。对于部分开发人员,它意味着程序在运行中显示出了动态的行为。而对于另一部分开发人员,它可以指代对 KVO/KVC 的使用,或是迟绑定(Late binding)、子类(Subclassing)、Swizzling、运行时转化(Runtime casting)、动态类型(Dynamic typing)等。基于不同的交谈对象,动态编程还可能是上述技术的一个扩展子集。

在 Cocoa 中论及“动态编程”时,通常是指代运行时编程。在 Objective-C 中,使用运行时已成为大多数框架的重要基础。使用运行时编程我们可以编写更精简的程序,移除大量的样板文本。虽然代码及样板文本的精简是件好事,但是这也具有相应的风险,即在依赖于语言的特性时很难静态地发现缺陷。缺陷只有在运行时才会被发现。

不同于 Cocoa,UIKit 早已抛弃了运行时编程。绑定不再存在,并且与 Cocoa 相比,KVO/KVC 也不再重要了。Swift 在这个方向走得更远一些。

Drew Crawford:动态特性在运行时改变程序的行为,例如鸭子类型(Duck-typing,转化相似命名的方法所实现的无关类型)、在运行时创建新类、Swizzling 方法(在运行时改变函数)、选择器(在运行时确定所调用的函数)等。

Cocoa 是围绕着诸如此类的特性而设计的,其基本原则之一是假定存在着这些特性。Cocoa 的“目标 - 行为”模式的关键是选择器,而字符串是 NSCoding 的关键所在。过去的数年中,这些动态模式已深深地融入到了 Cocoa 结构中的数百个细微之处。

InfoQ:Swift 在哪些方面存在短板?哪些问题易于用 Objective-C 解决而难以用 Swift 实现?

Chris Eidhof: Swift 中的“反射”特性(Reflection)仍然十分不成熟,部分运行时特性依然完全不可用。例如,在 Objective-C 中可以用“键值观察者”(Key-Value Observing ,KVO)发现对象属性。在 Swift 中,KVO 只能与 Objective-C 对象一起使用,而不能与结构一起使用。

Swift 中不少事情难以用面向运行时的方法实现。在舍弃这些运行时特性时,最难做到的是学会如何做另类思考。其实 Swift 可用更少的代码解决几乎所有的问题,我们需要的是另辟蹊径。不使用运行时编程,我们还可以使用闭包、泛型、协议等特性,用更短的代码、更安全的方式解决同样的问题。例如,在线视频教学 Swift Talk 的“ Networking ”一节展示了如何安全地使用泛型为 Web 服务提供一个十分动态的接口。

Drew Crawford: XCTest 就是一个的典型例子。它是 Xcode 的测试框架,通过运行时搜索所有以“test”开始的函数,发现你所编写的新测试。如果没有该特性,为让 XCTest 知道如何调用你的测试,必须要手工维护所有测试的列表。现实中列表中的测试很容易被遗漏,这样遗漏的测试就得不到运行,导致你认为已测试的代码事实上并未得到测试。

Swizzling 是应对软件提供商缺陷的常用方法(可能会一些人指出这种用法是不对的)。当闭源软件库存在问题时,项目往往会深陷其中。通过在运行时围绕问题构建定制的环境,你可以嵌入自己的代码去应对这样的问题。该做法的问题在于,在软件提供商做更新后,程序很容易被破坏,因此这种应对方法是一把双刃剑。

InfoQ:你们是否设想过一种更为动态的 Swift?是否能在语言或架构中添加一定程度上的动态?Swift 是否应该做到完全动态?

Chris Eidhof:动态分发和消息传递是早就可用的特性,它们借助了类或协议的使用。或许更多地支持对运行时编程会是十分有用的,但可能要在安全和性能上付出代价。就我个人而言,自 Swift 公开发布以来我就在全职地编写 Swift 代码,我至始至终地运用着运行时编程特性。

有别于依赖运行时编程,我采用了其它所有允许的动态行为特性,包括闭包、高阶函数、协议、泛型等。使用这些特性让我具备了动态的行为,但依然用编译器做静态检查。在我们所著的《 Advanced Swift 》一书中展示了所有这些在实践中用到的技术。

我在近期的一个博客帖子中介绍了我们如何替换运行时编程(正是它使得 Objective-C 动态)为函数(正是它使得 Swift 动态)。我通过仅仅使用函数就重新实现了 NSSortDescriptor API

Drew Crawford: Swfit 已具有了不少的动态特性。iOS/macOS 环境自带完全可用的 Objective-C 运行时,你可将 Swift 代码成功地关联到运行时,在 Swift 中做动态发布、选择器和 Swizzling。

而最大的问题在于如何在其它的平台上实现同样的事情,例如 Linux 等这些不具有 Objective-C 传统的平台。我们打算让越来越多的程序可在各大平台间移植,而开发人员在写新代码时坚持这些可移植的技术是些很有压力的事。这个问题在很大程度上并未得到解决。

但是我认为必须率先考虑 Swift 存在的合理性。Swift 并非是具有现代语法的 Objective-C。如果是这样的话,那么 Swift 项目岂非轻易就可实现(该语言的发展历史中,在很多点上的确是这么做的)。如果你正致力于此,那么只能说你并不需要一种新的语言、新的运行时、新的编译器、新的基金会。

相反,Swift 做出了这样的一个结论,即 Objective-C 所走的路并非我们想要的。我们想要的语言并非来自于接下来十年中每一届 WWDC 大会中将会给出的增量改进,唯一的实现方法是去重新审视那些十分基础的想法,去大量地重写代码,去杀掉我们曾心爱的代码。

我的意思并非是要将动态特性作为众矢之的,但我的确认为应抛开“如果 Objective-C 做了什么那么 Swift 也应做到”的这种理念。喜欢 Objective-C 的开发人员就继续使用它吧!Swift 需要规划自身的发展蓝图。它需要了解历史,也需要从中学习,这意味着以 Swift 要以自己的方式做事。

InfoQ:实现更具动态特性的 Swift 的挑战有哪些?在不降低 Swift 安全性的条件下,如何解决这些挑战?

Chris Eidhof:我并不认为会丧失过多的特性,但我还是要指出两点。第一,为反射添加更多的支持将会是十分有帮助的,但这可能会成为双刃剑,它也易于导致编写不正确的代码。

第二,也是更为重要的一点,我切实地期待着协议中的条件符合特性,它将使开发人员更具有表达力。例如,数组一般不遵从 Equatable,原因在于这要求数组中的所有元素都满足 Equatable。以前该条件在语言中是无法表述的,但是现在 Swift 团队正优先考虑添加条件符合

条件符合还将允许开发人员做数据类型泛型编程(Datatype-generic Programming),这是一种已经被 Haskell 等语言使用的方法。数据类型泛型编程是一种类型安全的编程方法,执行开发人员数据结构上的操作(类似于运行时编程)。

Drew Crawford:性能和安全性是困扰 Objective-C 的两个主要问题。很多人并不清楚,这两个问题是由 Objective-C 的动态直接导致的。

在 Objective-C 这样高度动态的语言中,编程人员的权力大到异乎寻常。他可以打开核心系统库并置入自己的代码。可以钩到方法调用中的每一个组成环节,创建具有无穷个方法的对象,或是创建在对象生命周期中反复闪现的方法。这样的权利真是匪夷所思呀。

但是能力越大,责任也就越大。作为编程人员你所具有的权力越大,留给编译器的权力就相应地减少了。这时就像是给 Objective-C 编译器戴上遮眼罩,将你的代码“看成”类似于在数组上的一个基本 for 循环。但是我们如何才能知道你并未将 NSArray 替换为具有无穷个方法的另一些对象?我们怎么能知道数组并非程序生成的并具有不限量元素?这些问题听上去十分疯狂,但是在 Objective-C 中是“合法上路”的,无法避免会有编程人员这么做。除非编译器屈从于你所编写的代码,否则它几乎无法在你的程序中前进一行,它正是按你代码的编写方式工作。而你编写的代码不可能总是很快的或是非常安全的。

Swift 收回了对编程人员具有无限权力的许可。在此交易中,我们得到了摘掉遮眼罩的编译器。它可以更深入地审视你的代码,通常会具有整个程序的鸟瞰图。这使它成功地实现了程度惊人的优化,并且可对远距离组件间交互等过程中的细微缺陷进行报警。一旦所能做的不再仅限于语法和括号风格之类的事情,就更能吸引人们去使用 Swift 了。它决定了 Swift 语言的特性。

其中所存在的难题是,我们如何在支持 Objective-C 开发人员所需特性的同时,维持 Swift 开发人员所期望的速度和安全性。事实上就我们自身而言是完全办不到的,但是如果群策群力,我们就能接近目标的达成。

InfoQ:为使 Swift 更适合于解决上述的关注问题,你们是否了解 Swift 开发中已经做了或是正在做哪些工作?

Chris Eidhof:是的,几乎所有讨论都是发生于邮件列表中的,Github 的 Swift-evolution 代码库也是一个非常好的资源。如果觉得其中的内容过于庞杂,可以去关注一个称为“ Swift Weekly Brief ”的每周简报。

Drew Crawford: 举一个实例,就是在 Swift 3 中所引入的“_typeByName”。它对应于 Objective-C 中的“NSClassFromString”解决方案,允许动态构建类。

你可能会对以下划线为开始命名的 API 设计心存疑虑,害怕它们在设计时并未被完全地考虑清楚,将来可能会发生改变。

InfoQ:Swift 的目标在于具备处理广泛问题的能力,包括服务器端开发和系统编程。从整体上看,解决这些问题对于 Swift 的重要性如何?你们是否认为 Swift 开发团队在听取意见?

Chris Eidhof: Swift 团队确实正在很好地听取意见。该团队活跃于所有的 Swift 邮件列表中,并深入参与了社区。Swift 是一种雄心勃勃的语言,我认为 Swift 团队的确发现了雄心和实用主义间的最有效点。但是,听取意见并不意味着要把每个可能的特性添加到 Swift 中。我印象十分深刻的是,Swift 团队经常是后退一步,尽量去理解问题真正所在,而非只是实现“增加一个特性”。

Drew Crawford:除了动态问题,Swift 现在还面对着很多挑战,例如核心函数的语法和命名还非常不稳定、不具备 ABI 兼容性、泛型没有完成等。这些问题十分严重,就像一栋建筑正在燃烧。在我看来,事实上核心团队十分关注这些问题。

在得到实现动态特性的建议后,团队从来没有说过要“不做”。团队的答复是:动态问题是一个重要的问题,但是在被其它的问题所干扰的情况下,该问题远非在当下所能得到解决的。我与 Objective-C 开发人员感同身受,他们苦恼于没有更好的动态解决方案,必须又得年复一年地忍受该问题。但是我认为如果连续每年都有超过大半的标准库被重命名,他们会更为苦恼。

应注意到,Apple 所维护 Objective-C 代码多于世界上任何其它人。Core Data 自身所暗藏的解决动态问题的高招可能要多于其它所有的生态系统的总和。因此认为看上去 Apple 对动态问题一无所知是一种误导,Apple 比其它任何人都更好地了解该问题,包括动态的优点和代价。

但是我认为还应该去做些事情。Swift Way™就是首要以静态方式去考虑解决那些在 Objective-C 中被动态解决的问题,并在其它的方法不成功时引入一些动态特性。如果某些程序更适合用 Objective-C 表示,那么我们就需要与这些程序和平共处,因为我们中的很多人也十分清楚,同样会有不少程序更适合于用 Swift 表示。正是这些差异赋予了一种语言其品质和特性,我们需要允许各种语言去做最好的自己,找到适合语言自身的发展道路。

Swift 的发展道路依然是一个十分开放的问题。我很有信心 Swift 将会包括比任何现有语言还要多的动态特性。本书的故事依然在发展,鉴于社区正在完成第三章,我认为现在对这本书如何结尾做出定论还为时尚早。

结论

在本文中,我们探讨了一些关于 Swift 缺失动态特性的问题,这些问题是 Objective-C 编程人员所关注的。我们访谈的两位开发人员深入地参与了推进 Swift 语言及其软件库生态系统的过程,这将有助于我们理解问题的发生场景,以及最终 Swift 将如何解决这些问题。

关于被访者

Drew Crawford是一位软件开发人员、作家和顾问。他编写了首个静态链接的 Swift/Linux 程序。除了编写 Swift 程序,他还在 Austin 运作了一家精品开发公司,为不同规模的企业编写 iOS 应用和服务器软件,并授权定制的 Swift 技术。他所著的《Why Mobile Web Apps are Slow》一书被广为阅读,撰写的文章也已被翻译为多种语言,并被指定为全球多所大学的移动开发教学中的指定读物。

Chris Eidhof是在柏林的一位 Swift 开发人员。他创立了 objc.io 和一系列的会议,并是图书《Functional Swift》和《Advanced Swift》的作者,同时还是 Deckset 和 Scenery 的创立者。在丰富的业余时间中,他喜欢跑步。

查看英文原文: Swift and Objective-C Runtime Programming


感谢冬雨对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们。

2017 年 7 月 10 日 17:492158
用户头像

发布了 226 篇内容, 共 59.5 次阅读, 收获喜欢 14 次。

关注

评论

发布
暂无评论
  • Swift 正在蚕食 Objective-C 的市场

    在过去的几个月里,Objective-C每个月丢失大约1%的市场份额。如果这一趋势继续下去,那么Objective-C将会在年底前跌出TIOBE排行榜的前十。Tiobe将这一现象的原因归结为苹果去年宣布用Swift取代Objective-C。InfoWorld编辑Paul Krill对此进行了报道。

  • 拥抱 Swift!优酷 Mac 迁移 Swift 实践

    本文选自《阿里文娱技术精选系列:超级APP背后的移动端技术大揭秘》

  • Swift 这一年:打消疑虑 站稳脚跟

    在WWDC 2015大会即将召开之际,InfoWord发文回顾了Swift一年来的变化。Swift迅速发展,在Tiobe、PyPL及RedMonk三大编程语言排行榜上均上升到了不错的位次。目前,Swift分列Tiobe和PyPL排行榜的18位和11位,并且保持着良好的上升势头。在GitHub上搜索Swift,可以找到超过13000个条目。人们曾经担心的性能现在似乎也已经不是问题了。

  • 给 Objective-C 的“悼词”

    Aaron Hillegass是知名的Objective-C开发者和布道者,他于90年代在NeXT公司工作,后来专注技术布道,是Big Nerd Ranch的创始人兼CEO。他撰写了多本Objective-C书籍,在苹果发布Swift后,目前他开始将布道重心转向Swift。在6月8日举办的AltConf2015上,他向与会者做了《给Objective-C的悼词》的演讲,回顾了Objective-C的发展和它的影响,但在最后他话锋一转,说明了为什么Objective-C没有真正死亡。

  • Chris Lattner 谈 Swift 3 和 Cocoa“重命名”

    近日,在“swift-evolution”邮件列表中,Swift创建者Chris Lattner在一篇博文中概述了Swift 3定义的一些指导原则,并明确表示该版本会带来破坏性修改。

  • 【基本功】深入剖析 Swift 性能优化

    本文来自美团点评技术文章系列。

  • 从 Observer 到 Observable:使用 Functional Swift 提升复杂 iOS 项目的可维护性

    演讲嘉宾 王文槿,Swift 函数式编程布道者。 内容介绍 iOS发展已经超过十年的时间,已经成长成为一个成熟的软件平台。这意味着绝大多数的iOS项目的迭代时间已经超过了3年,伴随着的不断膨胀的代码,还有各种各样的历史包袱。最严重就是大量的状态、中间层导致迭代新功能所需要的成本越来越高。 本次分享会结合Swift的函数式特性以及FRP的思想,尝试对传统的观察者模式(Listener/Delegate/Callback)和状态机(State Machine)进行改进,来实现更好的状态管理,更可控的回调时序以及更安全、鲁棒的编程模式,解放程序员在维护大型复杂项目的心智负担。

    2018 年 9 月 12 日

  • 面向对象编程:怎样才能写出一个“好”的类?

    我从设计思想、实现原则和编码准则这几个角度谈谈我对面向对象编程的看法,以及在C++里应用的一些经验技巧。

    2020 年 5 月 16 日

  • 开篇词 | C++ 这么难,为什么我们还要用 C++?

    不可否认的是,C++ 仍然是一门非常流行且非常具有活力的语言。

    2019 年 11 月 25 日

  • Swifter 之 UnsafePointer、接口和类方法中的 Self、多元组

    本文节选自王巍著《Swifter : 100 个 Swift 开发必备 Tip》中的三个章节UnsafePointer、接口和类方法中的Self、多元组,分享了作者对Swift语言的研究和发现。

  • 工具漫谈:编译、格式化、代码检查、排错各显身手

    用好工具,可以大大提升你的开发效率。

    2020 年 1 月 13 日

  • 苹果发布新的编程语言 Swift

    在6月3日凌晨举行的WWDC 2014大会上,苹果发布了新的编程语言Swift。Swift是一门基于C和Objective-C的编程语言,它被设计用于开发iOS和OS X的应用程序。Swift采用了安全的编程模式并添加了新的主流功能使编程变得更加灵活、简单、有趣。Swift沿用了Objective-C的命名参数和动态对象模型,并提供了对Cocoa和Cocoa Touch框架的支持。另外,Swift采用了与Objective-C一样的编辑和运行环境LLVM,因此它可以兼容Objective-C,开发者也可以在开发过程中无缝切换。

  • Swift 1.0 GM 发布:App Store 欢迎 Swift 应用

    在6月2日的WWDC 2014大会上,苹果发布了新的编程语言Swift。从发布之初,Swift就被给予厚望,号称可以让编程变得更加简单、灵活、有趣。接下来的几个月里,苹果也在不断更新Swift语言,特别是在语法上进行了多次改进。今天Swift迎来了第二个里程碑:Swift 1.0 GM发布。GM是Golden Master的缩写,可以理解为测试版,按照以往经验来看,GM版几乎等同于正式版,不过这次苹果还特意在发布新闻中提到GM版并非final版本,未来Swift还将添加其它更高级的新功能、提升性能以及精简语法。

  • 开源如何加速了 Swift 的崛起

    2015年12月,苹果的Swift编程语言成为了一个开源项目。开源增加了人们对这门语言的兴趣,因为它不再是苹果平台专属的语言。

  • Swift 和 Objective-C 混合编程踩坑指南

    2019 年 11 月 18 日

  • C#开发团队在《Future Focus》系列文章中介绍 C#的下一步发展规划

    C#开发团队中的Charlie Calvert和Mads Torgersen宣布他们将开始一个新的名为《Future Focus》的文章系列,其中将每月对C#最新的开发状况以及发展规划进行介绍。

  • Go FaaSter: Serverless 平台冷启动优化

    演讲嘉宾目前作为腾讯云 Serverless 计算产品的研发Leader,负责 Serverless 平台整体的架构设计。毕业后进入腾讯工作超过十年,是腾讯云从0到1建设的先行者,曾经负责过虚拟机产品、消息队列中间件、域名注册&DNSPod、API 等平台的技术方案落地。近期聚焦在 Serverless 架构中函数冷启动优化、函数高并发以及低时延调用优化、函数与云资源互联互通等核心能力建设。内容介绍众所周知,在 Serverless 领域,绝大多数函数在第一次冷启动的时候需要数秒钟,这样的等待延时会对关键应用有较大的影响。本次分享将基于腾讯 Serverless 云函数平台(Tencent Serverless Cloud Functions,缩写 SCF),介绍提升冷启动性能的措施。从 SCF 的架构出发,从网络、基础设施以及函数的部署等方面来分析影响冷启动延时的关键因素。此外,也会分享为改善冷启动所做的架构措施的调整,例如弹性网络接口的优化,从容器到微虚拟机的迁移,函数代码部署,以及资源复用等方面。最后,介绍腾讯云如何通过弹性伸缩的技术来尽可能避免冷启动的发生。

    2019 年 7 月 26 日

  • C#特性聚焦:动态类型化对象、Duck 类型和多重分配

    随着动态语言和DLR日益增加的重要性,C#也需要能处理动态类型化的对象(Dynamically Typed Objects)。在C# 3中,这需要编写大量的CLR或DLR反射代码。而在C# 4中,只需要使用关键字dynamic即可解决。

  • IBM 终止参与 Swift,这会产生什么影响?

    IBM终止参与Swift,这将对Swift及Swift社区产生什么影响,InfoQ就此采访了IBM的Chris Bailey。

发现更多内容

架构师第二周作业

跨域刀

极客大学架构师训练营

架构师训练营 - 第二周学习总结

清风徐徐

2020-06-13-第二周学习总结

路易斯李李李

设计模式原则小结

L001

架构师训练营第二章总结

吴吴

架构师训练营第二章作业

吴吴

架构师训练营-第二周作业

清风徐徐

单例模式 极客大学架构师训练营 组合模式

架构师0期02周命题作业

喵呜的小哥哥

week2 homework

依赖倒置原则的个人理解

潜默闻雨

隔离原则优化 Cache 类的设计

潜默闻雨

第二周 - 学习总结

molly

极客大学架构师训练营

架构师第二课总结

Dennis

架构师训练营Lesson2-Homework

强哥

极客大学架构师训练营 依赖倒置

架构师实现架构目标的主要手段(第2周学习总结)

李德政

极客大学架构师训练营

架构师训练营第二周总结

15359861984

编程与设计

eazonshaw

架构师训练营 第二周作业

孙有能希

学习心得

蒜泥精英

架构师训练营第二周作业

草原上的奔跑

架构师训练营第二周学习总结

人世间

极客大学架构师训练营

【架构师训练营】week 2 homework

eazonshaw

架构师训练营 - 第二周作业

teslə

第二周总结

跨域刀

极客大学架构师训练营

架构师训练营 第二周【作业】

小K

Python 核心技术与实践 string

Bonaparte

Python string

第二周作业

重新来过

架构师训练营第二周作业

fenix

依赖倒置

wei

架构师训练营-第二周作业

坂田吴奇隆

极客大学架构师训练营

第二周学习总结

Darren

InfoQ 极客传媒开发者生态共创计划线上发布会

InfoQ 极客传媒开发者生态共创计划线上发布会

Swift和Objective-C的运行时编程-InfoQ