WWDC2016 Session 207 - What’s New in Foundation for Swift

Why Foundation

首先上幾張圖炊琉,再引入正文:


圖1.png
圖2.png

概括來(lái)說(shuō),是這么三點(diǎn):

  • SDK 中獨(dú)具特色
  • 底層無(wú)處不在
  • 上層又與我們息息相關(guān),它建立了通用類型和設(shè)計(jì)模式

Foundation 說(shuō):給我一個(gè)支點(diǎn)苔咪,我能撬動(dòng)整個(gè) App锰悼。有個(gè)東西說(shuō)出來(lái)你們可能不信,F(xiàn)oundation 目前很多都開(kāi)始使用了值類型來(lái)實(shí)現(xiàn)团赏。這絕壁是玩命的革命盎恪!

推薦 SE-0069 Mutability and Foundation Value TypessSE-0086 Drop NS Prefix in Swift Foundation 這兩個(gè)提案舔清,基本闡述了關(guān)于如何改善 Foundation API丝里,包括值語(yǔ)義、命名調(diào)整体谒、遵循標(biāo)準(zhǔn)庫(kù)協(xié)議杯聚、更多類型安全以及更加 Swift 風(fēng)格的功能。

Value Types 和 Reference Types

這里簡(jiǎn)單過(guò)一遍抒痒,為之后鋪墊:

// 值類型
let start = CGPoint(x: 1, y: 2)
var end = start
end.x += 8

關(guān)系圖:


Screen Shot 2016-06-24 at 9.06.38 PM.png
// 引用類型
let data = NSMutableData(withContentsOf: file1)
var otherData = data
otherData.append(NSData(withContentsOf: file2)

關(guān)系圖:


Screen Shot 2016-06-24 at 9.06.44 PM.png

關(guān)于值類型和引用類型孰優(yōu)孰劣幌绍,其實(shí)是看應(yīng)用場(chǎng)景的,并非哪個(gè)更勝一籌故响,而是合適不合適的問(wèn)題傀广。官方對(duì)此也表示Neither is better—just used in different ways。另外舉了幾個(gè)例子彩届,讓我們一睹為快:

//OperationQueue.main

class OperationQueue : NSObject { 
  class var main: OperationQueue
}

//URLSession.delegate
public protocol URLSessionDataDelegate : URLSessionTaskDelegate { 
      optional public func urlSession(_ session: URLSession,
                                                dataTask: URLSessionDataTask,
                                                didReceive data: Data)
}

這里 main 是一個(gè)單例對(duì)象伪冰,旨在讓所有使用者操作同一個(gè)對(duì)象,所以假若使用值類型樟蠕,那么各個(gè)持有的就是不同的對(duì)象贮聂,沒(méi)有任何意義,而下面的 session也是一樣的道理坯墨。

而對(duì)于 Date 則是一個(gè) struct 類型,因此是一個(gè)值類型病往,看下定義:

public struct Date : Comparable, Equatable { 
    private var _time : Double
}

因?yàn)槲覀兏P(guān)心的是內(nèi)容的存儲(chǔ)捣染,所以用值類型更合適一些。對(duì)了 Data 也是結(jié)構(gòu)體類型:

public struct Data : Equatable, Hashable, RandomAccessCollection, MutableCollection{
    /// The Objective-C bridged type of `Data`.
    public typealias ReferenceType = NSData
}

注意到NSData沒(méi)停巷,實(shí)際結(jié)構(gòu)體中保留了一個(gè)類型為 NSData 的指針耍攘,保留對(duì)objective-c 中對(duì)象的引用。那么問(wèn)題來(lái)了畔勤!既然說(shuō)結(jié)構(gòu)體是值類型蕾各,那么var otherData=data后,otherData和data中指向的內(nèi)存是同一塊嗎庆揪?答案是在你未修改之前是YES式曲!見(jiàn)圖:

Screen Shot 2016-06-24 at 9.20.36 PM.png

那么修改了otherData[0] 的數(shù)據(jù)呢?那么otherData 會(huì)先copy()一份,然后修改吝羞!所以圖是這樣的:

Screen Shot 2016-06-24 at 9.25.03 PM.png

此時(shí) otherData 和 data 各自擁有一個(gè) class NSData 實(shí)例兰伤,你修改你的,我處理我的钧排,進(jìn)水不犯河水敦腔。

那么目前有哪些新的值類型呢?這里我列出一些:

  • AffineTransform
  • CharacterSet
  • Data
  • Date
  • DateComponent
  • DateInterval (新類型)
  • Decimal (有改動(dòng))
  • IndexPath
  • IndexSet
  • Measurement (新類型)
  • Notification
  • PersonNameComponents
  • URL
  • URLComponents
  • URLRequest
  • URLQueryItem
  • UUID

