跳到主要内容

9 篇博文 含有标签「Swift」

查看所有标签

GCD 在 Swift 中的用法

· 阅读需 2 分钟
BY

DispatchQueue

Swift 中,对 GCD 语法进行了彻底改写。引入了 DispatchQueue 这个类。

先来看看在一个异步队列中读取数据, 然后再返回主线程更新 UI, 这种操作在新的 Swift 语法中是这样的:

DispatchQueue.global().async {

DispatchQueue.main.async {

// 更新UI操作

}

}

DispatchQueue.global().async 相当于使用全局队列进行异步操作。然后在调用 DispatchQueue.main.async 使用主线程更新相应的 UI 内容。

优先级

新的 GCD 引入了 QoS (Quality of Service) 的概念。

先看看下面的代码:

DispatchQueue.global(qos: .userInitiated).async {

}

QoS 对应的就是 Global Queue 中的优先级。 其优先级由最低的 background 到最高的 userInteractive 共五个,还有一个未定义的 unspecified

public static let background: DispatchQoS

public static let utility: DispatchQoS

public static let `default`: DispatchQoS

public static let userInitiated: DispatchQoS

public static let userInteractive: DispatchQoS

public static let unspecified: DispatchQoS

自定义 Queue

除了直接使用 DispatchQueue.global().async 这种封装好的代码外,还可以通过DispatchWorkItem 自定义队列的优先级,特性:

let queue = DispatchQueue(label: "swift_queue")
let dispatchworkItem = DispatchWorkItem(qos: .userInitiated, flags: .inheritQoS) {

}
queue.async(execute: dispatchworkItem)

GCD定时器

Swift 中 dispatch_time的用法改成了:

let delay = DispatchTime.now() + .seconds(60)
DispatchQueue.main.asyncAfter(deadline: delay) {

}

相较与OC来说更易读了:

let dispatch_time = dispatch_time(DISPATCH_TIME_NOW, Int64(60 * NSEC_PER_SEC))

Swift 4 新特性

· 阅读需 3 分钟
BY

private 权限扩大

在 Swift 4 中,extension 可以读取 private 变量了。

Swift 3 中,如果将主体函数的变量定义为 private,则其 extension 无法读取此变量,必须将其改为 filePrivate 才可以。

单向区间

单向区间是一个新的类型,主要分两种:确定上限和确定下限的区间。直接用字面量定义大概可以写成 …62…

例如

let intArr = [0, 1, 2, 3, 4]

let arr1 = intArr[...3] // [0, 1, 2, 3]
let arr2 = intArr[3...] // [3, 4]

字符串改动

String 操作简化了

String 许多要通过 .characters 进行的操作,可以直接用 String 进行操作了。

例如:

let greeting = "Hello, 😜!"
// No need to drill down to .characters
let n = greeting.count
let endOfSentence = greeting.index(of: "!")!

新增 Substring 类型

swift 4 为字符串片段新增了一个叫 Substring 的类型。

当你创建一个字符串的片段时,会产生一个 Substring 实例。SubstringString 用法相同, 因为子串和原字符串共享内存,所以对子串的操作快速而且高效。

let greeting = "Hi there! It's nice to meet you! 👋"
let endOfSentence = greeting.index(of: "!")!

// 产生 Substring 实例
let firstSentence = greeting[...endOfSentence]
// firstSentence == "Hi there!"

// `Substring` 与 `String` 用法相同
let shoutingSentence = firstSentence.uppercased()
// shoutingSentence == "HI THERE!"

但是要注意一个 Substring 保留从其生成的完整的 String值。 当您传递一个看似很小的 Substring 时,这可能导致意外的高内存开销。所以使用 Substring时,最好转化为 String.

let newString = String(substring)

换行可以不用 \n了!

Swift 3,字符串换行要插入 \n。 例如:

在 Swift 4 可以这样操作:

用两个 “”“ 包裹起来的字符串会自动添加 \n 换行,更加直观了。注意:换行与缩进参照的是第二个 “”“ 号的位置。

嗯,我觉得OK!

支持 Unicode 9

Swift 4 支持 Unicode 9,为现代表情符号修正了一些问题

let family1 = "👨‍👩‍👧‍👦"
let family2 = "👨\u{200D}👩\u{200D}👧\u{200D}👦"
family1 == family2 // → true

居然还有这种操作~

新增 KeyPath 数据类型

KeyPath 是 Swift 4 新增加的数据类型。

定义两个结构体 PersonBook

struct Person {
var name: String
}

struct Book {
var title: String
var authors: [Person]
var primaryAuthor: Person {
return authors.first!
}
}

let abelson = Person(name: "Harold Abelson")
let sussman = Person(name: "Gerald Jay Sussman")
let book = Book(title: "Structure and Interpretation of Computer Programs", authors: [abelson, sussman])
book[keyPath: \Book.title]
book[keyPath: \Book.primaryAuthor.name]
// 相当与
book.title
book.primaryAuthor.name

这里 \Book.title\Book.primaryAuthor.name 就是 KeyPath.

KeyPath 可以用 .appending 拼接

let authorKeyPath = \Book.primaryAuthor
let nameKeyPath = authorKeyPath.appending(path: \.name)
// nameKeyPath = \Book.primaryAuthor.name

新增 swapAt() 函数

Swift 4 引入了一种在集合中交换两个元素的新方法: swapAt()

Swift 3 交换集合中的元素的用 swap()

var numbers = [1,2,3,4,5]
swap(&numbers[0], &numbers[1])
// numbers = [2,1,3,4,5]

Swift 4 中可以直接用

var numbers = [1,2,3,4,5]
numbers.swapAt(0,1)
// numbers = [2,1,3,4,5]

其他改动

其他改动如:新的整数协议泛型下标NSNumber bridging

可以参考:whats new in swift4

Swift的HMAC和SHA1加密

· 阅读需 2 分钟
BY

HMAC是密钥相关的哈希运算消息认证码(Hash-based Message Authentication Code)。 HMAC运算利用哈希算法,以一个密钥和一个消息为输入,生成一个消息摘要作为输出。也就是说HMAC通过将哈希算法(SHA1, MD5)与密钥进行计算生成摘要。

Objectice-C

在上个 Objectice-C 项目中,使用的 HMAC 和 SHA1 进行加密。如下代码:

+ (NSString *)hmacsha1:(NSString *)text key:(NSString *)secret {

NSData *secretData = [secret dataUsingEncoding:NSUTF8StringEncoding];
NSData *clearTextData = [text dataUsingEncoding:NSUTF8StringEncoding];
unsigned char result[20];
// SHA1加密
CCHmac(kCCHmacAlgSHA1, [secretData bytes], [secretData length], [clearTextData bytes], [clearTextData length], result);
char base64Result[32];
size_t theResultLength = 32;
// 转为Base64
Base64EncodeData(result, 20, base64Result, &theResultLength,YES);
NSData *theData = [NSData dataWithBytes:base64Result length:theResultLength];
NSString *base64EncodedResult = [[NSString alloc] initWithData:theData encoding:NSUTF8StringEncoding];
return base64EncodedResult;
}

swift

最近用 swift 重构项目,用 Swift 重写了 HMAC 的 SHA1 加密方式。

使用

// 使用HMAC和SHA加密
let hmacResult:String = "myStringToHMAC".hmac(HMACAlgorithm.SHA1, key: "myKey")

代码

使用下面代码时,需要在 OC 桥接文件xxx-Bridging-Header#import <CommonCrypto/CommonHMAC.h>

extension String {
func hmac(algorithm: HMACAlgorithm, key: String) -> String {
let cKey = key.cStringUsingEncoding(NSUTF8StringEncoding)
let cData = self.cStringUsingEncoding(NSUTF8StringEncoding)
var result = [CUnsignedChar](count: Int(algorithm.digestLength()), repeatedValue: 0)
CCHmac(algorithm.toCCHmacAlgorithm(), cKey!, strlen(cKey!), cData!, strlen(cData!), &result)
var hmacData:NSData = NSData(bytes: result, length: (Int(algorithm.digestLength())))
var hmacBase64 = hmacData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.Encoding76CharacterLineLength)
return String(hmacBase64)
}
}

