swift中的自定义集合:它是正确的方式吗?

时间:2016-05-05 13:35:13

标签: ios swift2.2 custom-collection

我正在迅速学习。我想使用一个自定义类来循环[能够在...循环中]像Array一样。下面是给定的示例代码,到目前为止,我已经尝试过了。有问题的类是“GuestManager”,它持有私人的客人集合[类Guest的对象]

import Foundation

class Guest{
    var guestId: String
    var guestName: String

    init(gId: String, name: String){
        self.guestId = gId
        self.guestName = name
    }
}

class GuestManager: GeneratorType, SequenceType{
    private var guests = [Guest]?()
    private var nextIndex: Int

    init(guests: [Guest]){
        self.guests = guests
        self.nextIndex = 0
    }

    func next() -> Guest? {
        if  self.nextIndex > (self.guests?.count)! - 1 {
            self.nextIndex = 0
            return nil
        }
        let currentGuest = self.guests![self.nextIndex]
        self.nextIndex += 1
        return currentGuest
    }
    subscript(aGuestId gID: String) -> Guest{
        return (self.guests?.filter({$0.guestId == gID}).first)!
    }
}

我不想创建符合GeneratorType&的单独类。 SequenceType协议。相反,我创建了一个符合这两种协议的类。

以下是我的一些问题:

  1. 我想知道这是否是一种自定义集合类型的正确方法?

  2. 我们可以使用下标作为基于属性执行搜索的方法,例如上面的示例代码中的“下标(aGuestId gID:String)”吗?

  3. 从上面示例代码中的next()函数实现的代码中可以清楚地看到,当迭代到达时,重置了“nextIndex”。如何处理我们在for ... in循环中使用break语句的情况,如下所示:

    for aGuest in guestManager{//guestManager an instance of GuestManager class instantiated with several guest objects
        print(aGuest.guestName)
    }
    for aG in guestManager{
        print(aG.guestId)
        break
    }
    

    在第二个for循环中,代码在获取第一个Element [Guest对象(在本例中)之后)中断。后续for循环将从集合中的索引1开始而不是0.是否仍然处理此中断情况,以便对于每个后续循环,索引始终设置为0?

  4. 由于

    编辑:似乎可以使用以下代码[在GuestManager类中添加]修复“nextIndex”重置问题,以便生成()方法实现

    func generate() -> Self {
            self.nextIndex = 0
            return self
        }
    

2 个答案:

答案 0 :(得分:2)

您不应将nextIndex存储在课程中。您可以在generate方法中使用局部变量,然后让您传递给在该方法中创建的生成器的闭包捕获该变量。这就是你需要采用SequenceType

class GuestManager: SequenceType{
    private var guests: [Guest]

    init(guests: [Guest]) {
        self.guests = guests
    }

    func generate() -> AnyGenerator<Guest> {
        var nextIndex = 0
        return AnyGenerator {
            guard nextIndex < self.guests.endIndex else {
                return nil
            }
            let next = self.guests[nextIndex]
            nextIndex += 1
            return next
        }
    }
}

对于下标,您应该采用Indexable。实际上,满足所有要求的最简单方法是将尽可能多的SequenceTypeIndexable和最终(如果您想支持它)CollectionType的逻辑传递给您的数组,已经具备这些功能。我会这样写:

class GuestManager {
    private var guests: [Guest]

    init(guests: [Guest]){
        self.guests = guests
    }
}

extension GuestManager: SequenceType {
    typealias Generator = IndexingGenerator<GuestManager>

    func generate() -> Generator {
        return IndexingGenerator(self)
    }
}

extension GuestManager: Indexable {
    var startIndex: Int {
        return guests.startIndex
    }

    var endIndex: Int {
        return guests.endIndex
    }

    subscript(position: Int) -> Guest {
        return guests[position]
    }
}

更多观察结果:

  1. 您的guests属性不应是可选的。它使代码更复杂,没有任何好处。我在代码中相应地更改了它。
  2. 您的Guest类应该是值类型(结构)。 GuestManager也是结构的一个很好的候选者,除非你需要一个类的引用语义(标准库中的所有集合类型都是结构)。

答案 1 :(得分:0)

我认为你在这里尝试的下标方法有点令人费解。就个人而言,为了清楚起见,我会使用一个函数来执行此操作。

guestManager[aGuestId: guestId]

guestManager.guestWithID(guestId) 

所以风格上我可能会落在这样的东西上

import Foundation

class Guest{
  var guestId: String
  var guestName: String

  init(guestId: String, guestName: String){
    self.guestId = guestId
    self.guestName = guestName
  }
}

class GuestManager: GeneratorType, SequenceType{
  private var guests: [Guest]
  private var nextIndex = 0

  init(guests: [Guest]){
    self.guests = guests
  }

  func next() -> Guest? {
    guard guests.count < nextIndex else {
      nextIndex = 0
      return nil
    }

    let currentGuest = guests[nextIndex]
    nextIndex += 1
    return currentGuest
  }

  func guestWithID(id: String) -> Guest? {
    return guests.filter{$0.guestId == id}.first ?? nil
  }
}