文章目录
  1. 1. reduce 方法需要忽略传入参数的情况
  2. 2. 字面量转换的使用
  3. 3. 关于 DarwinBoolean
  4. 4. 泛型类型的初始化问题

学习喵神的做法,去苹果官方论坛 Swift 版和 Stackoverflow 中逛逛,整理一些有趣的问题,每周末整理成文,放到 blog 中,也算是一个积累。要有工匠精神,那就从现在开始吧。
第一次做,找了几个简单一些问题,算是一个好的开始:

reduce 方法需要忽略传入参数的情况

1
2
3
4
5
6
func replicateWithReduce<T>(elem: T, count: Int) -> [T]
{
return (0 ..< count).reduce([T]()) { return $0.append(elem) }
}
// 编译错误:Cannot convert value of type '[T]' to expected argument type '[_]'

这是为什么呢?

  • 问题解答:
    首先让我们来回归一下,reduce 方法的定义:
1
reduce(initial: U, combine: (U, T) -> U) -> U

可以看到,reduce 函数的闭包参数 combine 会输入两个参数,U,T 类型在闭包函数中做了处理以后,返回 U 类型。

上例中,使用 $0 来表示第一个输入参数 U,这是闭包表达式的一种,匿名参数表示。这里的问题是, T 去哪儿了?

本实例中,由于遍历的数组(0 ..< count)中的元素不需要传入到闭包中,只是为了计数循环而已。所以这里的第二个参数 T 就不会传入到闭包定义中进行处理。上例中也就省略了 $1 这个表示。

但是在 Swift 当中,如果定义的闭包,具有 2 个输入参数的时候,在闭包定义中,只使用 $0 一个匿名参数的情况下,编译器会认为这个闭包是单参数闭包,和原来的 2 个参数闭包不一致。

帖子中也顺便吐槽了一下苹果糟糕的出错提示。我也没看懂是想表达啥意思。

找到了问题,也好解决了,那就是不需要匿名参数咯。然后不需要使用第二个参数,那就使用_代替吧:

1
2
3
4
5
6
func replicateWithReduce<T>(elem: T, count: Int) -> [T]
{
return (0 ..< count).reduce([T]()) { arr, _ in
return arr + [elem]
}
}

我也想到一种比较tricky的方式:

1
2
3
4
5
6
7
func replicateWithReduce<T>(elem: T, count: Int) -> [T]
{
return (0 ..< count).reduce([T]()) {
$1
return $0 + [elem]
}
}

字面量转换的使用

泛型函数gimmeAZero的类型 T 遵循IntegerLiteralConvertible协议。提问者查看IntegerLiteralConvertible时,发现定义了一个别名和初始化函数,于是就想到使用初始化方法构造一个返回值,结果报错:

1
2
3
4
5
6
7
8
9
public protocol IntegerLiteralConvertible {
typealias IntegerLiteralType
/// Create an instance initialized to `value`.
public init(integerLiteral value: Self.IntegerLiteralType)
}
func gimmeAZero<T: IntegerLiteralConvertible>() -> T {
return T(integerLiteral: 0) // error: Cannot invoke initializer for type 'T' with an argument list of type '(integerLiteral: Int)'
}
  • 问题解答:

在字面量转换的每个具体协议中,别名都被Swift标准库已经有了定义,如本例子当中的IntegerLiteralType:

1
typealias IntegerLiteralType = Int

所以在具体的协议中,不需要再去定义别名。

遵循此协议的结构体或者类只需要实现初始化方法即可。实际使用中,只需要使用赋值语句即可,在本例的函数定义中,返回是一个 T 类型,遵循IntegerLiteralConvertible协议,于是,只需要返回 0 即可:

1
2
3
func gimmeAZero<T: IntegerLiteralConvertible>() -> T {
return 0
}

关于 DarwinBoolean

  • 原文链接:What is DarwinBoolean type in Swift

  • 问题描述:
    什么是 DarwinBoolean?如何使用?

  • 问题解答:
    stackoverflow 高手如云,问题出来20分钟以后,就有人做了详细的解答。简单点说,DarwinBoolean就是 Swift 中去映射 C 语言中的Boolean的类型。当 C 函数包含指向Boolean的指针时,Swift会将这个指针,转换成 UnsafeMutablePointer<DarwinBoolean>。注意这里的Boolean对应是 Mac OS 平台的。C 语言中的bool类型对应 Swift 中的类型是CBool。具体可以查看苹果的官方文档

关于DarwinBoolean的使用,本问题中后面的解答有详细的说明。这个知识点比较少用到。这里做一个记录,以后用到时候可以回看。这里就不展开了。

泛型类型的初始化问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct Point<Element> {
var x : Element! = nil
var y : Element! = nil
init(_ x : Element) {
self.x = x
}
init(_ x : Element, _ y : Element) {
self.x = x
self.y = y
}
}
let p = Point(1, 2) // this produces an Ambiguous use of init compile error
  • 问题解答:

对于泛型类型来说,在初始化时候应该指定类型,Swift 编译器并不会根据传入的参数去推断Point 的具体类型, 需要显式指定泛型的具体类型。 所以,以下代码才能运行:

1
let p = Point<Int>(1, 2)
文章目录
  1. 1. reduce 方法需要忽略传入参数的情况
  2. 2. 字面量转换的使用
  3. 3. 关于 DarwinBoolean
  4. 4. 泛型类型的初始化问题