您的位置:金沙游乐场85155 > 大数据库 > 从 Swift 中的序列到类型擦除

从 Swift 中的序列到类型擦除

发布时间:2019-12-06 00:53编辑:大数据库浏览(158)

    1、创建序列

    图片 1Swift.png

    2、序列初始化存储过程

    如果有这样的一个需求,我希望能像数组一样,用 for 循环遍历一个类或结构体中的所有属性。就像下面这样:

    create procedure proDemo

    let persion = Persion()for i in persion { print}
    

    as

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

    begin

    Sequence 协议是集合类型结构中的基础。一个序列 代表的是一系列具有相同类型的值,你可以对这些值进行迭代。Sequence 协议提供了许多强大的功能,满足该协议的类型都可以直接使用这些功能。上面这样步进式的迭代元素的能力看起来十分简单,但它却是 Sequence 可以提供这些强大功能的基础。

         alter sequence dbo.序列名

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

         restart with 0;

    public protocol Sequence { associatedtype Iterator : IteratorProtocol public func makeIterator() -> Self.Iterator // ...}
    

    end

    在 Sequence 协议有个关联类型 Iterator,而且它必须遵守 IteratorProtocol 协议。从这里我们可以看出 Sequence 是一个可以创建迭代器协议的类型。所以在搞清楚它的步进式的迭代元素能力之前,有必要了解一下迭代器是什么。

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

    序列通过创建一个迭代器来提供对元素的访问。迭代器每次产生一个序列的值,并且当遍历序列时对遍历状态进行管理。在 IteratorProtocol 协议中唯一的一个方法是 next(),这个方法需要在每次被调用时返回序列中的下一个值。当序列被耗尽时,next() 应该返回 nil,不然迭代器就会一直工作下去,直到资源被耗尽为止。

     

    IteratorProtocol 的定义非常简单:

    public protocol IteratorProtocol { associatedtype Element public mutating func next() -> Self.Element?}
    

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

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

    struct Persion: Sequence { var name: String var age: Int var email: String func makeIterator() -> MyIterator { return MyIterator(obj: self) }}
    

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

    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)" }}
    

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

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

    for item in Persion.author { print}// out put:// name is jewelz// age is 23// email is hujewelz@gmail.com
    

    如果现在有另外一个结构体或类也需要迭代输出所以属性呢?,这很好办,让我们的结构体遵守 Sequence 协议,并返回一个我们自定义的迭代器就可以了。这种拷贝代码的方式确实能满足需求,但是如果我们利用协议拓展就能写出更易于维护的代码,类似下面这样:

    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) }}
    

    这里我定义了一个继承 Sequence 的空协议,是为了不影响 Sequence 的默认行为。现在只要我们自定义的类或结构体遵守 Sequencible 就能使用 for 循环输出其所有属性值了。就像下面这样:

    struct Demo: Sequencible { var name = "Sequence" var author = Persion.author}
    

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

    for obj in array { for item in obj { print }}
    

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

    本文由金沙游乐场85155发布于大数据库,转载请注明出处:从 Swift 中的序列到类型擦除

    关键词: