从 金沙官网线上:Swift 中的序列到类型擦除

1、创建序列

struct _AnySequence<Element>: Sequence { private var iteratorImpl: () -> Element?}

     alter sequence dbo.序列名

struct _Iterator: IteratorProtocol { var children: Mirror.Children init { children = Mirror(reflecting: obj).children } mutating func next() -> String? { guard let child = children.popFirst() else { return nil } return "(child.label.wrapped) is (child.value)" }}protocol Sequencible: Sequence { }extension Sequencible { func makeIterator() -> _Iterator { return _Iterator(obj: self) }}

end

_AnySequence 的指定构造器也被定义为泛型,接受一个遵循 Sequence 协议的任何序列作为参数,并且规定了这个序列的迭代器的 next() 的返回类型要跟我们定义的这个泛型结构的 Element 类型要一致。这里的这个泛型约束其实就是我们实现类型擦除的魔法所在了。它将具体的序列的类型隐藏了起来,只要序列中的值都是相同的类型就可以当做同一种类型来使用。就像下面的例子中的 array 就可以描述为 "元素类型是 String 的任意序列的集合"。

     restart with 0;

要实现这样的需求,我们需要让自定义的类型遵守 Sequence 协议。

create procedure proDemo

满足 Sequence 协议的要求十分简单,你需要做的所有事情就是提供一个返回迭代器 的 makeIterator() 方法:

金沙官网线上,as

在标准库中,其实已经提供了 AnyIteratorAnySequence。我还没去看标准库的实现,有兴趣的同学可以点击这里查看。 我这里实现了自己的 _AnyIterator 和 _AnySequence 就是为了提供一种实现类型擦除的思路。如果你在项目中频繁地使用带有关联类型或 Self 的协议,那么你也一定会遇到跟我一样的问题。这时候实现一个类型擦除的封装,将具体的类型隐藏了起来,你就不用为 Xcode 的报错而抓狂了。

 

现在需求又变了,我想将所有遵守了 Sequencible 协议的任何序列存到一个数组中,然后 for 循环遍历数组中的元素,因为数组中的元素都遵守了 Sequencible 协议,所以又可以使用 for 循环输出其所有属性,就像下面这样:

3、创建定时任务自动执行序列初始化存储过程。

Persion 遵守了 Sequence 协议,并返回了一个自定义的迭代器。迭代器的实现也很简单:

2、序列初始化存储过程

对于刚刚上面的那个数组就可以这样初始化了:

begin

回想一下 Sequence 协议的内容,我们只要通过 makeIterator() 返回一个迭代器就可以了。那么我们可以实现一个封装类,里面用一个属性存储了迭代器的实现,然后在 makeIterator() 方法中通过存储的这个属性构造一个迭代器。类似这样:

上面的代码中通过一个闭包初始化了一个 _AnySequence,这里我就不给出自己的实现,同学们可以自己动手实现一下。

我们希望外面传入一个闭包也能创建一个 _AnyIterator,现在我们添加下面的代码:

关联类型 Element 指定了迭代器产生的值的类型。这里next() 被标记了 mutating,表明了迭代器是可以存在可变的状态的。这里的 mutating 也不是必须的,如果你的迭代器返回的值并没有改变迭代器本身,那么没有 mutating 也是没有任何问题的。 不过几乎所有有意义的迭代器都会要求可变状态,这样它们才能够管理在序列中的当前位置。

得益于 Swift 的类型推断,这里的 array 可以不用显式地指明其类型,点击 option 键,你会发现它是 [_AnySequence<String>] 类型。也就是说只有其元素是 String 的任意序列都可以作为数组的元素。这就跟我们平时使用类似 "一个 Int 类型的数组" 的语义是一致的了。如果要向数组中插入一个新元素,可以这样创建一个序列:

func makeIterator() -> _AnyIterator<Element> { return _AnyIterator(iteratorImpl)}
struct Demo: Sequencible { var name = "Sequence" var author = Persion.author}

迭代器中的 childrenAnyCollection<Mirror.Child> 的集合类型,每次迭代返回一个值后,更新 children 这个状态,这样我们的迭代器就可以持续的输出正确的值了,直到输出完 children 中的所有值。

_AnyIterator 实现完后就可以来实现我们的 _AnySequence 了。我这里直接给出代码,同学们可以自己去实现:

现在我们可以一步步来实现上面的 _AnyIterator 和 _AnySequence。_AnyIterator 的实现跟上面提到的 _AnySequence 的思路一致。我们不直接存储迭代器,而是让封装类存储迭代器的 next 函数。要做到这一点,我们必须首先将 iterator 参数复制到一个变量中,这样我们就可以调用它的 next 方法了。下面是具体实现:

那么这里的 array 应该定义成什么类型呢?定义成 [Any] 类型肯定是不行的,这样的话在循环中得将 item 强转为 Sequencible,那么是否可以定义成 [Sequencible] 类型呢?答案是否定的。当这样定义时编辑器会报出这样的错误:

对 Sequence 和 IteratorProtocol 有了基础了解后,要实现开头提到的需求就很简单了。比如我想迭代输出一个 Person 实例的所有属性,我们可以这样做:

现在可以使用 for 循环输出 Persion 中所有的属性值了:

public protocol IteratorProtocol { associatedtype Element public mutating func next() -> Self.Element?}
let sequencibleStore: [Sequencible] = [Persion.author, Demo()]
struct MyIterator: IteratorProtocol { var children: Mirror.Children init(obj: Persion) { children = Mirror(reflecting: obj).children } mutating func next() -> String? { guard let child = children.popFirst() else { return nil } return "(child.label.wrapped) is (child.value)" }}
struct Persion: Sequence { var name: String var age: Int var email: String func makeIterator() -> MyIterator { return MyIterator(obj: self) }}

本文由金沙官网线上发布于数据库,转载请注明出处:从 金沙官网线上:Swift 中的序列到类型擦除

您可能还会对下面的文章感兴趣: