2016-01-16 3 views
4

Я пытаюсь реализовать структуру, которая позволяет мне хранить набор слабых наблюдателей.Набор слабых наблюдателей в Swift

Вот обертка наблюдатель:

public func ==<T: Hashable>(lhs: WeakObserver<T>, rhs: WeakObserver<T>) -> Bool { 
    return lhs.hashValue == rhs.hashValue 
} 

public struct WeakObserver<T where T: AnyObject, T: Hashable> : Hashable { 

    private weak var weakObserver : T? 

    public init(weakObserver: T){ 
    self.weakObserver = weakObserver 
    } 

    public var hashValue : Int { 
    return self.weakObserver!.hashValue 
    } 

} 

А вот протокол каждый наблюдатель должен соответствовать:

public protocol DataModelObserverProtocol : class, Hashable, AnyObject { 

    func someFunc() 

} 

Использование:

public class DataModel: NSObject, DataModelInterface { 

    public var observers = Set<WeakObserver<DataModelObserverProtocol>>() 
    //^^^^^ Using 'DataModelObserverProtocol' as a concrete type conforming to protocol 'AnyObject' is not supported 
} 

Теперь, когда я осознавая, что это может быть ограничением самой Swift, я ищу альтернативное решение, не имея конкретного cl в качестве ограничения типа (если это невозможно, чего я боюсь, я все равно буду любить альтернативные «не-хаки»).

+1

Его стоит проверить https://gist.github.com/preble/13ab713ac044876c89b5 – Kirsteins

+0

@ Kirsteins Это действительно похоже на то, что я ищу, но оно также выглядит немного излишним для меня (без видимой причины) –

+1

@ Kirsteins Даже с «WeakSet» на месте я получаю сообщение об ошибке «Использование протокола как конкретного типа, соответствующего протоколу, не поддерживается». –

ответ

0

Я знаю, что были бы лучшие способы сделать это, но это просто кажется s, чтобы быть слишком сложным для такой простой концепции, поэтому я пошел по-другому:

Я только что использовал NSNotificationCenter и реорганизовал мой код, чтобы не иметь тесно связанной структуры, поэтому вместо передачи информации из уведомления, Мне удалось отвлечь его так, чтобы это работало для меня таким образом, что уведомлениям не нужно было передавать аргументы своим наблюдателям. Из-за того, что уведомления поддерживают только передачу информации через словарь userInfo, я был вынужден об этом подумать.

Поэтому, как я уже говорил, мне нужен был контекстно-развязанный способ сделать это. Это очень абстрактный ответ, но я думаю, что это может помочь кому-то обойти проблему, связанную с «дисфункциональными дженериками».

Для того, чтобы уведомить своих наблюдателей я просто зарегистрировать их:

NSNotificationCenter.defaultCenter().addObserver(...) 

и почтовые уведомления без аргументов:

NSNotificationCenter.defaultCenter().postNotification... 

я уже использовал встроенный NSNotificationCenter в предыдущих проектах, и я прекрасно понимал, но первоначально я собирался передать информацию наблюдателям, поэтому я хотел немного ограничить ее созданием WeakSet.

Снова это сработало в моем случае, потому что мои наблюдатели не обязательно должны проходить специфику наблюдаемого, потому что он обычно присутствует и доступен наблюдателям в любом случае (это то, что предоставляет моя структура, возможно, это не так случай читателя). Трудно объяснить это так, что это имеет смысл для всех, кто читает это, но я был очень обескуражен предлагаемым WeakSet, поэтому я пошел с этим. Это более читаемо (возможно, было менее понятным для третьей стороны), и это просто казалось более подходящим для моего случая использования.

2

Использование набора для удержания ссылок запускает риск того, что Set в конечном итоге должен будет ссылаться на элемент, используя его hashValue, а когда слабая ссылка равна нулю, функция hashValue выйдет из строя.

Я не мог сделать это с помощью протоколов, но нашел способ получить аналогичную функциональность, используя универсальную функцию, которая возвращает кортеж функций.

struct WeakReference<T> 
{ 
    weak var _reference:AnyObject? 
    init(_ object:T) {_reference = object as? AnyObject} 
    var reference:T? { return _reference as? T } 
} 

func weakReferences<T>(_:T.Type) -> ( 
            getObjects:()->[T], 
            addObject: (T)->() 
            ) 
{ 
    var references : [WeakReference<T>] = [] 

    func getObjects() -> [T] 
    { 
     return references.filter({ $0.reference != nil }).map({$0.reference!}) 
    } 

    func addObject(object:T) 
    { 
     if getObjects().contains({ ($0 as! AnyObject) === (object as! AnyObject) }) 
     { return } 
     references.append(WeakReference(object)) 
    } 

    return (getObjects:getObjects, addObject:addObject) 
} 

public protocol DataModelObserverProtocol: class, AnyObject 
{ 
    func someFunc() -> String 
} 

public class DataModel: NSObject, DataModelInterface 
{ 
    var observers = weakReferences(DataModelObserverProtocol) 
} 

Для добавления наблюдателей, можно использовать:

observers.addObject(yourObserver) 

Для перебирать наблюдатель:

for observer in observers.objects() 
{ 
    observer.someFunc() 
} 

Обе функции типобезопасные и будет принимать только/возврат DataModelObserverProtocol совместимых объектов

+0

Будет ли тип литья в 'WeakReference' влиять на производительность? – kelin

+0

Тип каста такого типа обрабатывается во время компиляции и просто сообщает компилятору, что мы знаем, что переменные являются совместимыми типами. –

+0

Единственные накладные расходы - это пара дополнительных «слов» памяти для хранения и одно косвенное обращение в вызове функции. Это вряд ли имеет какое-либо значимое влияние на производительность. –

Смежные вопросы