Swift 流利的 api 带有自定义运算符的设计

Swift fluent-like api design with custom operators

我想用自定义运算符和函数编写一些类似流利的 api。这是我正在寻找的示例:

var result = [Section]()

result +++= Section()
    .appendTitle(title)
    .appendPhotos(photos)

result +++= Section()
    .appendSomething(sth)
    .appendPhotos(photos)
    .appendAnything()

return result

为此,我声明了两个自定义运算符:

infix operator +++{ associativity left precedence 95 }

func +++(lhs : [Section], rhs : Section) -> [Section] {
    var result = lhs
    result.append(rhs)
    return result
}

infix operator +++= { associativity left precedence 95 }

func +++=(inout lhs : [Section], rhs : Section) {
    lhs = lhs +++ rhs
}

当然还有 Section 结构的正确扩展:

extension Section {
    mutating func appendTitle(title: String?) -> Section {
        guard let unwrappedTitle = title
            else { return self }

        ...

        return self
    }

    mutating func appendPhotos(photos: [Photo]?) -> Section {
        ...
    }

    ...
}

不幸的是,它没有按预期工作...

单独的 result +++= Section() 行是正确的,但是当我添加 .append 时它无法编译。

第一条消息是:Passing value of type '[Section]' to an inout parameter requires explicit '&'

然后我试着在result前面放一个&(但我从来没有为a += 1做过)有第二条消息:Cannot use mutating member on immutable value: function call returns immutable value

因此,如果有人可以提供帮助,我们将不胜感激。

我正在使用 Swift 2.2 和 Xcode 7.

找到了!!!

这些方法不应该发生变化:

func appendTitle(title: String?) -> Section {
    guard let unwrappedTitle = title
        else { return self }

    var result = self

    ... // Work with result instead of self!!! 

    return result
}

好吧,您找到了解决方案。太棒了!

但是请允许我解释一下这里到底发生了什么。

让我们仔细看看您之前的实施。

extension Section {
    mutating func appendTitle(title: String?) -> Section {

        guard let unwrappedTitle = title else { return self }
        self.title = unwrappedTitle
        return self
    }

    mutating func appendPhotos(photos: [Photo]?) -> Section {
        guard let unwrappedPhotos = photos else { return self }
        self.photos = unwrappedPhotos
        return self
    }
}

我们知道(至少现在)运算符重载函数绝对没问题,没有错。

给定的扩展正在改变参数的值,因此它应该是变异的,确实是。那为什么会报错:

"Cannot use mutating member on immutable value: function call returns immutable value"

原因:当调用"appendTitle"或"appendPhotos"时,它return是self的一个副本。不完全相同的引用!

这是因为 swift 处理不变性的方式。如果某个值类型的成员 属性 的值发生更改,则会创建一个副本,并将该值分配给相应的成员。

因此,当你写:Section().appendTitle(title).appendPhotos(photos)

Section() returns 是 self 的副本,即 Section 的不可变版本。 因此,人们不能 appendTitle 它,或者一般不能改变它。

可以解决:

var section = Section() section.appendTitle(title).appendPhotos(photos)

但现在 appendTitle(title) return 是 self 的副本,即 Section 的不可变版本。同样的问题仍然存在。

您所做的是,您没有使用 self 更改任何内容,而是创建了一个新实例,这次是使用 var 的可变实例。对其进行变异并 return 对其进行编辑。现在它不会改变自己,因此它不必是 mutating func.

您提供的解决方案在这里工作得很好,但实际上它是一个 hack,这里需要它来涵盖我现在在 Swift 中所说的 "bug"。

其他解决方案可以将 struct Section 更改为 class Section。在那里您不需要创建 var result,只需更改属性和 return.

这是我在 SO 上的第一个回答:)