跳到主要内容

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 才可以。

单向区间​

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

例如

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 实例。Substring 与 String 用法相同, 因为子串和原字符串共享内存,所以对子串的操作快速而且高效。

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 新增加的数据类型。

定义两个结构体 Person与Book

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)!

可以通过 AppStore 或 Apple 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

这创建了一个采用 String 的 StringProvider 协议而在你使用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. f 与 g 进入函数后由非逃逸状态,分别转换为逃逸闭包:escapableF和escapableG。
  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函数返回两次,例如vfork 和 vfork 已经不用了。他们以有趣的方式改变了程序的控制流程。所以 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 还是 Swift ,Delegate 有着具足轻重的位置,如TabelViewDelegate 与 TableViewDataSource。

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✨下一吧!