12

構造体変数 (S) を持つクラス (A) があります。このクラスの 1 つの関数で、構造体変数の変更関数を呼び出します。この関数はクロージャを取ります。このクロージャの本体は、構造体変数の name プロパティをチェックします。

Struct の変更関数は、あるクラス (B) の関数を順番に呼び出します。このクラスの関数は再びクロージャーを取ります。このクロージャの本体で、構造体を変更します。つまり、name プロパティを変更し、最初のクラスによって提供されたクロージャを呼び出します。

最初のクラス (A) クロージャーが構造体の name プロパティを検査している場所で呼び出されると、それは決して変更されません。

しかし、ステップ 2 で、クラス B の代わりに構造体 (C) を使用すると、クラス A のクロージャー構造体の内部が実際に変更されていることがわかります。以下はコードです:

class NetworkingClass {
  func fetchDataOverNetwork(completion:()->()) {
    // Fetch Data from netwrok and finally call the closure
    completion()
  }
}

struct NetworkingStruct {
  func fetchDataOverNetwork(completion:()->()) {
    // Fetch Data from netwrok and finally call the closure
    completion()
  }
}

struct ViewModelStruct {

  /// Initial value
  var data: String = "A"

  /// Mutate itself in a closure called from a struct
  mutating func changeFromStruct(completion:()->()) {
    let networkingStruct = NetworkingStruct()
    networkingStruct.fetchDataOverNetwork {
      self.data = "B"
      completion()
    }
  }

  /// Mutate itself in a closure called from a class
  mutating func changeFromClass(completion:()->()) {
    let networkingClass = NetworkingClass()
    networkingClass.fetchDataOverNetwork {
      self.data = "C"
      completion()
    }
  }
}

class ViewController {
  var viewModel: ViewModelStruct = ViewModelStruct()

  func changeViewModelStruct() {
    print(viewModel.data)

    /// This never changes self.viewModel inside closure, Why Not?
    viewModel.changeFromClass {
      print(self.viewModel.data)
    }

    /// This changes self.viewModel inside/outside closure, Why?
    viewModel.changeFromStruct {
      print(self.viewModel.data)
    }
  }
}

var c = ViewController()
c.changeViewModelStruct()

なぜこの異なる動作。差別化要因は、viewModel に構造体を使用するか、クラスに使用するかであると考えました。ただし、ここでは、Networking がクラスであるか構造体であるかに依存し、ViewController または ViewModel から独立しています。誰でもこれを理解するのを手伝ってもらえますか?

4

3 に答える 3

2

これはどう?

import Foundation
import XCPlayground


protocol ViewModel {
  var delegate: ViewModelDelegate? { get set }
}

protocol ViewModelDelegate {
  func viewModelDidUpdated(model: ViewModel)
}

struct ViewModelStruct: ViewModel {
  var data: Int = 0
  var delegate: ViewModelDelegate?

  init() {
  }

  mutating func fetchData() {
    XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
    NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: "http://stackoverflow.com")!) {
       result in
      self.data = 20
      self.delegate?.viewModelDidUpdated(self)
      print("viewModel.data in fetchResponse : \(self.data)")

      XCPlaygroundPage.currentPage.finishExecution()
      }.resume()
  }
}

protocol ViewModeling {
  associatedtype Type
  var viewModel: Type { get }
}

typealias ViewModelProvide = protocol<ViewModeling, ViewModelDelegate>

class ViewController: ViewModelProvide {
  var viewModel = ViewModelStruct() {
    didSet {
      viewModel.delegate = self
      print("ViewModel in didSet \(viewModel)")
    }
  }

  func viewDidLoad() {
    viewModel = ViewModelStruct()
  }

  func changeViewModelStruct() {
    print(viewModel)
    viewModel.fetchData()
  }
}

extension ViewModelDelegate where Self: ViewController {
  func viewModelDidUpdated(viewModel: ViewModel) {
    self.viewModel = viewModel as! ViewModelStruct
  }
}

var c = ViewController()
c.viewDidLoad()
c.changeViewModelStruct()

ソリューション 2、3 では、ViewController に新しいビュー モデルを割り当てる必要があります。そこでProtocol Extensionを使って自動で作りたいと思います。didSet オブザーバーはうまく機能します!ただし、これはデリゲート メソッドの強制キャストを削除する必要があります。

于 2016-06-20T08:43:38.470 に答える
0

これは解決策ではありませんが、このコードを使用すると、クラスと構造体の両方のケースでViewController's,が適切に設定されていることがわかります。viewModel.data異なるのは、viewModel.changeFromClassクロージャが stale をキャプチャすることself.viewModel.dataです。特に、class の '3 self' print だけが間違っていることに注意してください。それを包む「2セルフ」と「4セルフ」のプリントではありません。

ここに画像の説明を入力

class NetworkingClass {
  func fetchDataOverNetwork(completion:()->()) {
    // Fetch Data from netwrok and finally call the closure
    print("\nclass: \(self)")
    completion()
  }
}

struct NetworkingStruct {
  func fetchDataOverNetwork(completion:()->()) {
    // Fetch Data from netwrok and finally call the closure
    print("\nstruct: \(self)")
    completion()
  }
}

struct ViewModelStruct {

  /// Initial value
  var data: String = "A"

  /// Mutate itself in a closure called from a struct
  mutating func changeFromStruct(completion:()->()) {
    let networkingStruct = NetworkingStruct()
    networkingStruct.fetchDataOverNetwork {
      print("1 \(self)")
      self.data = "B"
      print("2 \(self)")
      completion()
      print("4 \(self)")
    }
  }

  /// Mutate itself in a closure called from a class
  mutating func changeFromClass(completion:()->()) {
    let networkingClass = NetworkingClass()
    networkingClass.fetchDataOverNetwork {
      print("1 \(self)")
      self.data = "C"
      print("2 \(self)")
      completion()
      print("4 \(self)")
    }
  }
}

class ViewController {
  var viewModel: ViewModelStruct = ViewModelStruct()

  func changeViewModelStruct() {
    print(viewModel.data)

    /// This never changes self.viewModel, Why Not?
    viewModel.changeFromClass {
      print("3 \(self.viewModel)")
      print(self.viewModel.data)
    }

    /// This changes self.viewModel, Why?
    viewModel.changeFromStruct {
      print("3 \(self.viewModel)")
      print(self.viewModel.data)
    }
  }
}

var c = ViewController()
c.changeViewModelStruct()
于 2016-06-16T07:05:08.707 に答える