Используйте общую структуру с протоколом - PullRequest
0 голосов
/ 12 июня 2018

Кажется, что дженерики Swift работают нормально, пока я не пытаюсь их комбинировать любым практическим способом.Я использую Swift 4.1, и я хотел бы создать универсальный массив, содержащий только слабые ссылки.Я могу определить это как WeakList<T>.Пока все хорошо.Но: я бы хотел использовать протокол для T.Свифт говорит, что нет ..

import Foundation

protocol Observer: class {
    func stateChanged(sender: SomeClass, newState: Int)
}

struct WeakList<T> where T: AnyObject {
    struct Ptr {
        weak var p: T?
    }
    private var storage: [Ptr] = []
    var aliveObjects: [T] {
        var result: [T] = []
        for ptr in storage {
            if let p = ptr.p {
                result.append(p)
            }
        }
        return result
    }
    mutating func add(_ obj: T) {
        storage.append(Ptr(p: obj))
    }
    // Let's ignore for a moment that this implementation leaks memory badly.
}

class SomeClass {
    var someVar: WeakList<Observer> = WeakList<Observer>()
    // Error: WeakList requires that 'Observer' be a class type

    var thisIsOk: WeakList<NSObject> = WeakList<NSObject>()
}

(это не мой оригинальный код, а минимальный проверяемый пример, который содержит достаточно деталей, чтобы никто не мог сказать «просто удалите ограничение AnyObject из структуры»)

Полагаю, то, что я пытаюсь сделать, просто невозможно.Либо это?Просто разочаровывает, что 4 раза из 5, когда я пытаюсь что-то сделать с помощью дженериков Swift, я позже узнаю, что то, что я пытаюсь сделать, просто невозможно.(Кстати, я могу легко реализовать то же самое в Objective-C.)

  • Я пытался изменить ограничение class на ограничение AnyObject =>, тоже не работает.
  • Я пытался изменить ограничение AnyObject на class ограничение => даже не компилируется.
  • А изменение на protocol Observer where Self: NSObject неизменить что-нибудь.NSObject - это тип класса, Observer - это NSObject.Из этого следует, что Observer является типом класса.Отношения «есть» здесь не кажутся транзитивными.

1 Ответ

0 голосов
/ 12 июня 2018

В текущей реализации вы не можете наследовать протокол от AnyObject.Что вы можете сделать, так это создать Type Eraser для вашего протокола и использовать его вместо этого.Теперь ваш ластик типов может быть унаследован от AnyObject.

Примерно так:

protocol Observer {
    func stateChanged(sender: SomeClass, newState: Int)
}

class AnyObserver: NSObject, Observer {
    private let observer: Observer

    init(observer: Observer) {
        self.observer = observer
    }

    func stateChanged(sender: SomeClass, newState: Int) {
        observer.stateChanged(sender: sender, newState: newState)
    }
}

struct WeakList<T> where T: AnyObject {
    struct Ptr {
        weak var p: T?
    }
    private var storage: [Ptr] = []
    var aliveObjects: [T] {
        var result: [T] = []
        for ptr in storage {
            if let p = ptr.p {
                result.append(p)
            }
        }
        return result
    }
    mutating func add(_ obj: T) {
        storage.append(Ptr(p: obj))
    }
    // Let's ignore for a moment that this implementation leaks memory badly.
}

class SomeClass {
    var someVar: WeakList<AnyObserver> = WeakList<AnyObserver>()

    var thisIsOk: WeakList<NSObject> = WeakList<NSObject>()
}
...