API 改動(dòng)實(shí)例說(shuō)明

Michael LeHew 介紹了很多恨溜,我抽幾個(gè)值得一談與大家分享下:

1.嵌套枚舉

接下來(lái)基本都是 Objc 符衔、Swift2.x 和 Swift3.0 的比較,前方高能糟袁,注意E凶濉!

//Objc
typedef NS_ENUM(NSUInteger, NSNumberFormatterStyle) { ... }
typedef NS_ENUM(NSUInteger, NSNumberFormatterBehavior) { ... }
typedef NS_ENUM(NSUInteger, NSNumberFormatterPadPosition) { ... }
typedef NS_ENUM(NSUInteger, NSNumberFormatterRoundingMode) { ... }
// Swift 2.2
public enum NSNumberFormatterStyle : UInt { ... }
public enum NSNumberFormatterBehavior : UInt { ... }
public enum NSNumberFormatterPadPosition : UInt { ... }
public enum NSNumberFormatterRoundingMode : UInt { ... }

首先 Objective-C 中定義了的4個(gè)枚舉類型系吭,實(shí)際上它們都屬于 NSNumberFormatter類型五嫂,但是籠統(tǒng)概括到一個(gè)枚舉又不是很合適,于是最終還是定義了style肯尺、behavior沃缘、padPositionroundingMode4個(gè)枚舉。在Swift2.2中橋接過(guò)來(lái)也是相當(dāng)老實(shí)则吟!一對(duì)一也是4個(gè)枚舉槐臀。怎么說(shuō)呢?中規(guī)中矩吧氓仲,盡管并排放在了一起水慨,但是感覺(jué)還是有種距離感。

再來(lái)看看 Swift3:

// Swift 3
public class NumberFormatter { 
  public enum style { ... } 
  public enum behavior { ... } 
  public enum padPosition { ... } 
  public enum roundingMode { ... }
}

可以看到 NumberFormatter 類下內(nèi)嵌了4個(gè)枚舉敬扛,style等和 NumberFormatter的關(guān)系一目了然晰洒。

強(qiáng)類型的字符串枚舉

Foundation 中定義了很多 NSString 類型的字符串常量對(duì)象,如下:

NSString *const NSProcessInfoThermalStateDidChangeNotification;
NSString *const NSTaskDidTerminateNotification;
NSString *const NSCalendarDayChangedNotification;

NSString *const NSURLIsRegularFileKey;
NSString *const NSURLCreationDateKey;
NSString *const NSURLVolumeMaximumFileSizeKey;

現(xiàn)在 Objective-C 別名了新類型替換掉這些礙眼的 NSString類型啥箭。

typedef NSString *NSNotificationName NS_EXTENSIBLE_STRING_ENUM;
NSNotificationName *const NSProcessInfoThermalStateDidChangeNotification;
NSNotificationName *const NSTaskDidTerminateNotification;
NSNotificationName *const NSCalendarDayChangedNotification;

NSURLResourceKey *const NSURLIsRegularFileKey;
NSURLResourceKey *const NSURLCreationDateKey;
NSURLResourceKey *const NSURLVolumeMaximumFileSizeKey;

請(qǐng)注意 NS_EXTENSIBLE_STRING_ENUM 修飾符谍珊,它的作用是在橋接到 Swift 中時(shí)可進(jìn)行枚舉擴(kuò)展。

// Objective-C 中我們新增一個(gè)NSNotificationName 的常量是這樣的
extern NSNotificationName const MyUserBecameActiveNotification;

// 而Swift 3 是這樣的
public extension Notification.Name { 
  public static let userLoggedOut = Notification.Name("UserLoggedOut")
}
let n = Notification(name: .userLoggedOut, object: nil)

看到這里是不是對(duì) Notification.Name 又詫異了急侥?為此我特地看了下聲明:

extension NSNotification {
    public struct Name : RawRepresentable, Equatable, Hashable, Comparable {
        public init(_ rawValue: String)
        public init(rawValue: String)
    }
}

原來(lái) NameNSNotification 的內(nèi)嵌結(jié)構(gòu)體砌滞,而上面的extension不過(guò)是在對(duì) Name結(jié)構(gòu)體做新增字段擴(kuò)展操作。

類屬性

這個(gè)比較簡(jiǎn)單坏怪,Objective-C 新增了一個(gè)名為 class 的特性贝润,Objective-C 的類對(duì)象其實(shí)使用了 getter 方法間接得到:

// Objective-C (conventional class properties)
@interface NSUserDefaults
+ (NSUserDefaults *)standardUserDefaults;
@end