enum HMACAlgorithm {
case MD5, SHA1, SHA224, SHA256, SHA384, SHA512

func toCCHmacAlgorithm() -> CCHmacAlgorithm {
var result: Int = 0
switch self {
case .MD5:
result = kCCHmacAlgMD5
case .SHA1:
result = kCCHmacAlgSHA1
case .SHA224:
result = kCCHmacAlgSHA224
case .SHA256:
result = kCCHmacAlgSHA256
case .SHA384:
result = kCCHmacAlgSHA384
case .SHA512:
result = kCCHmacAlgSHA512
}
return CCHmacAlgorithm(result)
}

func digestLength() -> Int {
var result: CInt = 0
switch self {
case .MD5:
result = CC_MD5_DIGEST_LENGTH
case .SHA1:
result = CC_SHA1_DIGEST_LENGTH
case .SHA224:
result = CC_SHA224_DIGEST_LENGTH
case .SHA256:
result = CC_SHA256_DIGEST_LENGTH
case .SHA384:
result = CC_SHA384_DIGEST_LENGTH
case .SHA512:
result = CC_SHA512_DIGEST_LENGTH
}
return Int(result)
}
}


在 Swift 中使用 IBInspectable

· 阅读需 3 分钟
BY

本文首次发布于 BY Blog, 作者 @柏荧(BY) ,转载请保留原文链接.

通过 IB 设置 控件 的属性非常的方便。

但是缺点也很明显,那就是有一些属性没有暴露在 IB 的设置面板中。这时候就要使用 @IBInspectable 在 IB 面板中添加这些没有的属性。

关于在 OC 中使用 IBInspectable 可以看一下我的 这篇文章

正文

在项目中最常遇到的情况是为 view 设置圆角、描边,以及为 文本控件 添加本地化字符串。

圆角、描边

先来看看设置圆角、描边

extension UIView {
@IBInspectable var cornerRadius: CGFloat {
get {
return layer.cornerRadius
}

set {
layer.cornerRadius = newValue
layer.masksToBounds = newValue > 0
}
}

@IBInspectable var borderWidth: CGFloat {
get {
return layer.borderWidth
}
set {
layer.borderWidth = newValue > 0 ? newValue : 0
}
}

@IBInspectable var borderColor: UIColor {
get {
return UIColor(cgColor: layer.borderColor!)
}
set {
layer.borderColor = newValue.cgColor
}
}

}

添加完成就可以在 IB 中设置 view 的这些属性了

运行效果

利用 @IBDesignable 在 IB 中实时显示 @IBInspectable 的样式

创建一个新的 class 继承 UIView ,并且使用 @IBDesignable 声明

import UIKit

@IBDesignable class IBDesignableView: UIView {

}

在 IB 中,选择 view 的 class 为 我们新建的 IBDesignableView

这样在 IB 调整属性时,这些属性的变化就会实时显示在 IB 中。

本地化字符串

本地化字符串的解决方法和上面的添加圆角一样

extension UILabel {
@IBInspectable var localizedKey: String? {
set {
guard let newValue = newValue else { return }
text = NSLocalizedString(newValue, comment: "")
}
get { return text }
}
}

extension UIButton {
@IBInspectable var localizedKey: String? {
set {
guard let newValue = newValue else { return }
setTitle(NSLocalizedString(newValue, comment: ""), for: .normal)
}
get { return titleLabel?.text }
}
}

extension UITextField {
@IBInspectable var localizedKey: String? {
set {
guard let newValue = newValue else { return }
placeholder = NSLocalizedString(newValue, comment: "")
}
get { return placeholder }
}
}

这样,在 IB 中我们就可以利用对应类型的 Localized Key 来直接设置本地化字符串了:

结语

IBInspectable 可以使用这些的类型

  • Int
  • CGFloat
  • Double
  • String
  • Bool
  • CGPoint
  • CGSize
  • CGRect
  • UIColor
  • UIImage

合理的使用@IBInspectable 能减少很多的模板代码,提高我们的开发效率。

参考

R.swift 的使用

