2015-03-08 6 views
0

Я сделал свой список (массив объектов MarketIndex) в качестве наблюдаемого с помощью этого сайта:как сделать просмотр наблюдателя контроллера быстрым?

http://www.drewag.me/posts/swift-kvo-substitute-observable-variables

Это код Наблюдаемых из приведенного выше сайта, который я добавил к моей программе:

import Foundation 


class Observable: NSObject { 

    typealias DidChangeHandler = (oldValue: Array<MarketIndex>?, newValue: Array<MarketIndex>) ->() 

    dynamic var value : Array<MarketIndex> { 
     didSet { 
      for (owner, handlers) in self.observers { 
       for handler in handlers { 
        handler(oldValue: oldValue, newValue: value) 
       } 
      } 
     } 
    } 

    init(_ value: Array<MarketIndex>) { 
     self.value = value 
     super.init() 
    } 

    func addObserverForOwner(owner: AnyObject, triggerImmediately: Bool, handler: DidChangeHandler) { 

     if let index = self.indexOfOwner(owner) { 
      // since the owner exists, add the handler to the existing array 
      self.observers[index].handlers.append(handler) 
     } else { 
      // since the owner does not already exist, add a new tuple with the 
      // owner and an array with the handler 
      self.observers.append(owner: owner, handlers: [handler]) 
     } 

     if (triggerImmediately) { 
      // Trigger the handler immediately since it was requested 
      handler(oldValue: nil, newValue: self.value) 
     } 
    } 

    func removeObserversForOwner(owner: AnyObject) { 
     if let index = self.indexOfOwner(owner) { 
      self.observers.removeAtIndex(index) 
     } 
    } 

    // #pragma mark - Private Properties 

    var observers: [(owner: AnyObject, handlers: [DidChangeHandler])] = [] 

    // #pragma mark - Private Methods 

    func indexOfOwner(owner: AnyObject) -> Int? { 
     var index : Int = 0 
     for (possibleOwner, handlers) in self.observers { 
      if possibleOwner === owner { 
       return index 
      } 
      index++ 
     } 
     return nil 
    } 
} 

И вот код SocketManager, который имеет список объектов MarketIndex, которые станут заметными.

import Foundation 

class SocketManager: WebSocketDelegate { 

    class var sharedInstance: SocketManager { 
     struct Static { 
      static var onceToken: dispatch_once_t = 0 
      static var instance: SocketManager? = nil 
     } 
     dispatch_once(&Static.onceToken) { 
      Static.instance = SocketManager() 
     } 
     return Static.instance! 
    } 
    var marketIndexList: Array<MarketIndex> = [] 
    var indexesList: Observable = Observable([]) 

    init() { 

    } 

    func getMarketIndexes(inputStream: String) { 

      // the marketIndexList will be updated from server every time that a value changes in the server 

      indexesList = Observable(marketIndexList) 

      println("\(indexesList.value)") 
     } 

     println("Market Index List Size: \(indexesList.value.count)") 
    } 
} 

Теперь я хочу, чтобы мой ViewController был наблюдателем для этого списка. Я добавил функцию addObserverForOwner в функцию viewDidLoad(), но она не меняется при каждом изменении моего списка. Вот код моего ViewController:

SocketManager - это класс, который имеет наблюдаемый индекс. Кроме того, SocketManager является Singleton. Функция notify() обновит значения меток и перезагрузит tableView моего ViewController.

class IndexViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { 
    override func viewDidLoad() { 
      super.viewDidLoad()  
    SocketManager.sharedInstance.indexesList.addObserverForOwner(self, triggerImmediately: true) { (oldValue, newValue) ->() in 

       self.marketIndexes = newValue 
       self.notify() 
      } 
    } 

func notify() { 

     updateValues(marketIndexes) 
     tableView.reloadData() 
    } 
    } 

Что я здесь делаю неправильно? Есть идеи?

Спасибо, ребята,

ответ

0

Похоже, у вас есть Observable объект, indexesList, который имеет init метод, который выглядит следующим образом:

init(_ value: Array<MarketIndex>) { 
    self.value = value 
    super.init() 
} 

Но обратите внимание, что Array является struct, так self.value получает копию того, что вы ему передаете. Таким образом, логика didSet не будет вызываться при обновлении marketIndexes, но только тогда, когда обновляется локальная копия value в пределах indexesList (т. Е. Никогда).

Есть и другие проблемы с этим примером кода (я не вижу, где вы вызываете getMarketIndexes; если вы его вызывали, он повторно создает объект Observable, отбрасывая старый массив замыканий, вы хотите быть осторожным об использовании [weak self] или [unowned self] в ваших замыканиях и т. д.), но я не уверен, что мы должны беспокоиться о них. Важнейшей проблемой для решения в первую очередь является то, что если вы хотите использовать этот шаблон с этого другого сайта, можно было бы уйти в отставку indexesList и использовать этот шаблон для самого marketIndexes.

На мой взгляд, это серьезно уменьшает значение класса Observable. Это не общий механизм, который может быть применен к существующим классам/свойствам, а скорее шаблон, который вы должны повторно реализовать для каждого свойства, к которому вы хотите добавить наблюдателей. Вы, конечно, можете это сделать, и это работает, но это немного тяжело.

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

var marketIndexList: [MarketIndex] = [] { 
    didSet { 
     tableView.reloadData() 
    } 
} 

Вы, вероятно, только что трудоустроить «массив замыканий, который должен быть вызван шаблоном didSet», если объект, который вы наблюдали, находился в другом классе (т. е.класс с marketIndexList не имеет способа узнать, сколько будет наблюдателей, и что конкретно наблюдатели захотят сделать). И даже тогда я лично долго и долго думал о том, хочу ли я своего собственного механизма наблюдения, а не существующих, устоявшихся шаблонов (уведомлений, KVO/KVN и т. Д.).

+0

Спасибо, что ответит, Роб. Но 'marketIndexes' не обновляется каждый раз, когда мой массив обновляется из другого класса. Поэтому я не могу использовать didSet и перезагрузить таблицу. Моя проблема заключается в том, как обновить мои «marketIndexes» в моем ViewController, когда когда-либо обновлялся массив в другом классе (SocketManager). Кроме того, я вызвал мой метод 'getMarketIndexes' в методе, который получил сообщение из сокета. Таким образом, каждый раз, когда новое сообщение получает, я вызываю 'getMarketIndexes' и обновляю свой массив и делаю его Observable. – user2366997

+0

Нет, ваш код не будет работать вообще. Кажется, вы работаете в предположении, что вы можете использовать этот шаблон Observable для переменной 'indexesList', а некоторые, как изменения в' marketIndexList' будут переводить на сообщения наблюдателей, управляемые 'indexesList'. Это не сработает. Вы должны использовать этот шаблон для самой переменной marketIndexList, если хотите, чтобы это работало. Устраните эту переменную 'indexesList' вообще. – Rob

+0

Сначала это было то, что я хотел сделать. Я хотел сделать marketIndexList наблюдаемым с вышеописанным классом Observable, но это не сработало. Я написал 'marketIndexList = Observable (marketIndexList)', но я получил сообщение об ошибке: «MarketIndex» не идентичен «AnyObject» – user2366997