Hi,SwiftGG 翻译组启用了新的域名:swiftgg.team今后翻译组的各项活动将会在新域名下开展,不要错过哦!

作者:shanks

本周共整理了 5 个问题。主要涉及到的知识点有:defer关键字,emoji表情提取,便利构造器的继承,元组的存在感和如何重写didSet。

本周整理问题如下:

对应的代码都放到了 github 上,有兴趣的同学可以下载下来研究:点击下载

Question1: Is “defer” guaranteed to be called in Swift?

Q1链接地址

问题描述

这个问题比较有意思,楼主贴出以下代码,第一个 defer 顺利执行,而第二个 defer 没有执行。中间夹杂着一个 try 语句抛出了异常。

enum SomeError: ErrorType {
case BadLuck
}

func unluckey() throws {
print("\n\tunluckey(💥) -> someone will have a bad day ;)\n")
throw SomeError.BadLuck
}

func callsUnluckey() throws {

print("callsUnluckey() -> OPENING something")
defer {
print("callsUnluckey(😎) -> CLOSEING something")
}

print("callsUnluckey() -> WORKING with something")

try unluckey()
print("callsUnluckey() -> will never get here so chill...")

defer {
print("callsUnluckey(💩) -> why this is not getting called?")
}
}

do {
try callsUnluckey()
} catch {
print("")
print("someone had a bad day")
}

问题解答

官方文档对这个隐藏逻辑应该没有提到,如果是程序运行中,没有运行到 defer 的代码段,在函数结束之前, defer 代码也不会执行。大家可以试试,去掉 try 语句,加上 return 语句也是一样的效果。也就是说。 defer 最好写在 tryreturn 之前。

Question2: How to extract emojis from a string?

Q2链接地址

问题描述

这是一个纯知识点的问题:如何找出字符串中的 emoji 表情?

问题解答

直接看代码注释:

// 获取字符编码对应的数值
extension Character {
private var unicodeScalarCodePoint: Int {
let characterString = String(self)
let scalars = characterString.unicodeScalars
return Int(scalars[scalars.startIndex].value)
}
}

//扩展定义字符串的一个计算属性,得到字符串中的emoji字符数组。
extension String {
var emojis:[Character] {
let emojiRanges = [0x1F601...0x1F64F, 0x2702...0x27B0] // emoji 的范围,可以自己扩充
let emojiSet = emojiRanges.reduce(Set<Int>()) { (result, elm) -> Set<Int> in
return result.union(elm)
}
return self.characters.filter { emojiSet.contains($0.unicodeScalarCodePoint) }
}
}

let sentence = "😃 hello world 🙃"
sentence.emojis // ["😃", "🙃"]

Question3: Subclassing a class that has convenience initializers.

Q3链接地址

问题描述

请看代码描述:

class BaseClass {
var property1 = 0

init(property1: Int) {
self.property1 = property1
}

convenience init(value1: Int, value2: Int) {
self.init(property1: value1 + value2)
}
}

class SubClass: BaseClass {
var property2: Int?

init(property2: Int) {
super.init(property1: 10)
self.property2 = property2
}
}

let c1 = SubClass(property2: 4)
print(c1.property2!)

// 以下代码报错
let c2 = SubClass(value1: 5, value2: 5)
print(c2.property1)

问题解答

Swift 官方教程里面说明了,如果要继承父类的便利构造器,那么子类需要重写所有父类的指定构造器,把代码变成下面的样子就行啦:

class SubClass1: BaseClass {
var property2: Int?

override init(property1: Int) {
super.init(property1: 10)
self.property2 = property1
}
}

let c3 = SubClass1(property1: 4)
print(c3.property2!)

// 以下代码报错
let c4 = SubClass1(value1: 5, value2: 5)
print(c4.property1)

Question4: Tuple vs Struct in Swift

问题链接

Q4链接地址

问题描述

楼主的问题:结构体也可以定义多个属性,而且看起来更加间接,为啥还有元组( Tuples )这样的东西存在?

问题解答

跟帖中提到了2点,来试图解释 Tuples 的作用,仅供参考:

  • 第一点:要实现一个结构体的对象的 “=” 操作符,那我们需要去显式的实现它,比如以下的例子:
struct Foo {
var a : Int = 1
var b : Double = 2.0
var c : String = "3"
}

var a = Foo()
var b = Foo()
func == (lhs: Foo, rhs: Foo) -> Bool {
return lhs.a == rhs.a && lhs.b == rhs.b && lhs.c == rhs.c
}

a == b

而在即将到来的 Swift 2.2 中,有一个关于元组的提议,已经被实现了,请参见Tuple comparison operators, 带来的变化是:
如果元组的中的元素个数 <=6,那么编译器会帮你解决元组比较的问题,前提是元素中的满足 Equatable 协议,如果满足 Comparable 协议,甚至是支持 < , <=, >= , > 等比较操作符。见以下例子:

/* 以下代码将在 Swift 2.2 中实现,不用自己实现 */
@warn_unused_result
public func == <A: Equatable, B: Equatable, C: Equatable>(lhs: (A,B,C), rhs: (A,B,C)) -> Bool {
return lhs.0 == rhs.0 && lhs.1 == rhs.1 && lhs.2 == rhs.2
}
/* 更多的比较 ... */

var aa = (1, 2.0, "3")
var bb = (1, 2.0, "3")

aa == bb // true
aa.0 = 2
aa == bb // false
  • 第二点:Tuples 的访问可以更加灵活,可以使用 .0,.1这样的访问方式,也可以使用标签。而结构体只能访问定义的属性。

Question5: Overriding didSet

问题链接

Q5链接地址

问题描述

楼主的问题:为神马不能重写 didSet? 见以下代码:

class Foo {
var name: String = "" { didSet { print("name has been set") } }
}

let foo = Foo();
foo.name = "test"
class Bar: Foo {
override var name: String = "" {
didSet {
print("print this first")
// print the line set in the superclass
}
}
}

问题解答

实际上,不是不能重写,在重写时报错:具有 gettersetter 的变量不能有初始值。适用于子类重写属性的情况。不过这个错误提示有一些歧义,在父类中,其实是可以赋初始值的,见以下代码:

class Foo {
var name: String = "" { didSet { print("name has been set") } }
}

class Bar: Foo {
override var name: String {
didSet {
print("print this first")
// print the line set in the superclass
}
}
}

let bar = Bar()
bar.name = "name"

更多关于继承属性和 didSet 的介绍,见官方教程

文章目录
  1. 1. Question1: Is “defer” guaranteed to be called in Swift?
    1. 1.1. 问题描述
    2. 1.2. 问题解答
  2. 2. Question2: How to extract emojis from a string?
    1. 2.1. 问题描述
    2. 2.2. 问题解答
  3. 3. Question3: Subclassing a class that has convenience initializers.
    1. 3.1. 问题描述
    2. 3.2. 问题解答
  4. 4. Question4: Tuple vs Struct in Swift
    1. 4.1. 问题链接
    2. 4.2. 问题描述
    3. 4.3. 问题解答
  5. 5. Question5: Overriding didSet
    1. 5.1. 问题链接
    2. 5.2. 问题描述
    3. 5.3. 问题解答