2016-06-20 28 views
5

Yeni projemde MVVM desenini kullanmaya çalışıyorum. İlk kez tüm görüş modelimi yapıya hazırladım. Ancak, kapatmalarla birlikte fetchDataFromNetwork gibi async iş mantığını uyguladığımda, kapaklar eski görünüm model değerini yakalar ve ardından bu şekilde güncelleştirilir. Yeni bir görünüm modeli değeri değil. İşte Swift: ViewModel bir yapı mı yoksa sınıf mı olmalı?

oyun alanında bir test kodudur.

import Foundation 
import XCPlayground 

struct ViewModel { 
    var data: Int = 0 

    mutating func fetchData(completion:()->()) { 
    XCPlaygroundPage.currentPage.needsIndefiniteExecution = true 
    NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: "http://stackoverflow.com")!) { 
     result in 
     self.data = 10 
     print("viewModel.data in fetchResponse : \(self.data)") 
     completion() 
     XCPlaygroundPage.currentPage.finishExecution() 
     }.resume() 
    } 
} 

class ViewController { 
    var viewModel: ViewModel = ViewModel() { 
    didSet { 
     print("viewModel.data in didSet : \(viewModel.data)") 
    } 
    } 

    func changeViewModelStruct() { 
    print("viewModel.data before fetch : \(viewModel.data)") 

    viewModel.fetchData { 
     print("viewModel.data after fetch : \(self.viewModel.data)") 
    } 
    } 
} 

var c = ViewController() 
c.changeViewModelStruct() 

Konsol sınıfa, didSet aramadı ViewModel değişti ama viewController Görünüm Modeli varsa

viewModel.data before fetch : 0 
viewModel.data in didSet : 0 
viewModel.data in fetchResponse : 10 
viewModel.data after fetch : 0 

sorun yeni Değerini 10.

yok viewController Görünüm Modeli olduğunu yazdırır yeni Değer 10.

cevap

5

bir sınıf kullanmalısınız. Eğer bir mutasyona fonksiyonuna sahip bir yapı kullanıyorsanız

, işlevi, bir kapak içindeki mutasyon gerçekleştirmek olmamalıdır; Eğer değil şunları yapmalıdır: Ben sınıfa ViewModel değiştirdiyseniz

struct ViewModel { 
    var data: Int = 0 

    mutating func myFunc() { 
     funcWithClosure() { 
      self.data = 1 
     } 
    } 
} 

, didSet burada

Hiçbir şey yanlış aramadı - o beklenen bir davranış. Eğer struct kullanmayı tercih ediyorsanız


, sen

func fetchData(completion: ViewModel ->()) { 
    XCPlaygroundPage.currentPage.needsIndefiniteExecution = true 
    NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: "http://stackoverflow.com")!) { 
     result in 
     var newViewModel = self 
     newViewModel.data = 10 
     print("viewModel.data in fetchResponse : \(self.data)") 
     completion(newViewModel) 
     XCPlaygroundPage.currentPage.finishExecution() 
     }.resume() 
    } 


    viewModel.fetchData { newViewModel in 
    self.viewModal = newViewModel 
     print("viewModel.data after fetch : \(self.viewModel.data)") 
    } 

Ayrıca dataTaskWithURL sağlanan kapatma ana iş parçacığı üzerinde çalışmaz unutmayın yapabilirsiniz. İçinde dispatch_async(dispatch_get_main_queue()) {...}'u aramak isteyebilirsiniz.

+0

Yani zaman uyumsuz API çağrısı @Code ile yapı kullanma yolu yoktur? Çünkü yapıyı sınıftan daha çok kullanmayı tercih ediyorum. – Paul

+0

@Paul Gönderiyi düzenledim (tekrar). – Code

+0

Evet, bu kötü bir tasarım. :(Bu durumda dersi kullanmalıyım.Çok teşekkürler @Code. – Paul

0

Sen self.data iki seçenekleri alabilir: ya (struct olarak viewModel kullanarak) fetchResponse için kapatılması bir iade parametresini kullanın VEYA kendi set yöntemi/kapatma oluşturmak ve init yönteminde kullanabilirsiniz (viewModel kullanarak class).

class ViewModel { 
var data: Int = 0 
func fetchData(completion:()->()) { 
    XCPlaygroundPage.currentPage.needsIndefiniteExecution = true 
    NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: "http://stackoverflow.com")!) { 
     result in 
     self.data = 10 
     print("viewModel.data in fetchResponse : \(self.data)") 
     completion() 
     XCPlaygroundPage.currentPage.finishExecution() 
     }.resume() 
    } 
} 

class ViewController { 
    var viewModel: ViewModel! { didSet { print("viewModel.data in didSet : \(viewModel.data)") } } 

    init(viewModel: ViewModel) { 
     // closure invokes didSet 
     ({ self.viewModel = viewModel })() 
    } 

    func changeViewModelStruct() { 
     print("viewModel.data before fetch : \(viewModel.data)") 

     viewModel.fetchData { 
      print("viewModel.data after fetch : \(self.viewModel.data)") 
     } 
    } 
} 

let viewModel = ViewModel() 
var c = ViewController(viewModel: viewModel) 
c.changeViewModelStruct() 

Konsol baskılar:

viewModel.data in didSet : 0 
viewModel.data before fetch : 0 
viewModel.data in fetchResponse : 10 
viewModel.data after fetch : 10 

Apple Document böyle diyor: Bir özellik ilk başlatıldığında

willSet ve didSet gözlemciler denilen değildir. Yalnızca mülkün değeri bir başlatma bağlamının dışında ayarlandığında çağrılır.

+0

Evet, bunu biliyorum. Ancak didSet gözlemcisini, fetchData işleviyle temsilci seçme veya geri çağrıdan kullanmayı tercih ediyorum. Bu yüzden sınıfa göre yapı kullanıyorum. Async mutasyon fonksiyonuyla yapı kullanmanın bir yolu yoksa, sınıfı kullanmalıyım. – Paul

İlgili konular