前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Swift: 有用的标准库全局函数

Swift: 有用的标准库全局函数

作者头像
韦弦zhy
发布2020-05-18 16:29:29
2.6K0
发布2020-05-18 16:29:29
举报

全局函数 Global functions无需特定类型范围就可以从任何地方访问的函数是一个古老的概念,在 C 和 Objective-C 等语言中很流行,但是在 Swift 中不建议使用,因为我们希望对它们进行很好的类型化和范围划分("swifty")。

global function

由于历史原因,Swift 标准库中仍然具有相当多的公共全局功能,其中一些功能至今仍然非常有用。让我们看一下zip()dump()之类的函数。

zip()

zip函数也许是最著名的全局函数,它使您可以采用两个或多个数组并将它们合并为一个元组序列。如果您需要同时迭代两件事,这将非常有用,因为如果没有zip,则必须手动构建一个for循环并分别访问每个数组中的每个索引。使用zip可以使您以更实用的for-in方式访问所有数组中的元素。

例如,如果我们有一个用户注册表单界面,并且我们想更新我们的textFields以呈现从后端获取的验证结果的列表,我们可以执行以下操作:

代码语言:javascript
复制
func present(validationResults: [FieldValidationResult],
             inTextFields textFields: [MyTextField]) {
    for i in 0..<textFields.count {
        let field = textFields[i]
        let result = validationResults[i]
        field.render(validationResult: result)
    }
}

使用zip,我们可以删除所有手动索引。

代码语言:javascript
复制
func present(validationResults: [FieldValidationResult],
             inTextFields textFields: [MyTextField]) {
    for (field, result) in zip(textFields, validationResults) {
        field.render(validationResult: result)
    }
}

zip的返回类型是符合SequenceZip2Sequence对象,因此所有其他与序列相关的方法都适用于它,包括将其转换为真正的数组。

dump()

dump函数可以很好地替代打印对象。尽管打印对象只是类型的descriptiondebugDescription属性的语法糖,而dumpMirror(reflecting :)的增强版本,它使用反射来打印对象的内容,这通常会产生更多信息,包括对象的层次结构。

代码语言:javascript
复制
class Foo: NSObject {
    let bar: String = "bar"
}

let foo = Foo()
print(foo)
// <SwiftRocks.Foo: 0x1030b9250>

dump(foo)
// ▿ <SwiftRocks.Foo: 0x1030b9250> #0
//     - super: NSObject
//    - bar: "bar"

sequence()

全局sequence()函数有点晦涩,但是它是一个非常酷的函数,可让您以更好的语法编写递归函数。

假设我们要更改子视图及其所有父视图的背景颜色。也许您会像这样建立一个while循环:

代码语言:javascript
复制
var currentView: UIView? = self
while currentView != nil {
    currentView?.backgroundColor = .green
    currentView = currentView?.superview
}

这是sequence()的最佳用例,因为此函数的目的是为您提供一个序列,该序列反复应用特定的闭包。由于此方法的递归内容 currentView = currentView?.superview 始终相同,因此我们可以使用sequence()将其转换为简单的for循环:

代码语言:javascript
复制
for view in sequence(first: self, next: { $0.superview } ) {
    view.backgroundColor = .green
}

它的工作方式是sequence()返回自定义的UnfoldFirstSequence类型,这是Sequence的简单包装,该包装不断在其next()函数中反复应用闭包。

isKnownUniquelyReferenced()

isKnownUniquelyReferenced函数接收一个类对象,并返回一个布尔值,该布尔值指示该对象是否仅被引用了一次,目的是使您能够对引用类型实现值语义。尽管结构本身就是值类型,但其中的内容可能不是。您可能知道,将类放入结构体并不意味着它将在赋值时复制:

代码语言:javascript
复制
class Foo: NSObject {
    var bar: String = "bar"
}

struct FooHolder {
    let foo: Foo = Foo()
    var intValue: Int = 1
}

var fooHolder = FooHolder()
var fooHolder2 = fooHolder

fooHolder2.foo.bar = "bar2"
fooHolder2.intValue = 2

print(fooHolder.intValue)
// 1
print(fooHolder2.intValue)
// 2

print(fooHolder.foo.bar)
// bar2
print(fooHolder2.foo.bar)
// bar2

在此示例中,尽管fooHolder2及其基础编号是与原始持有人不同的实体,但是基础类仍在它们之间共享。为了解决这个问题,我们可以使用isKnownUniquelyReferenced检测何时访问此属性,并在必要时创建该类的新实例:

代码语言:javascript
复制
struct FooHolder {
    private var _foo: Foo = Foo()

