Cocoa之殇

原来我译作名字为“Cocoa之殇”,被编辑改名为“Mattt Thompson:Cocoa之死”,终于能改回原来的名字了

http://www.cocoachina.com/swift/20150107/10858.html

译者注:Mattt从分析Swift标准库的角度出发,分析了Swift带来的一些新的变革和Swift的未来发展方向,并断言,未来Swift肯 定会渐渐替代Cocoa,而Cocoa作为一个过去十分成功的技术,也将渐渐被新时代所抛弃。http://www.cocoachina.com /swift/20150107/10858.html

(原文:”http://nshipster.com/the-death-of-cocoa/The Death of Cocoa” 作者:Mattt Thompson 译者:xiaoying )

------------------------

Cocoa, 作为一个包含大多数工作在Objective-C语言上必要的基本库的集合——比如Foundation、AppKit,和CoreData,已经成为了 Objective-C的事实标准。而Cocoa Touch基本上就是用UIKit更换掉AppKit的Cocoa,而且它也经常用来和Cocoa放在一起,来指代在iOS上的工作的系统框架。

对于我们多数人(开发者)来说,我们之所以构建基于苹果的平台之上的程序,是因为苹果的硬件和软件集成在一起的那种简单、优雅,和它优秀的性能表现。确实,在评价Cocoa的设计和功能上,从来都不缺少褒奖之词。

但是,在使用Swift几个月之后,我觉得Cocoa已经开始失去了它昔日的光环。我们都能看到Swift的流行最终会带来Objective-C的消失,但是Cocoa呢?这可不是第一次苹果抛弃自己的标准库,还记得Carbon吗?译者:真的不知道可以在里找到

Swift在设计上包含了很多现代程序语言的特性,这些特性让用户能写出更安全高效的代码。不过,如果有人将Swift的出现仅仅看做是对编译器工具团队的消遣,也是情有可原的,因为我们很少能看到Swift的优秀特性在传统软件中的应用。

在 刚开始的时候,使Swift能够以简单有效的方法和Objective-C进行互操作是很有战略意义的决定——同时在一定程度上也是必须的。这样做将允许 团队中愿意冒险的工程师,以一种低风险的方式将Swift加入到已经在运行的代码中去,这对于一个新语言的发展和大面积推广是很重要的。但是对于专注于源 文件映射和API 审核(source mapping and API auditing)方面工作的人来说,他们可能会争论说Cocoa已经成为负担(译者:因为这些往往是跟语言特性相关的)。

如果我们要基于 Swift标准库构建一个新的Foundation框架呢?我们会做出什么不同的事情,还有我们如何从过去的错误中吸取教训?讨论这些对于 NSHipster—一个基于对Objective-C和Cocoa的喜好而建立的网站来说,可能是一个奇怪的话题,但却是一个值得探索的课题。

所以为了告别这对苹果开发者来说具有历史性意义的一年,让我们花点时间来看看未来会有那些事情可能会发生。

如果我比别人看得更远,那是因为我站在巨人的肩上。

——艾萨克 牛顿

我们所有的生产力都要归功于标准库。

一个设计良好的标准库不仅提供了最常用的编程结构的实现,同时它们也用一种良好的方式澄清了那些结构的概念。当一个标准库与已存在的(甚至是内部的)约定背道而驰时,事情往往就开始变得糟糕了

例如,NSURLComponents 遵从文档 RFC 3986,其功能和使用方法在这个文档中 有详尽说明。这样不仅使用者在使用这个API的过程中能够优雅而正确的理解术语的含义,就算是新学习这个API的人,只要他们熟悉RFC3986文档,就 可以轻易的开始工作。(另外,相关的文档的编写将会变得多么简单,只要写“RTFM”然后挂一个超链接到这个文档就好了)

标准库就应该实现标准。

当我们说技术变得很直观,通常意思是说他们是很常见的技术。当我们要构建任何新标准库时,都应该建立在IETF、ISO和其它地方定义的标准之上。

基于这个断言,让我们用一些实际的例子看看有哪些Cocoa已经实现了的,Swift标准库可以改进的地方。

数值计算

NSNumber的存在仅仅是作为对integer, float, double, 和 boolean等基本变量的包装。在Swift中则不需要考虑到这点,所以在Swift中没有这样一种结构。

Swift的标准库聪明地将顶级函数、操作符和类型层级进行组合,通过这样的组合在构建数值基本变量上做了非常卓越的工作。(而且额外的在十进制的基础上增加了二进制,八进制和十六进制)。目前对于 Swift标准库特性的抱怨貌似不多,那么我来说下接下来那些应该加进去的特性。

一个合适的NSDecimalNumber的替代。根据Swift的文档,Double有64位,但是NSDecimalNumber可以表示任何可以用(a乘以10的n次方)表示的数,其中a是一个最长为38位的十进制整形变量,n是从-128到127的整形变量。同时,这里提供了一些关于在Swift环境下使用NSDecimalNumber的必要的补充。

复杂数字的支持,例如这里描述的。

简单的生成随机数的本地方法这里举了几个相似的例子。

利用重载来提供一个可以用来执行一个或者多个数值计算的统一接口的方法,例如这里描述的。

基于Playground的一个内建的数学符号框架,比如Euler,可以用来作为一个优雅的教学工具。

字符串处理

字符串的危险在于他们可以编码如此多不同类型的信息,像之前写的一篇文章:

字符串可能是在计算领域最万能的数据类型。它通过符号传输,可以用来编码数值,关联键值对,表示资源路径,表示语言学内容,还可以格式化信息。

尽管如此,NSString可能太过通用了。尽管它在处理Unicode方面特别出色,但是整个API背负了因字符串作为路径而带来的多样化负担。stringByAppendingPathComponent有和它相同的一族API都非常用,但是用来源字符串作URL的不合理使用。

这其中很多都是因为写@"this"(一个字符串常量)要比写[NSURL URLWithString:@"that"](一个构造函数)要简单方便的多得多。不过,在Swift中,随着Swift常量可改变协议的出现,使用字符串构建URLPath得非常简单

在Swift设计中有很多非常明智的决定,其中之一便是内部使用编码独立的Unicode字符,和暴露的“视图”用来指定编码类型。

使用utf-8编码的字符单元集合 (使用 utf8 属性访问)

使用utf-16编码的字符单元集合 (使用 utf16 属性访问)

21位的Unicode编码值的集合,和utf-32编码格式相同 (使用 utf16 属性访问)

当然对于Swift的String也有一些抱怨,其中之一就是有很多功能被隐藏了,就像某些功能隐藏在顶级函数之下一样。多数开发者被训练成在编辑 器中打出点( . )之后,等待IDE代码提示像“count”这样的函数;而不太可能去查阅像countElements这样的顶级函数。(再次提示, 就像在这篇文章里描述的一样,这个情况可以通过Xcode或者Swift本身允许在函数中自动连接隐式和显式的self来解决)

URI, URL, and URN

理想的URL实现应该是NSURLComponents的值型(value-type,也就是struct)实现,如果这样做,以上我们提及的在NSString中路径相关的API都将被取代。这里是一些与此相关的东西。一个清晰的基于RFC 4395的URI机制的实现,能够减轻现在存在于NSURL中的关于文件URL(file://)的多义合并。一个好的基于RFC 2141的URN的实现可以让开发者理解什么是URN,以及对理清URI、URL和URN相互间的关系带来帮助。(这全是举一反三的能力)

数据结构

Swift中的数据类型,从泛型到序列再到集合,所有的一切都很美。在对Array和Dictionary常量的语法糖的运用,和它们背后更耐人寻味的标准库思想之间达到了很好的平衡。

在这些强大的基本数据结构基础之上,就算一个大学计算机科学课程上的学生,都已经可以轻易地构建可用于生产环境的数据结构。在此之上再借助维基百科,利用一个清闲的下午,差不多任何一个人都可以完成这个工作,或者说至少可以差不多完成。

关于复杂数据类型,如果Swift的基本库要提供这些一大堆的数据结构(例如,树,链表,队列/堆栈)的实现的话,我才笑掉大牙哩。但是有一个我认为是例外:集合(Set),这个可以有。

在Foundation框架中有的三种集合类型,分别是NSArrayNSDictionary, and NSSet (还有他们的mutable版本)。在这三个之中,只有Set现在在Swift中还没有。作为一个基本的数据结构,他们在很多用例场景中都会被用到。具体到对于Swift来说,Set是可以解决很多不常遇到的问题的——参考RawOptionSetType.

Nate Cook 构建了一个漂亮而且完整的Set的实现,仅供参考。

日期和时间

日历相关的功能是Cocoa中最古老并且最健壮的特性之一。在其他语言中,日期和时间相关的编程总是会带来恐惧,但是当你使用NSDate和NSCalendar时就完全不会有这种感觉。然而,让人抓狂的是它们却有点难用而且不能扩展。

为了做一些日历的计算工作,比如想获取从今天开始算一整个月后的日期,人们可能会用NSCalendar和NSDateComponents,至少,这是正确的方法,大多数的开发者可能还在用dateWithTimeIntervalSinceNow:放一个不变的秒数硬编码在那里。不幸的是,仅仅提供正确的方法来对于API来说远远不够的,API需要提供一个更易于使用的方法。

NSCalendar还有另外一个缺点(尽管非常小),就是它不允许新日历的加入。对于那些非要在法国共和历上稍加改动的人来说,这是非常讨厌的。

幸运的是,这些问题都可以使用Swift中的新的特性优雅地解决掉。这可能会耗费一些时间,但是一个基于范型的日历系统真的非常了不起。如果有人想中这个方面挑战我,这里是一些想法

数据交换格式

在 Objective-C中,很长时间之后(iOS5/ OS X Lion!)才有了对于JSON格式的操作的标准方法,这着实非常令人惊讶。JSON已经是在web service中最流行的数据交换格式,而那些想要在iOS中使用JSON的开发者被迫要从少数几个彼此互不兼容的第三方库中做出选择。

然而,现在我们又碰到了这样的问题,NSJSONSerializationSwift中使用的体验太垃圾了,以至于我们要再次去从第三方库中做出选择。

//Swift:
let data: NSData
var error: NSError? = nil
if let JSON = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &error) as? NSDictionary {
    for product in JSON["products"]! as NSArray {
        let name: String = product["name"] as String
        let price: Double = product["price"] as Double
    }
}


//Objective-C:
NSData *data;
NSError *error = nil;
id JSON = [NSJSONSerialization JSONObjectWithData:data
                                          options:0
                                            error:&error];
if (!error) {
    for (id product in JSON[@"products"]) {
        NSString *name = product[@"name"];
        NSNumber *price = product[@"price"];

        // ...
    }
}

这里我要为苹果公司辩护一下,我曾经在WWDC中问过一个工程师,为什么iOS支持JSON要花这么久的时间。它们的答案还是很中肯的。意思大致如下:
苹果是一个着眼于长远科技的公司。很难去判断像JSON这样的一个技术是否会持续流行,或者它只是一时的狂热。苹果曾经为PubSub发布过一个框架,在这之后,虽然PubSub并没有被广泛的了解和使用,但是在可见的未来一段时间内我们还是必须要去支持它。就像赌博一样,对于每一个我们下注的技术,我们都要付出很多资源。

数据封送(marshaling)和序列化是非常无聊的工作,而这种无聊的工作就是一个标准库应该处理的。当苹果开发Cocoa时就知道了这个,所以在Cocoa中实现了健壮的文本和二进制的property list,这两个对于iOS和OS X来说都是至关重要的。对于苹果来说,预先判定哪种数据交换格式会流行可能很困难,但是对新兴的技术提供适用版本的官方支持会为开发者带来很多好处。

正则表达式

正则表达式是脚本语言的标志,它们通常拥有对于字面常量的专用语法。如果Swift想要在Cocoa之上更进一步,它应该包含一个NSRegularExpression的继承者,比如这个工程,这样才是更加明智的做法。

错误处理

Objective-C更喜欢使用在这篇文章中描述的错误指针(NSError **)来和运行时的错误进行通信,而不是使用@throw跑出的异常。前者是每一个Cocoa开发者都应该熟悉的用法:

NSError *error = nil;
BOOL success = [[NSFileManager defaultManager] moveItemAtPath:@"/path/to/target"
                                                       toPath:@"/path/to/destination"
                                                        error:&error];
if (!success) {
    NSLog(@"%@", error);
}

使用error来作为输出参数是一种变通的方案,这是由于Objective-C只能有一个返回值。如果出现了错误,会有一个新的包含出错信息的对象填充NSError实例,并传递出来。

在Swift中就不需要这种用法了,因为方法可以返回一个元组,在这个元组中可以包含一个可选的值和error信息。

func moveItemAtPath(from: String toPath to: String) -> (Bool, NSError?) { ... }

我们甚至可以更进一步,为相关的成功和失败的情况定义一个通用的Result类型。

struct Error { ... }

public enum Result {
    case Success(T)
    case Failure(Error)
}

使用这种新的方法,错误处理可以使用编译器强制执行来枚举所有可能出现的情况。

HTTPClient.getUser { (result) in
    switch result {
    case .Success(let user):  // Success
    case .Failure(let error): // Failure
    }
}

Swift这种用法已经在一些急切的想要改善Swift中原始相关用法的社区出现了。如果标准库能够将这些用法纳入其中,将会提高开发者对Swift的讨论热情,让Swift在开发者群体中更加受欢迎。

AppKit & UIKit

AppKit 和UIKit它们两个本身就充满了可以讨论的话题。现在看来它们好像不会马上被重写并适配到Swift,而是先着眼于如何将这两个框架进行统一。相比而 言,一个更有趣的问题是,Swift是否会扩大自己目前在iOS和OS X上适用范围,比如在系统开发或者网络脚本方面,还有如果Swift这么做,会对Cocoa作为目前苹果平台上的事实标准的地位产生什么样的影响。

进一步思考

也许到现在为止,我们对于标准库应该是什么样子,想的还是太局限。

Wolfram语言,对于一个编程语言来说,似乎是包罗万象(这里是YouTube视频,可以去墙那边看看:The mother of all demos这里无意冒犯Douglas Engelbart)。

的确,Wolfram像是存在于另外一个平行宇宙里的东西,在那个宇宙里只有Wolfram一个东西,而且Wolfram本身是一团乱麻。

这里是Wolfram提供的功能概要列表

2D / 3D Visualization Graph Analysis Data Analytics Image Processing
Audio Processing Machine Learning Equation Solving Algebraic Computation
Arbitrary Precision Calculus Computation Matrix Computation String Manipulation
Combinatorial Optimization Computational Geometry Database Connectivity Built-In Testing
Device Connectivity Functional Programming Natural Language Understanding Sequence Analysis
Time Series Geographic Data Geomapping Weather Data
Physics & Chemistry Data Genomic Data Units & Measures Control Theory
Reliability Analysis Parallel Computation Engineering Data Financial Data
Financial Computation Socioeconomic Data Popular Culture Data Boolean Computation
Number Theory Document Generation Table Formatting Mathematical Typesetting
Interactive Controls Interface Building Form Construction XML Templating

理智可能会告诉你,哦, 一个简简单单的标准库要包含对《阿凡达》这部电影预算进行编码方法包含大黄蜂战斗机最高速度的计算方法,还有对法国领土形状的编码,这太TM疯狂了。想获取这些信息,你可以去查询IMDB,查阅维基百科,导入一个GIS系统,这样可以更好更简单的解决这些问题。

但是其他事情,比如英里对公里的转换聚值的计算,或者获取地球的尺寸——这些事情确实会对一些应用程序有用。

另外,Cocoa和其他标准库不同的地方就在于在Cocoa中可以用NSLocale和NSCalendar来编码一些特定的信息,但是这大多数都归功于Unicode通用区域数据库Unicode Common Locale Data Repository (CLDR).

是什么阻止了标准库从其它数据源获取信息?为什么不在libphonenumber中暴露出接口,还有为什么不继续扩展HealthKit在基本计量单位上的努力?

对于第三方框架来说,它们不可能很好地在框架内包含处理这些类型的信息的功能,另外这些功能又很重要,故而又不能完全交给自由的开源社区去做,所以,标准库应该承担起责任。

好吧,我的观点是,从很多方面看,标准库的角色定位有点像社会中公共企业和私营企业的角色。相对而言,第三方库相当于社会中的自由主义者。

———————————————————————-

Swift能够带来的,不仅是这个语言本身能做什么,还有它对苹果,对iOS和OS X的开发者来说意味着什么,以及对开发者社区会带来什么样的影响。有太多的因素会影响到Swift的未来,一个技术的可行性肯定也会受到社会和经济的影响。

未 来Swift是否会使用开源许可来发布?如果苹果在Safari浏览器中加入Swift的解释器,Swift会不会取代Javascript而成为未来的 web语言之王?最终,这都会是决定Swift标准库发展方向的因素。如果Swift想要变成移动设备系统语言或者脚本语言,那么它首先就要把自己从 Objective-C的运行时环境,和对网络重度依赖中剥离出来。

然而,几乎可以肯定的是,就像Objective-C一样,Cocoa将会渐渐消亡。现在的问题不是它是否会消亡,而是消亡的时间。(当然,这可能需要好多年;谁都不能轻易地说Objective-C & Cocoa会在一夕之间就消失)。

最后,我还想说,Swift的标准库和Cocoa某些地方存在冲突,这是正常的,如果这个新语言的势头继续发展下去,那么大家可以期待,在苹果开发的系统框架内部,可能会出现一些新的变革。

在过去的30年里,这些技术(指代Cocoa)很好的为我们服务,为了尊重它们的贡献,我们能做的最好的事情就是,从它们的错误中学到教训,并且确保代替它们的技术更好。
(本文为CocoaChina组织翻译,本译文权利归译者所有,未经允许禁止转载。)

关于我

月光下我看到自己的身影,有时很远有时很近。

KK笔记:kknotes.com
本文链接地址: Cocoa之殇

转载须以超链接形式标明文章原始出处和作者信息及版权声明

未经允许不得转载:KK笔记 » Cocoa之殇

赞 (0)

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址