// 而現(xiàn)在Objective-C 支持類屬性拉,完全可以這么做
@interface NSUserDefaults
@property (class, readonly, strong) standardUserDefaults;
@end

所以嘍铝宵,swift 也做出了相應(yīng)改動(dòng):

// Swift 2.2
public class NSUserDefaults { 
  public class func standardUserDefaults() -> NSUserDefaults
}

// Swift 3 (大部分而言)
public class UserDefaults { 
    public class var standardUserDefaults: UserDefaults
}

關(guān)于新的值類型

  • Date
  • Measurement
  • URLComponents
  • Data

下面通過(guò)實(shí)例來(lái)講解

// Swift 2.2
func whenToLeave() -> NSDate { ... }
let date = whenToLeave()//?
let reminder = date.dateByAddingTimeInterval(-5.0 * 60.0)//?

// Swift 3
func whenToLeave() -> Date { ... }
var date = whenToLeave().addTimeInterval(-5.0 * 60.0)

由于 NSDate類型是引用類型打掘,所以在?和?處實(shí)際分配了兩次內(nèi)存。另外使用 Date 的好處在于它支持時(shí)間比較,就像這樣:

func whenToLeave() -> Date { ... }
let when = whenToLeave().addingTimeInterval(-5.0 * 60.0)
if Date() < when {
timer = Timer(fireDate: when, interval: 0, repeats: false) {
 print("Almost time to go!")
}
 RunLoop.main.add(timer, forMode: .commonModes)
} else {
 print("You're late!")}

Measurement 新類型這里咱不討論

接下來(lái)說(shuō)說(shuō) URLComponents

var template = URLComponents()//?
template.scheme = "https"
template.host = "www.apple.com"
template.path = "/shop/buy-mac"
template.queryItems = [URLQueryItem(name: "step", value: "detail")]
var urls = Array<URLComponents>()
for product in ["MacBook", "MacBook Pro"]  {
    var components = template//?
    components.queryItems!.append(URLQueryItem(name: "product", value: product))
    urls.append(components)
}

首先? template 是一個(gè)值類型胧卤,那么在?中賦值之后唯绍,components在未修改之前它們確實(shí)是指向同一片內(nèi)存區(qū)域的,但是一旦修改枝誊,不好意思况芒,copy()一份,各自打理叶撒!不妨你看看 URLComponents 定義绝骚,內(nèi)部同樣有一個(gè) ReferenceType 指向Objective-C 引用類型。

接下來(lái)看下關(guān)于 Data 的改變祠够,不過(guò)前文說(shuō)到實(shí)際內(nèi)部還是引用了一個(gè) NSData Objective-C 對(duì)象压汪,因此我們先構(gòu)造一個(gè)OC 類,繼承自 NSData古瓤。

class AllOnesData : NSData { 
  override func getBytes(_ buffer: UnsafeMutablePointer<Void>, length: Int) {
       memset(buffer, 1, length)
   }
       ...
}

這里我們重寫(xiě)了 getBytes 方法對(duì)傳入指針指向的內(nèi)存區(qū)域做了初始化為1的操作止剖,也就是memset(buffer, 1, length)

現(xiàn)在我們定義一個(gè) Date 值類型對(duì)象 ones落君,這樣 let ones = Data(reference: AllOnesData(length: 5))穿香,緊接著我們將 ones 賦值給 copyvar copy = ones绎速,這是的關(guān)系圖應(yīng)該是這樣的:

Screen Shot 2016-06-24 at 11.23.07 PM.png

此時(shí)我們對(duì) copy 進(jìn)行操作

copy.withUnsafeMutableBytes { (bytes : UnsafeMutablePointer<UInt8>) in
   bytes.pointee = 0
}

那么就如前面所說(shuō)皮获,copy 會(huì)復(fù)制一份 NSData對(duì)象修改,然后將copy中的referencetype 指向新的對(duì)象∥圃現(xiàn)在是這樣的:

Screen Shot 2016-06-24 at 11.25.11 PM.png

Type Safe Access

Swift 同樣有Runtime洒宝,但是與OC的區(qū)別還是蠻大,不管怎么說(shuō)萌京,Swift 中也存在很多條件在運(yùn)行時(shí)才得以確定雁歌。

// Swift 2.2
let url = NSURL.fileURL(withPath: "/my-special-file")
let keys = [NSURLCreationDateKey, NSURLIsRegularFileKey, NSURLVolumeMaximumFileSizeKey]
let values = try url.resourceValues(forKeys: keys)// values的類型為[String,AnyObject]