    var foo: Foo {
        mutating get {
            if isKnownUniquelyReferenced(&_foo) {
                return _foo
            } else {
                let newFoo = Foo()
                newFoo.bar = _foo.bar
                _foo = newFoo
                return _foo
            }
        } set {
            _foo = newValue
        }
    }

    var intValue: Int = 1
}

您可能有兴趣知道,这正是 Swift 标准库如何实现对数组和字符串的写时复制(copy-on-write)语义的实现——我在有关值类型的内存管理的文章中已经提到了这一点

repeatElement()

repeatElement()完全就是他表面上的意思。给定一个对象和一个数字,结果是一个可以重复的序列,为您提供该对象特定次数的数量。

代码语言:javascript
复制
let repeated: Repeated<String> = repeatElement("SwiftRocks", count: 3)
for value in repeated {
    print(value)
}
//SwiftRocks
//SwiftRocks
//SwiftRocks

重复元素是Swift中的常见操作,尤其是填补StringsArrays中的空白。实际上,大多数这些类型甚至为此都有一个特定的初始化程序:

代码语言:javascript
复制
let array = [Int](repeating: 0, count: 10)

那么,为什么要使用repeatElement?原因是性能。repeatElement()的返回类型是Repeated<T>序列类型,类似于Zip2Sequence,它除了提供此“重复”功能外不执行任何操作。假设您想用另一个数字替换数字数组的特定部分;实现此目的的一种方法是将replaceSubrange与另一个数组一起使用:

代码语言:javascript
复制
array.replaceSubrange(2...7, with: [Int](repeating: 1, count: 6))
print(array)
// [0, 0, 1, 1, 1, 1, 1, 1, 0, 0]

在这种情况下,[Int](repeating:)的使用带来了必须初始化数组缓冲区的所有开销,而这在这里毫无用处。如果只需要重复功能,则使用repeatElement会更好:

代码语言:javascript
复制
array.replaceSubrange(2...7, with: repeatElement(1, count: 6))

stride()

同样非常流行的是,将stride()函数添加到Swift中,作为一种创建可以跳过某些元素的循环的方法,因为从swift 语言中删除了等效的 C 样式方法:

代码语言:javascript
复制
for (int i = 0; i < 10; i += 2) { ... }

现在,您可以使用stride()实现相同的行为:

代码语言:javascript
复制
for i in stride(from: 0, to: 10, by: 2) {
    //从0到9,跳过奇数。
}

stride()的参数是符合Strideable协议的参数,该协议表示可以表示距离概念的对象。例如,这是我们如何在Date对象中添加“日差”的概念,以便可以在stride()中使用它们:

代码语言:javascript
复制
extension Date: Strideable {
    func advanced(by n: Int) -> Date {
        return Calendar.current.date(byAdding: .day, value: n, to: self)!
    }

    func distance(to other: Date) -> Int {
        return Calendar.current.dateComponents([.day], from: other, to: self).day!
    }
}

let startDate = Date()
let finalDate = startDate.advanced(by: 5)

for date in stride(from: startDate, to: finalDate, by: 1) {
    print(date)
}
// March 24th
// March 25th
// March 26th
// March 27th
// March 28th

(请注意,Date已经实现了Strideable方法的实现,该实现可以在几秒钟内完成,因此将其复制到项目中将不起作用。) PS: 稍微修改一下,Date默认实现了distanceadvanced所以只需要补上一个:

代码语言:javascript
复制
/// A type that represents the distance between two values.
associatedtype Stride : Comparable, SignedNumeric

所以代码如下:

代码语言:javascript
复制
extension Date: Strideable {
    typealias step = TimeInterval
}

let startDate = Date()
let finalDate = startDate.advanced(by: 5)

for date in stride(from: startDate, to: finalDate, by: 1) {
    print(date)
}
//2020-05-06 12:48:15 +0000
//2020-05-06 12:48:16 +0000
//2020-05-06 12:48:17 +0000
//2020-05-06 12:48:18 +0000
//2020-05-06 12:48:19 +0000

其他有用的函数

Math

  • max()返回参数的最大值
  • min()返回参数的最小值
  • abs()返回参数的绝对值(在竞争性编程问题中很有用)

Values

swap()交换两个对象的值。本文未在本节中单独提及,因为如果需要交换数组元素,则使用的正确方法是Array.swapAt()。但是,在需要创建伪“aux”属性来保存值的其他情况下,仍然可以使用swap()

结论

我们可以看到,尽管这些方法都不是使事情发生的必要方法,但是使用它们可以使您编写的代码比以前的解决方案更易于维护,并且有时甚至可以提高性能。

译自

Useful Global Swift Functions

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • zip()
  • dump()
  • sequence()
  • isKnownUniquelyReferenced()
  • repeatElement()
  • stride()
  • 其他有用的函数
    • Math
      • Values
      • 结论
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档