2016-06-20 23 views
5

Tôi đang cố gắng sử dụng mẫu MVVM trong dự án mới của mình. Lần đầu tiên, tôi tạo ra tất cả các mô hình xem của tôi để cấu trúc. Nhưng khi tôi triển khai thực hiện logic kinh doanh không đồng bộ như fetchDataFromNetwork với các bao đóng, các khung đóng lại nắm bắt giá trị mô hình xem cũ sau đó được cập nhật lên đó. Không phải là giá trị kiểu xem mới.Swift: ViewModel có nên là cấu trúc hay lớp không?

Đây là mã thử nghiệm trong sân chơi.

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() 

điều khiển in

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

Vấn đề là Xem các mẫu trong ViewController không có giá trị gia tăng mới 10

Nếu tôi thay đổi ViewModel lớp, didSet không được gọi nhưng Xem các mẫu trong ViewController có Giá trị mới 10.

Trả lời

5

Bạn nên sử dụng một lớp học.

Nếu bạn sử dụng cấu trúc có hàm biến đổi, hàm sẽ không thực hiện đột biến trong một đóng; bạn nên không làm như sau:

struct ViewModel { 
    var data: Int = 0 

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

Nếu tôi thay đổi ViewModel lớp, didSet không được gọi

Không có gì sai ở đây - đó là hành vi mong đợi.


Nếu bạn thích sử dụng struct, bạn có thể làm

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)") 
    } 

Cũng lưu ý rằng việc đóng cửa cung cấp cho dataTaskWithURL không chạy trên các chủ đề chính. Bạn có thể muốn gọi dispatch_async(dispatch_get_main_queue()) {...} trong đó.

+0

Vì vậy, không có cách nào để sử dụng cấu trúc với lệnh gọi async API @Code? Bởi vì tôi thích sử dụng struct hơn class. – Paul

+0

@Paul Đã chỉnh sửa bài đăng của tôi (một lần nữa). – Code

+0

Vâng, đó là một thiết kế tồi. :(Tôi nên sử dụng lớp trong trường hợp này. Cảm ơn @Code. – Paul

0

Bạn có thể nhận các self.data trong hai lựa chọn: hoặc sử dụng một tham số trở lại trong việc đóng cửa của bạn cho fetchResponse (sử dụng viewModel như struct) HOẶC bạn có thể tạo riêng của bạn thiết lập phương pháp/đóng cửa và sử dụng nó trong phương pháp init của bạn (sử dụng viewModelclass).

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() 

điều khiển in:

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

Apple Document nói như thế này:

willSet và didSet nhà quan sát không được gọi khi một tài sản được khởi tạo đầu tiên. Chúng chỉ được gọi khi giá trị của thuộc tính được đặt bên ngoài ngữ cảnh khởi tạo.

+0

Vâng, tôi biết điều đó. Nhưng tôi thích sử dụng quan sát didSet hơn là ủy quyền hoặc gọi lại với hàm fetchData. Vì vậy, tôi sử dụng cấu trúc hơn lớp. Nếu không có cách nào để sử dụng struct với hàm async mutating, tôi nên sử dụng class. – Paul

Các vấn đề liên quan