您是否发现自己无法理解框架或库,并希望您能看到源代码?Apple没有共享源代码,UIKit
但如果您正在努力理解的方法是Swift标准库的一部分,那么您很幸运。
访问GitHub存储库
Apple在公共GitHub存储库中发布Swift编程语言的源代码,包括标准库:
您可能会发现在GitHub上浏览存储库已足以快速查看但我喜欢克隆并下载本地副本:
$ mkdir swift-source
$ cd swift-source/
$ git clone https://github.com/apple/swift.git
Cloning into 'swift'...
remote: Enumerating objects: 915646, done.
remote: Total 915646 (delta 0), reused 0 (delta 0), pack-reused 915646
Receiving objects: 100% (915646/915646), 415.74 MiB | 6.42 MiB/s, done.
Resolving deltas: 100% (742664/742664), done.
Checking out files: 100% (16802/16802), done.
复制代码
您可以将swift
文件夹放入Xcode项目中进行浏览,但这会使我的Mac旋转沙滩球几分钟。我更喜欢用BBEdit打开文件夹,它既快又有更好的多文件搜索工具。
您将在core
子目录中找到标准库的源代码:
$ cd swift/stdlib/public/core
复制代码
值得花时间浏览源代码。我发现它有很好的记录和信息。让我们看一个源代码有助于我们理解的示例。
一个问题 String
我最近写了关于在Swift中测试空字符串的文章。在那篇文章中,我声称你应该使用isEmpty
而不是count
避免迭代整个字符串。一位读者问我这是不是真的?我的说法来自String
文件:
要检查字符串是否为空,请使用其isEmpty属性,而不是将其中一个视图的长度与0进行比较。与isEmpty不同,计算视图的count属性需要迭代字符串的元素。
我们能比信任文档更好吗?我们可以使用标准库源代码来查看字符串的工作方式isEmpty
和count
工作方式吗?
StringCharacterView
在标准库源代码中查找的位置并不总是很明显。搜索core
目录最终会带您到正确的地方,但也可以带走一些兔子洞。有一个String.swift
文件,但它不包含任何一个isEmpty
或count
。
回想一下我的Swift String备忘单,您可能还记得Swift 4将字符串的默认视图设置为一组字符。目录中有一个StringCharacterView.swift
文件core
扩展String
为采用BidirectionalCollection
协议:
// StringCharacterView.swift
extension String: BidirectionalCollection {
复制代码
在StringCharacterView
没有定义isEmpty
,但我们发现count
:
public var count: Int {
return distance(from: startIndex, to: endIndex)
}
复制代码
该distance
方法调用我们仍需要查找的内部方法,但它是一个开始:
public func distance(from start: Index, to end: Index) -> IndexDistance {
return _distance(from: start, to: end)
}
复制代码
BidirectionalCollection
双向集合扩展了集合,以在集合上添加向后遍历:
// BidirectionalCollection.swift
public protocol BidirectionalCollection: Collection
where SubSequence: BidirectionalCollection, Indices:
BidirectionalCollection {
复制代码
这是我们找到实现的地方,_distance
并且可以看到它对集合进行迭代(向前或向后):
internal func _distance(from start: Index, to end: Index) -> Int {
var start = start
var count = 0
if start < end {
while start != end {
count += 1
formIndex(after: &start)
}
}
else if start > end {
while start != end {
count -= 1
formIndex(before: &start)
}
}
return count
}
复制代码
让我们继续前进,看看能否找到isEmpty
。
采集
该Collection
协议的基础上Sequence
,并增加了startIndex
和endIndex
朋友与我们共同的属性isEmpty
和count
:
// Collection.swift (details omitted)
public protocol Collection: Sequence {
var startIndex: Index { get }
var endIndex: Index { get }
var isEmpty: Bool { get }
var count: Int { get }
复制代码
源代码中的注释为我们提供了更多提示:
对于不符合的集合
RandomAccessCollection
,访问该count
属性会迭代集合的元素。
因为count
有这个警告:
复杂性:O(1)如果集合符合
RandomAccessCollection
; 否则,O(n),其中n是集合的长度。
评论确认了我们的理解,String
但也告诉我们为什么会这样。重要的是该系列是否符合RandomAccessCollection
。类似的类型Array
允许随机访问,但String
不允许。有关完整列表,请参阅RandomAccessCollection。
在Collection.swift
文件的下方,我们找到了一个协议扩展,它最终为我们提供了以下的默认实现isEmpty
:
public var isEmpty: Bool {
return startIndex == endIndex
}
复制代码
所以对于a String
,我们最好isEmpty
不要使用而不是count
。