編者注:也許有人還記得前些天我們發的一篇翻譯文章Cocoa之死,那篇文章的作者是大神Mattt,但那也是他作為管理員在NSHipster上的最後一篇長文了,在那篇文章裡,Mattt對Swift充滿期許,暢想Swift標准庫的完美形態,期待Swift標准庫將順利的繼承Cocoa,這也是“之死”說法的由來。本文是Nate Cook所作,可以算是對那篇文章的呼應,他是SwiftDoc.org的創造者,也是繼Mattt之後NSHipster的主要維護者,讓我們期待新的NSHipster吧。
(原文:Long Live Cocoa 作者:Nate Cook 譯者:archerlu)
2015年,新年新開始——這注定是屬於Apple Watch和Swift的年份。你或許正期待著新手機,新的XCode beta,也可能正計劃著WWDC 2015之旅。別著急,讓我們看看今天我們擁有的工具:Objective-C,Swift,還有最重要的,Cocoa。
對部分人來說,Swift是令人激動的編程語言,但它畢竟屬於全新事物。Objective-C的成熟穩健,以及Cocoa的悠久歷史和強大功能,使得Swift一時之間還無法凌駕其上。Cocoa的內容深度和高效功能,還有如今這種與Swift齊頭並進、彼此依存的方式,使得它前景依然光明。實際上,我認為現在正是Cocoa開發者不可錯過的時期。
Cocoa擁有優秀出色、頗有深度的API——在它任何常用命令集之下探索,你就會發現一堆功能的寶庫。毋須以Mattt為例(這些年來,他工作出眾),只需看看Cocoa那些不為人知的功能。僅列舉部分如下:
NSLinguisticTagger ,AVSpeechSynthesizer::自然語言接口
NSCoding , NSKeyedArchiver:簡單數據持久化
NSOperation:面向對象的並發執行
CFStringTransform:多語種輸入的轉解碼
NSDataDetector:數據種類的檢測
UIActivityViewController , UIMenuController:本地自定義共享和編輯控制
NSURL內置的網絡緩存
更多列表項見此front page
實際上,Cocoa和Swift都為彼此而生——尤其是Swift。
在Cocoa這方面,前幾年它向工具集合的轉變為Swift的登堂入室鋪平了道路。向LLVM/Clang編譯框架的轉變,Objective-C中block語法的登場,NS_ENUM和NS_OPTIONS宏的出現,初始化構造器現在返回instancetype——幾年前就開始的這些步驟,與其說是為我們今天使用的Cocoa API量身定制,不如說是為了與Swift兼容。當你使用NSURLSession的CompletionHandler參數,或UIModalTransitionStyle的completion參數(注:Cocoa中使用block),如同今日使用Swift closure之時,你也在參與這項工程,直到Swift不久前結束秘密研發,走到公眾視野之中(或是說從Chris Lattner腦中醞釀成熟)。
Swift原本的設計基礎,是與Cocoa共同協作。如果要我指出哪個Swift特性最讓新手迷惑——那應該是Optionals——它所帶來的額外標點符號,以及待確定的變量值,都相當費解。盡管如此,Swift仍不失為一次成功的革新,像Optionals這類小問題可以略過不提:我們需要知道的是,Swift是一門全新的語言,但它並不需要全新的API。在如今指針和內存大批量存在的環境下,Swift類型安全,內存安全,完成了與數目繁多、基於C語言的Cocoa API直接交互的功能。
這決非區區小事。Apple開發工具團隊正致力於給全部API加上注釋,為它們的參數和返回值標明內存管理信息。一旦完成,Swift中使用functions就是安全的,因為編譯器知道如何在Swift和已注明的C代碼之間轉換。
這裡是已標注和未標注功能的例子。
首先,C語言版:
// Creates an immutable copy of a string. CFStringRef CFStringCreateCopy ( CFAllocatorRef alloc, CFStringRef theString ); // Encodes an OSType into a string suitable for use as a tag argument. CFStringRef UTCreateStringForOSType ( OSType inOSType );
兩個function都返回CFStringRef——CFString的引用。CFStringRef可以被轉換成Swift的CFString實例,但僅在方法被標注時才是安全的。
看看Swift的兩個例子:
// annotated: returns a memory-managed Swift `CFString` func CFStringCreateCopy(alloc: CFAllocator!, theString: CFString!) -> CFString! // unannotated: returns an *unmanaged* `CFString` func UTCreateStringForOSType(inOSType: OSType) -> Unmanaged< CFString>!
最後一行的Unmanaged
此外,Swift並不止步於Cocoa API——這些API得到了擴展。來看看存在了有些年頭的CGRect吧。作為一個C類的結構體,它不含任何實例方法,因此在眾多基礎庫中都包含有處理CGRect的函數。函數很強大,但你畢竟需要找到它們,還要知道如何使用。以下四行代碼,將CGRect分為兩個小部分,可以分為三行:
Objective-C
CGRect nextRect; CGRect remainingRect; CGRectDivide(sourceRect, &nextRect, &remainingRect, 250, CGRectMinXEdge); NSLog("Remaining rect: %@", NSStringFromCGRect(remainingRect));
在Swift中,結構體可以有靜態和實例方法,還可以有computed properties(計算屬性)。Core Graphics擴展了CGRect之後,那些處理CGRect的功能就更容易查找和使用了。由於CGRect*內置於實例方法或屬性中,代碼減少為:
Swift
let (nextRect, remainingRect) = sourceRect.rectsByDividing(250, CGRectEdge.MinXEdge) println("Remaining rect: \(remainingRect)")
(譯者注:作者大意是,CGRect現在自帶處理,已不必到庫裡去查找或引用)
實話說,Cocoa和Swift有時頗難整合,在那些堪稱Objective-C語法標志的地方更加如此。Delegates,target-selector,還有NSInvocation,至今依然占有一席之地。與此不同的是Swift closure,相對容易的closure因其簡潔出現在原本需要一整個方法(或許是更多方法)的地方。在現存的Cocoa類中加入更多closure類或block類的方法,可以緩解這個問題。比如,NSTimer的接口要求Objective-C方法,用selector或是invocation傳遞。當你定義timer時,准備好這個:NSTimer Swift擴展,對接Core Foundation的相應部分,CFTimer,簡單快捷。
Swift
let message = "Are we there yet?" let alert = UIAlertController(title: message, message: nil, preferredStyle: .Alert) alert.addAction(UIAlertAction(title: "No", style: .Default, handler: nil)) NSTimer.scheduledTimerWithTimeInterval(10, repeats: true) { [weak self] timer in if self?.presentedViewController == nil { self?.presentViewController(alert, animated: true, completion: nil) } } // I swear I'll turn this car around.
無意針對Mattt的文章(即那篇Cocoa之死)——多年之後,我們可能會手持42寸iPad,一邊使用Cocoa的後繼版本,一邊朝窗外Titan星(注:土星的衛星)那荒涼的景色望去——倘若那時Cocoa依然健在,豈不是美事一樁?