上面想要實(shí)現(xiàn)的是傳入三個(gè)keys值,然后取到對(duì)應(yīng)的資源知残,也就是一個(gè)key對(duì)應(yīng)各自的數(shù)據(jù)靠瞎,values 是一個(gè)類型為[String,AnyObject]的字典類型,取值和賦值操作:

if values[NSURLIsRegularFileKey] as! Boolean { ... }
if let maxSize = (values[NSURLVolumeMaximumFileSizeKey] as? Int) { ... }

var newValues = values
newValues[NSURLIsRegularFileKey] = false
newValues[NSURLCreationDateKey] = "Two Days Ago"
try url.setResourceValues(newValues)

取值時(shí)你會(huì)陷入無(wú)盡的 as 痛苦深淵中橡庞。再來(lái)看看賦值

而 Swift3 提供的方式是這樣的:

// Swift 3
let url = URL(fileURLWithPath: "/my-special-file")
let keys : Set<URLResourceKey> = [.creationDateKey,
                                  .isRegularFileKey,
                                  .volumeMaximumFileSizeKey]
let values = try url.resourceValues(forKeys: keys)

此時(shí)values的類型是struct URLResourceValues较坛。類型明確印蔗,這樣就可以摒棄太多的 as 操作了扒最。 那么struct URLResourceValues又是如何定義呢?

public struct URLResourceValues {
   ...
   public var creationDate: Date? { get set }
   public var isRegularFile: Bool? { get }
   public var volumeMaximumFileSize: Int? { get }
   ...
   public var allValues: [URLResourceKey : AnyObject] { get }
}

可以看到所有均為只讀屬性华嘹,你無(wú)法對(duì)它進(jìn)行修改吧趣,增加了安全性。

關(guān)于 Native Enumerations 這里不再展開(kāi),Swift 中的枚舉相當(dāng)強(qiáng)大强挫,不妨看看我翻譯的這篇文章岔霸,幾乎囊括了所有用法。

此外視頻最后簡(jiǎn)單介紹了一些新的橋接改動(dòng)俯渤,我建議可以看專門(mén)的 Session 視頻呆细。這里也不再展開(kāi)。

本文只是一個(gè)概括性的 Session八匠,所以并沒(méi)有任何具體的案例提供絮爷,零零碎碎的趕腳,但是對(duì)于只是當(dāng)做了解而言還是很有價(jià)值的梨树!倘若覺(jué)得喜歡坑夯,請(qǐng)點(diǎn)擊關(guān)注并按下喜歡吧!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末抡四,一起剝皮案震驚了整個(gè)濱河市柜蜈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌指巡,老刑警劉巖淑履,帶你破解...
    沈念sama閱讀 216,651評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異厌处,居然都是意外死亡鳖谈,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)阔涉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)缆娃,“玉大人,你說(shuō)我怎么就攤上這事瑰排」嵋” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,931評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵椭住,是天一觀的道長(zhǎng)崇渗。 經(jīng)常有香客問(wèn)我,道長(zhǎng)京郑,這世上最難降的妖魔是什么宅广? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,218評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮些举,結(jié)果婚禮上跟狱,老公的妹妹穿的比我還像新娘。我一直安慰自己户魏,他們只是感情好驶臊,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布挪挤。 她就那樣靜靜地躺著,像睡著了一般关翎。 火紅的嫁衣襯著肌膚如雪扛门。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,198評(píng)論 1 299
  • 那天纵寝,我揣著相機(jī)與錄音论寨,去河邊找鬼。 笑死爽茴,一個(gè)胖子當(dāng)著我的面吹牛政基,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播闹啦,決...
    沈念sama閱讀 40,084評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼沮明,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了窍奋?” 一聲冷哼從身側(cè)響起荐健,我...
    開(kāi)封第一講書(shū)人閱讀 38,926評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎琳袄,沒(méi)想到半個(gè)月后江场,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,341評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡窖逗,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評(píng)論 2 333
  • 正文 我和宋清朗相戀三年址否,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片碎紊。...
    茶點(diǎn)故事閱讀 39,731評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡佑附,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出仗考,到底是詐尸還是另有隱情音同,我是刑警寧澤,帶...
    沈念sama閱讀 35,430評(píng)論 5 343
  • 正文 年R本政府宣布秃嗜,位于F島的核電站权均,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏锅锨。R本人自食惡果不足惜叽赊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望必搞。 院中可真熱鬧必指,春花似錦、人聲如沸顾画。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,676評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)研侣。三九已至谱邪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間庶诡,已是汗流浹背惦银。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,829評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留末誓,地道東北人扯俱。 一個(gè)月前我還...
    沈念sama閱讀 47,743評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像喇澡,于是被迫代替她去往敵國(guó)和親迅栅。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容