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

Why Foundation

首先上几张图,再引入正文:


图1.png
图2.png

概括来说,是这么三点:

  • SDK 中独具特色
  • 底层无处不在
  • 上层又与我们息息相关,它建立了通用类型和设计模式

Foundation 说:给我一个支点,我能撬动整个 App。有个东西说出来你们可能不信,Foundation 目前很多都开始使用了值类型来实现。这绝壁是玩命的革命啊!

推荐 SE-0069 Mutability and Foundation Value TypessSE-0086 Drop NS Prefix in Swift Foundation 这两个提案,基本阐述了关于如何改善 Foundation API,包括值语义、命名调整、遵循标准库协议、更多类型安全以及更加 Swift 风格的功能。

Value Types 和 Reference Types

这里简单过一遍,为之后铺垫:

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

关系图:


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

关系图:


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

关于值类型和引用类型孰优孰劣,其实是看应用场景的,并非哪个更胜一筹,而是合适不合适的问题。官方对此也表示Neither is better—just used in different ways。另外举了几个例子,让我们一睹为快:

//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 是一个单例对象,旨在让所有使用者操作同一个对象,所以假若使用值类型,那么各个持有的就是不同的对象,没有任何意义,而下面的 session也是一样的道理。

而对于 Date 则是一个 struct 类型,因此是一个值类型,看下定义:

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

因为我们更关心的是内容的存储,所以用值类型更合适一些。对了 Data 也是结构体类型:

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

注意到NSData没,实际结构体中保留了一个类型为 NSData 的指针,保留对objective-c 中对象的引用。那么问题来了!既然说结构体是值类型,那么var otherData=data后,otherData和data中指向的内存是同一块吗?答案是在你未修改之前是YES!见图:

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

那么修改了otherData[0] 的数据呢?那么otherData 会先copy()一份,然后修改!所以图是这样的:

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

此时 otherData 和 data 各自拥有一个 class NSData 实例,你修改你的,我处理我的,进水不犯河水。

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

  • AffineTransform
  • CharacterSet
  • Data
  • Date
  • DateComponent
  • DateInterval (新类型)
  • Decimal (有改动)
  • IndexPath
  • IndexSet
  • Measurement (新类型)
  • Notification
  • PersonNameComponents
  • URL
  • URLComponents
  • URLRequest
  • URLQueryItem
  • UUID

API 改动实例说明

Michael LeHew 介绍了很多,我抽几个值得一谈与大家分享下:

1.嵌套枚举

接下来基本都是 Objc 、Swift2.x 和 Swift3.0 的比较,前方高能,注意!!

//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个枚举类型,实际上它们都属于 NSNumberFormatter类型,但是笼统概括到一个枚举又不是很合适,于是最终还是定义了stylebehaviorpadPositionroundingMode4个枚举。在Swift2.2中桥接过来也是相当老实!一对一也是4个枚举。怎么说呢?中规中矩吧,尽管并排放在了一起,但是感觉还是有种距离感。

再来看看 Swift3:

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

可以看到 NumberFormatter 类下内嵌了4个枚举,style等和 NumberFormatter的关系一目了然。

强类型的字符串枚举

Foundation 中定义了很多 NSString 类型的字符串常量对象,如下:

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

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

现在 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;

请注意 NS_EXTENSIBLE_STRING_ENUM 修饰符,它的作用是在桥接到 Swift 中时可进行枚举扩展。

// Objective-C 中我们新增一个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)

看到这里是不是对 Notification.Name 又诧异了?为此我特地看了下声明:

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

原来 NameNSNotification 的内嵌结构体,而上面的extension不过是在对 Name结构体做新增字段扩展操作。

类属性

这个比较简单,Objective-C 新增了一个名为 class 的特性,Objective-C 的类对象其实使用了 getter 方法间接得到:

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

// 而现在Objective-C 支持类属性拉,完全可以这么做
@interface NSUserDefaults
@property (class, readonly, strong) standardUserDefaults;
@end

所以喽,swift 也做出了相应改动:

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

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

关于新的值类型

  • Date
  • Measurement
  • URLComponents
  • Data

下面通过实例来讲解

// 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类型是引用类型,所以在❶和❷处实际分配了两次内存。另外使用 Date 的好处在于它支持时间比较,就像这样:

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 新类型这里咱不讨论

接下来说说 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 是一个值类型,那么在❷中赋值之后,components在未修改之前它们确实是指向同一片内存区域的,但是一旦修改,不好意思,copy()一份,各自打理!不妨你看看 URLComponents 定义,内部同样有一个 ReferenceType 指向Objective-C 引用类型。

接下来看下关于 Data 的改变,不过前文说到实际内部还是引用了一个 NSData Objective-C 对象,因此我们先构造一个OC 类,继承自 NSData

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

这里我们重写了 getBytes 方法对传入指针指向的内存区域做了初始化为1的操作,也就是memset(buffer, 1, length)

现在我们定义一个 Date 值类型对象 ones,这样 let ones = Data(reference: AllOnesData(length: 5)),紧接着我们将 ones 赋值给 copyvar copy = ones,这是的关系图应该是这样的:

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

此时我们对 copy 进行操作

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

那么就如前面所说,copy 会复制一份 NSData对象修改,然后将copy中的referencetype 指向新的对象。现在是这样的:

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

Type Safe Access

Swift 同样有Runtime,但是与OC的区别还是蛮大,不管怎么说,Swift 中也存在很多条件在运行时才得以确定。

// 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]

上面想要实现的是传入三个keys值,然后取到对应的资源,也就是一个key对应各自的数据,values 是一个类型为[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)

取值时你会陷入无尽的 as 痛苦深渊中。再来看看赋值

而 Swift3 提供的方式是这样的:

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

此时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 }
}

可以看到所有均为只读属性,你无法对它进行修改,增加了安全性。

关于 Native Enumerations 这里不再展开,Swift 中的枚举相当强大,不妨看看我翻译的这篇文章,几乎囊括了所有用法。

此外视频最后简单介绍了一些新的桥接改动,我建议可以看专门的 Session 视频。这里也不再展开。

本文只是一个概括性的 Session,所以并没有任何具体的案例提供,零零碎碎的赶脚,但是对于只是当做了解而言还是很有价值的!倘若觉得喜欢,请点击关注并按下喜欢吧!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容