· 阅读需 2 分钟
BY

本文首次发布于 BY Blog, 作者 @柏荧(BY) ,转载请保留原文链接.

介绍 R.swift 前,我们先看看 R.swift 能做什么

通常,我们是基于 字符串 来获取资源,例如:图片、xib、或者是 segue

let myImage = UIImage(named: "myImage")
let myViewController = R.storyboard.main.myViewController()

使用 R.swfit,我们可以这样写

let myImage = R.image.myImage()
let viewController = R.storyboard.main.myViewController()

R.swift 通过扫描你的各种基于字符串命名的资源,创建一个使用类型来获取资源。

在保证类型安全的同时,也在自动补全的帮助下节省了大量的时间。

导入 R.swift

R.swift 开源在 github 上。

这里是导入的视频教程

使用 CocoaPods 导入项目中

  1. 添加 pod 'R.swift'到 Podfile 文件,然后运行 pod install

  2. 添加一个 New Run Script Phase

  3. Run Script 拖动到 Check Pods Manifest.lock 的下面,并且添加脚本 "$PODS_ROOT/R.swift/rswift" "$SRCROOT/项目名称"

  4. Command+B 编译项目,在项目代码目录下,会生成一个 R.generated.swift 的文件,将它拖如项目中

    注意:不要勾选 Copy items if needed 选项,因为每次编译都会生成新的 R.generated.swift 文件,copy 的话,旧的 R.generated.swift 将不会被覆盖。

tip: 可以在添加 .gitignore 添加一行 *.generated.swift 忽略该文件,避免造成冲突

用法

导入完成后,就可以在使用 R.swift 了

关于 R.swift 的更多用法,可以 看这里

Swift 的懒加载和计算型属性

· 阅读需 2 分钟
BY

本文首次发布于 BY Blog, 作者 @柏荧(BY) ,转载请保留原文链接.

懒加载

常规(简化)写法

懒加载的属性用 var 声明

lazy var name: String = {
return "BY"
}()

完整写法

lazy var name: String = { () -> String i
return "BY"
}()

本质是一个创建一个闭包 {} 并且在调用该属性时执行闭包 ()

如OC的懒加载不同的是 swift 懒加载闭包只调用一次,再次调用该属性时因为属性已经创建,不再执行闭包。

计算型属性

常规写法

var name: string {
return "BY"
}

完整写法

var name: string {
get {
return "BY"
}
}

计算型属性本质是重写了 get 方法,其类似一个无参有返回值函数,每次调用该属性都会执行 return

通常这样使用

struct Cuboid {
var width = 0.0, height = 0.0, depth = 0.0
var volume: Double {
return width * height * depth
}
}
let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
// Prints "the volume of fourByFiveByTwo is 40.0"

两者对比

相同点

  • 使用方法完全一致
  • 都是用 var 声明

不同点

  • 实现原理不同

    懒加载是第一次调用属性时执行闭包进行赋值

    计算型属性是重写 get 方法

  • 调用 {}的次数不同

    懒加载的闭包只在属性第一次调用时执行 计算型属性每次调用都要进入 {} 中,return 新的值

强化 swift 中的 print

· 阅读需 2 分钟
BY

在 Swift 中,最简单的输出方法就是使用 print(),在我们关心的地方输出字符串和值。

当程序变得非常复杂的时候,我们可能会输出很多内容,而想在其中寻找到我们希望的输出其实并不容易。我们往往需要更好更精确的输出,这包括输出这个 log 的文件,调用的行号以及所处的方法名字等等。

在 Swift 中,编译器为我们准备了几个很有用的编译符号,它们分别是:

符号类型描述
#fileString包含这个符号的文件的路径
#lineInt符号出现处的行号
#columnInt符号出现处的列
#functionString包含这个符号的方法名字

有了上面的这些编译符号,我们就可以自定义一个输出函数:printm

public func printm(items: Any..., filename: String = #file, function: String = #function, line: Int = #line) {
print("[\((filename as NSString).lastPathComponent) \(line) \(function)]\n",items)
}

因为输出是一个很消耗性能的操作,所以在releass环境下需要将输出函数去掉,将上面的函数换成:

