Собственная коллекция в swift: это правильный путь?
Я учусь быстро. Я хотел бы использовать пользовательский класс для цикла [возможность цикла for... in], например, 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. Вместо этого я создал один класс, соответствующий обоим протоколам.
Ниже приведены некоторые из моих вопросов:
Я хотел бы знать, если это правильный способ иметь пользовательский тип коллекции?
Можем ли мы использовать подстрочный индекс как способ выполнения поиска на основе свойства, например "subscript(aGuestId gID: String)" в приведенном выше примере кода?
Из кода для реализации функции next() в вышеприведенном примере кода видно, что сбрасывает "nextIndex", когда итерация достигает конца. Как справиться с ситуацией, когда мы используем оператор break внутри цикла for... in, как показано ниже:
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 код прерывается после получения первого Элемента [в данном случае гостевого объекта]. Последующий цикл for будет начинаться с индекса 1 в коллекции, а не с 0. Есть ли способ обработать эту ситуацию прерывания, чтобы для каждого последующего цикла был установлен индекс 0?
Спасибо
Изменить: Кажется, проблема сброса "nextIndex" может быть исправлена с помощью приведенного ниже кода [добавлен в класс GuestManager] для реализации метода generate()
func generate() -> Self {
self.nextIndex = 0
return self
}
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
, На самом деле, самый простой способ выполнить все ваши требования - передать как можно больше логики SequenceType
, Indexable
и в конце концов (если вы хотите поддержать это) 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]
}
}
Еще несколько замечаний:
- Ваш
guests
свойство не должно быть необязательным. Это делает код более сложным, без каких-либо преимуществ. Я изменил это соответственно в моем коде. - Ваш
Guest
Класс, вероятно, должен быть типом значения (структура).GuestManager
также является хорошим кандидатом для структуры, если вам не требуется ссылочная семантика класса (все типы коллекций в стандартной библиотеке являются структурами).
Я думаю, что подход к подписке, который вы здесь пробуете, довольно запутанный. Лично я бы использовал функцию, чтобы сделать это для ясности.
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
}
}