#if DEBUG

public func printm(items: Any..., filename: String = #file, function: String = #function, line: Int = #line) {
print("[\((filename as NSString).lastPathComponent) \(line) \(function)]\n",items)
}

#else

public func printm(items: Any..., filename: String = #file, function: String = #function, line: Int = #line) { }

#endif

参考:

本文首次发布于 BY Blog, 作者 @柏荧(BY) ,转载请保留原文链接.

Swift 3.1 的新变化「译」

· 阅读需 12 分钟
BY

Xcode 8.3 和 Swift 3.1 现在已经发布了(3/28)!

可以通过 AppStoreApple Developer 进行下载

Xcode 8.3 优化了 Objective-C 与 Swift 混编项目的编译速度.

Swift 3.1 版本包含一些期待已久的 Swift package manager 功能和语法本身的改进。

如果您没有密切关注 Swift Evolution 进程,请继续阅读 - 本文非常适合您!

在本文中,我将强调Swift 3.1中最重要的变化,这将对您的代码产生重大影响。我们来吧!😃

开始

Swift 3.1与Swift 3.0源代码兼容,因此如果您已经使用Xcode 中的 Edit \ Convert \ To Current Swift Syntax ... 将项目迁移到Swift 3.0,新功能将不会破坏您的代码。不过,苹果已经在Xcode 8.3中支持Swift 2.3。所以如果你还没有从Swift 2.3迁移,现在是时候这样做了!

在下面的部分,您会看到链接的标签,如[SE-0001]。这些是 Swift Evolution 提案号码。我已经列出了每个提案的链接,以便您可以发现每个特定更改的完整详细信息。我建议您尝试在Playground上验证新的功能,以便更好地了解所有更改的内容。

Note:如果你想了解 swift 3.0 中的新功能,可以看这篇文章

语法改进

首先,我们来看看这个版本中的语法改进,包括关于数值类型的可失败构造器Failable Initializers),新的序列函数等等。

可失败的数值转换构造器(Failable Numeric Conversion Initializers)

Swift 3.1 为所有数值类型 (Int, Int8, Int16, Int32, Int64, UInt, UInt8, UInt16, UInt32, UInt64, Float, Float80, Double) 添加了可失败构造器

这个功能非常有用,例如,以安全、可恢复的方式处理外源松散类型数据的转换,下面来看 Student 的 JSON 数组的处理:

class Student {
let name: String
let grade: Int

init?(json: [String: Any]) {
guard let name = json["name"] as? String,
let gradeString = json["grade"] as? String,
let gradeDouble = Double(gradeString),
let grade = Int(exactly: gradeDouble) // <-- 3.1 的改动在这
else {
return nil
}
self.name = name
self.grade = grade
}
}

func makeStudents(with data: Data) -> [Student] {
guard let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments),
let jsonArray = json as? [[String: Any]] else {
return []
}
return jsonArray.flatMap(Student.init)
}

let rawStudents = "[{\"name\":\"Ray\", \"grade\":\"5.0\"}, {\"name\":\"Matt\", \"grade\":\"6\"},
{\"name\":\"Chris\", \"grade\":\"6.33\"}, {\"name\":\"Cosmin\", \"grade\":\"7\"},
{\"name\":\"Steven\", \"grade\":\"7.5\"}]"
let data = rawStudents.data(using: .utf8)!
let students = makeStudents(with: data)
dump(students) // [(name: "Ray", grade: 5), (name: "Matt", grade: 6), (name: "Cosmin", grade: 7)]

Student 类中使用了一个可失败构造器将 grade 属性从 Double 转变为 Int,像这样

let grade = Int(exactly: gradeDouble)

如果gradeDouble不是整数,例如6.33,它将失败。如果它可以用一个正确的表示Int,例如6.0,它将成功。

Note:虽然throwing initializers 可以用来替代 failable initializers。但是使用 failable initializers 会更好,更符合人的思维。

新的序列函数(Sequence Functions)

swift3.1添加了两个新的标准库函数在 Sequence 协议中:prefix(while:)``和prefix(while:)[SE-0045]

构造一个斐波纳契无限序列:

let fibonacci = sequence(state: (0, 1)) {
(state: inout (Int, Int)) -> Int? in
defer {state = (state.1, state.0 + state.1)}
return state.0
}

在Swift 3.0中,您只需指定迭代次数即可遍历fibonacci序列:

// Swift 3.0
for number in fibonacci.prefix(10) {
print(number) // 0 1 1 2 3 5 8 13 21 34
}

在swift 3.1中,您可以使用prefix(while:)drop(while:)获得符合条件在两个给定值之间的序列中的所有元素,就像这样:

// Swift 3.1
let interval = fibonacci.prefix(while: {$0 < 1000}).drop(while: {$0 < 100})
for element in interval {
print(element) // 144 233 377 610 987
}

prefix(while:)返回满足某个谓词的最长子序列。它从序列的开头开始,并停在给定闭包返回false的第一个元素上。

drop(while:) 相反:它返回从给定关闭返回false的第一个元素开始的子序列,并在序列结尾完成。

Note:这种情况,可以使用尾随闭包的写法:

let interval = fibonacci.prefix{$0 < 1000}.drop{$0 < 100}

Concrete Constrained Extensions(姑且翻译为类的约束扩展吧)

Swift 3.1允许您扩展具有类型约束的通用类型。以前,你不能像这样扩展类型,因为约束必须是一个协议。我们来看一个例子。

例如,Ruby on Rails提供了一种isBlank检查用户输入的非常有用的方法。以下是在Swift 3.0中用 String 类型的扩展实现这个计算型属性

// Swift 3.0
extension String {
var isBlank: Bool {
return trimmingCharacters(in: .whitespaces).isEmpty
}
}

let abc = " "
let def = "x"

abc.isBlank // true
def.isBlank // false

如果你希望isBlank计算型属性为一个可选值所用,在swift 3.0中,你将要这样做

// Swift 3.0
protocol StringProvider {
var string: String {get}
}

extension String: StringProvider {
var string: String {
return self
}
}

extension Optional where Wrapped: StringProvider {
var isBlank: Bool {
return self?.string.isBlank ?? true
}
}

let foo: String? = nil
let bar: String? = " "
let baz: String? = "x"

foo.isBlank // true
bar.isBlank // true
baz.isBlank // false

这创建了一个采用 StringStringProvider 协议而在你使用StringProvider扩展可选的 wrapped 类型时,添加isBlank方法。

Swift 3.1中,用来替代协议方法,扩展具体类型的方法像这样:

// Swift 3.1
extension Optional where Wrapped == String {
var isBlank: Bool {
return self?.isBlank ?? true
}
}

这就用更少的代码实现了和原先相同的功能~

泛型嵌套(Nested Generics)

Swift 3.1允许您将嵌套类型与泛型混合。作为一个练习,考虑这个(不是太疯狂)的例子。每当某个团队领导raywenderlich.com想在博客上发布一篇文章时,他会分配一批专门的开发人员来处理这个问题,以满足网站的高质量标准:

class Team<T> {
enum TeamType {
case swift
case iOS
case macOS
}

class BlogPost<T> {
enum BlogPostType {
case tutorial
case article
}

let title: T
let type: BlogPostType
let category: TeamType
let publishDate: Date

init(title: T, type: BlogPostType, category: TeamType, publishDate: Date) {
self.title = title
self.type = type
self.category = category
self.publishDate = publishDate
}
}

let type: TeamType
let author: T
let teamLead: T
let blogPost: BlogPost<T>

init(type: TeamType, author: T, teamLead: T, blogPost: BlogPost<T>) {
self.type = type
self.author = author
self.teamLead = teamLead
self.blogPost = blogPost
}
}

BlogPost内部类嵌套在其对应的Team外部类中,并使两个类都通用。这是团队如何寻找我在网站上发布的教程和文章:

Team(type: .swift, author: "Cosmin Pupăză", teamLead: "Ray Fix", 
blogPost: Team.BlogPost(title: "Pattern Matching", type: .tutorial,
category: .swift, publishDate: Date()))

Team(type: .swift, author: "Cosmin Pupăză", teamLead: "Ray Fix",
blogPost: Team.BlogPost(title: "What's New in Swift 3.1?", type: .article,
category: .swift, publishDate: Date()))

但实际上,在这种情况下,您可以简化该代码。如果嵌套的内部类型使用通用外部类型,那么它默认继承父类的类型。因此,您不需要如此声明:

class Team<T> {
// original code

class BlogPost {
// original code
}

// original code
let blogPost: BlogPost

init(type: TeamType, author: T, teamLead: T, blogPost: BlogPost) {
// original code
}
}

Note:如果您想了解更多关于Swift中的泛型,请阅读我们最近更新的Swift泛型入门的教程

Swift版本的可用性

您可以使用**#if swift(>= N)** 静态构造来检查特定的Swift版本:

// Swift 3.0
#if swift(>=3.1)
func intVersion(number: Double) -> Int? {
return Int(exactly: number)
}
#elseif swift(>=3.0)
func intVersion(number: Double) -> Int {
return Int(number)
}
#endif

然而,当使用Swift标准库时,这种方法有一个主要缺点。它需要为每个受支持的旧语言版本编译标准库。这是因为当您以向后兼容模式运行Swift编译器时,例如您要使用Swift 3.0行为,则需要使用针对该特定兼容性版本编译的标准库版本。如果您使用版本3.1模式编译的,那么您根本就没有正确的代码

因此,@available除了现有平台版本 [SE-0141] 之外,Swift 3.1扩展了该属性以支持指定Swift版本号:

// Swift 3.1

@available(swift 3.1)
func intVersion(number: Double) -> Int? {
return Int(exactly: number)
}

@available(swift, introduced: 3.0, obsoleted: 3.1)
func intVersion(number: Double) -> Int {
return Int(number)
}

这个新功能提供了与intVersionSwift版本有关的方法相同的行为。但是,它只允许像标准库这样的库被编译一次。编译器然后简单地选择可用于所选择的给定兼容性版本的功能。

Note:注意:如果您想了解更多关于Swift 的可用性属性( availability attributes),请参阅我们关于Swift中可用性属性的教程

逃逸闭包(Escaping Closures)

在Swift 3.0 [ SE-0103 ] 中函数中的闭包的参数是默认是不逃逸的(non-escaping)。在Swift 3.1中,您可以使用新的函数withoutActuallyEscaping()将非逃逸闭包转换为临时逃逸。

func perform(_ f: () -> Void, simultaneouslyWith g: () -> Void,
on queue: DispatchQueue) {
withoutActuallyEscaping(f) { escapableF in // 1
withoutActuallyEscaping(g) { escapableG in
queue.async(execute: escapableF) // 2
queue.async(execute: escapableG)

queue.sync(flags: .barrier) {} // 3
} // 4
}
}

此函数同时加载两个闭包,然后在两个完成之后返回。

  1. fg 进入函数后由非逃逸状态,分别转换为逃逸闭包:escapableFescapableG
  2. async(execute:) 的调用需要逃逸闭包,我们在上面已经进行了转换。
  3. 通过运行sync(flags: .barrier),您确保async(execute:)方法完全完成,稍后将不会调用闭包。
  4. 在范围内使用 escapableF and escapableG.

如果你存储临时逃离闭包(即真正逃脱)这将是一个Bug。未来版本的标准库可以检测这个陷阱,如果你试图调用它们。

Swift Package Manager 更新

啊,期待已久的 Swift Package Manage 的更新了!

可编辑软件包(Editable Packages)

Swift 3.1将可编辑软件包(editable packages)的概念添加到Swift软件包管理器 [ SE-0082 ]

swift package edit命令使用现有的Packages并将其转换为editable Packages。使用--end-edit命令将 package manager 还原回 规范解析的软件包(canonical resolved packag)

版本固定(Version Pinning)

Swift 3.1 添加了版本固定的概念[ SE-0145 ]。该 pin 命令 固定一个或所有依赖关系如下所示:

$ swift package pin --all      // 固定所有的依赖
$ swift package pin Foo // 固定 Foo 在当前的闭包
$ swift package pin Foo --version 1.2.3 // 固定 Foo 在 1.2.3 版本

使用unpin命令恢复到以前的包版本:

$ swift package unpin —all
$ swift package unpin Foo

Package manager 将每个依赖库的版本固定信息存储在 Package.pins 文件中。如果该文件不存在,则Package manager 会自动创建。

其他

swift package reset 命令将会把 Package 重置干净。

swift test --parallel 命令 执行测试。

其他改动

在 swift 3.1 中还有一些小改动

多重返回函数

C函数返回两次,例如vforkvfork 已经不用了。他们以有趣的方式改变了程序的控制流程。所以 Swift 社区 已经禁止了该行为,以免导致编译错误。

自动链接失效(Disable Auto-Linking)

Swift Package Manager 禁用了在C语言 模块映射(module maps)中的自动链接的功能:

// Swift 3.0
module MyCLib {
header “foo.h"
link “MyCLib"
export *
}

// Swift 3.1
module MyCLib {
header “foo.h”
export *
}

结语

Swift 3.1改善了Swift 3.0的一些功能,为即将到来的Swift 4.0的大改动做准备。这些包括对泛型,正则表达式,更科学的String等方面的作出极大的改进。

如果你想了解更多,请转到 Swift standard library diffs 或者查看官方的的Swift CHANGELOG,您可以在其中阅读所有更改的信息。或者您可以使用它来了解 Swift 4.0 中的内容!

著作权声明

本文译自 What’s New in Swift 3.1?

@柏荧(BY)进行翻译,首次发布于 BY Blog,转载请保留原文链接.

Swift 代理模式

· 阅读需 2 分钟
BY

Xcode 8.2 | Swift 3.0

在iOS开发中,无论是 Objective-C 还是 SwiftDelegate 有着具足轻重的位置,如TabelViewDelegateTableViewDataSource

Swift 中的代理模式 和 Objective-C 除了语法外,几乎一样。

Objective-C 代理模式

在介绍 Swift 代理模式前,先来看回顾一下 Objective-C 中的代理模式如何实现

Objective-C 中用代理实现反向传值:

委托方(子控制器)

委托方需要实现

  • 创建协议 、声明协议方法
		@protocol SubViewDelegate <NSObject>

- (void)backWithStr:(NSString *) str;

@end
  • 创建一个代理属性
		// weak声明
@property (nonatomic, weak) id<SubViewDelegate> delegate;
  • 执行协议方法
		// 判断代理是实现该方法,避免carsh
if ([self.delegate respondsToSelector:@selector(backWithStr:)]) {
[self.delegate backWithStr:self.textField.text];
}

代理方(主控制器)

代理方需要实现

  • 遵守(继承)协议
		@interface ViewController () <SubViewDelegate>```
- 将代理设为自己

subVC.delegate = self;```

  • 实现代理方法
		- (void)backWithStr:(NSString *)str {
self.label.text = str;
}

Swift 代理模式

Swift 代理模式 与 Objective-C 一样,只是语法不同。

Swift 中用代理实现反向传值:

委托方(子控制器)

  • 创建协议 、声明协议方法
		protocol SubViewDelegate {
func backStr(str: String)
} ```
- 创建一个代理属性

var delegate: SubViewDelegate?```

  • 执行协议方法
		/// 执行代理方法,将值回传
delegate?.backStr(str: textField.text ?? "")

代理方(主控制器)

  • 继承协议
		class ViewController: UIViewController, SubViewDelegate```
- 将代理设为自己

subVC.delegate = self

- 实现代理方法

```swift
func backStr(str: String) {
self.textF.text = str
}

总结

对比可以方法 Swift 代理模式 与 Objective-C 用法完全相同,只是语法发生了变化。

值得一提的是Swift 的扩展 extension可以用来继承协议,实现代码隔离,便于维护。

/// 使用扩展继承协议 实现协议方法 可以分离代码
extension ViewController: SubViewDelegate{
/// 实现代理方法
func backStr(str: String) {
self.textF.text = str
}
}

Demo源码

最后附上Demo源码

如果对你有帮助的话,Star✨下一吧!