Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/wix/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 为什么在Swift 4中使用委托传递数据失败_Ios_Swift - Fatal编程技术网

Ios 为什么在Swift 4中使用委托传递数据失败

Ios 为什么在Swift 4中使用委托传递数据失败,ios,swift,Ios,Swift,这是我的协议 protocol PassDataDelegate { func passData(data: String) } 我的第一个控制器 class FirstViewController: UIViewController { @IBOutlet weak var textField: UITextField! var delegate: PassDataDelegate? override func viewDidLoad() {

这是我的协议

protocol PassDataDelegate {
    func passData(data: String)
}
我的第一个控制器

class FirstViewController: UIViewController {

    @IBOutlet weak var textField: UITextField!

    var delegate: PassDataDelegate?

    override func viewDidLoad() {
        super.viewDidLoad()
        delegate = SecondViewController()
    }

    @IBAction func sendDataButtonTapped(_ sender: Any) {
        delegate?.passData(data: textField.text!)
        performSegue(withIdentifier: "Go", sender: nil)
    }

}
第二,最后一个

class SecondViewController: UIViewController, PassDataDelegate {

    @IBOutlet weak var myLabel: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    func passData(data: String) {
        print("This came from first: \(data). Will change UI.")
        myLabel.text = data
    }
}

应用程序在标签更改部分崩溃。它在展开时显示为零。这里出了什么问题?

您的代码有几个基本问题。 我认为,在授权和UIStoryboardSegue机制方面,您可能也存在一些误解。你可能应该好好读一读这方面的书

话虽如此,让我发布一个解决您问题的方法,用内联评论解释发生了什么

//  Has to be marked as a class protocol (`: class`) so that
//  `weak var delegate: PassDataDelegate?` can be weak.
protocol PassDataDelegate: class {
  func passData(data: String)
}

class FirstViewController: UIViewController {

  @IBOutlet weak var textField: UITextField!

  //  Important!
  //  Make this a `weak` var. In your case, you would fortunately not create a retain cycle
  //  but there is a big threat of creating those when using delegation patterns with non-weak delegates.
  //
  //  In your case, if you don't make this `weak`, `SecondViewController` would never be deallocated unless you
  //  cleared this var manually (setting it to `nil`).
  //
  //  Also note that, if you're using `PassDataDelegate` solely for forwarding some data to the next view controller,
  //  you can dismiss this var entirely. There is no need to have a reference to the second view controller hanging around.
  //  In fact, as mentioned above, it can be dangerous to do so.
  //  Additionally, you don't need to make the protocol `: class` then.
  private weak var delegate: PassDataDelegate?

  override func viewDidLoad() {
    super.viewDidLoad()

    //  It doesn't make any sense to create a `SecondViewController` here.
    //  The segue mechanism will create a new instance of `SecondViewController`.
//    delegate = SecondViewController()
  }

  @IBAction func sendDataButtonTapped(_ sender: Any) {
    //  `delegate?` is `nil` here.
//    delegate?.passData(data: textField.text!)
    performSegue(withIdentifier: "Go", sender: nil)
  }

  //  This is the proper 'hook' for you to forward data or do anything with a destination
  //  view controller presented using `UIStoryboardSegue`.
  //  This method will be called by the system following your call to `performSegue`.
  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    super.prepare(for: segue, sender: sender)

    //  `UITextField.text` can be `nil`, so safeguard for that here.
    //  If the destination implements our protocol, we can forward data to it.
    if let text = textField.text, let delegate = segue.destination as? PassDataDelegate {

      //  This is optional. You can hang on to the destination view controller here, but
      //  review the comments above to reason about whether this makes sense or not.
      self.delegate = delegate

      //  We can safely forward the data (text) here.
      delegate.passData(data: text)
    }
  }
}

SecondViewController
可以保持原样


更新 关于
授权


委托模式通常描述一个返回指针,它返回到一个发起实例。例如,使用
UITableView数据源
,一个
UITableView
与一个实现此协议的对象进行对话,以获取有关其数据的信息等等。
通过将数据转发到
SecondViewController
,您实际上在做相反的事情。如注释中所述,此代码甚至会中断,因为
SecondViewController
中的
passData
的实现正在使用尚未初始化的插座。

现在,您可以在此处执行以下三项操作之一:

1.
保留您现在使用的模式(准确地说是而不是委派),并更改
SecondViewController
以使事情正常进行

class SecondViewController: UIViewController, PassDataDelegate {

  @IBOutlet weak var myLabel: UILabel!

  private var data: String?

  override func viewDidLoad() {
    super.viewDidLoad()

    //  It is safe to access `myLabel` in `viewDidLoad`. Outlets have been connected.
    if let data = data {
      myLabel.text = data
    }
  }


  func passData(data: String) {
    self.data = data

    //  Only access `myLabel` if the view is loaded.
    if isViewLoaded {
      print("This came from first: \(data). Will change UI.")
      myLabel.text = data
    }
  }
}
这种方法实际上非常麻烦,因为您需要在
passData
可以随时调用的情况下进行调整。因此,您不知道您的门店是否已经初始化,这会导致代码臃肿和重复。糟糕

2. 完全剥离协议并使用更直接的方法

class FirstViewController: UIViewController {

  @IBOutlet weak var textField: UITextField!

  //  This is the proper 'hook' for you to forward data or do anything with a destination
  //  view controller presented using `UIStoryboardSegue`.
  //  This method will be called by the system following your call to `performSegue`.
  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    super.prepare(for: segue, sender: sender)

    //  `UITextField.text` can be `nil`, so safeguard for that here.
    //  If the destination is a `SecondViewController`, we know that is has `public var data: String` and we can forward data to it.
    if let text = textField.text, let destination = segue.destination as? SecondViewController {

      //  We can safely forward the data (text) here.
      destination.data = text
    }
  }
}

class SecondViewController: UIViewController {

  @IBOutlet weak var myLabel: UILabel!

  //  Deliberatly marking this a `public` to make clear that
  //  you're intented to set this from the 'outside'.
  public var data: String? {
    didSet {
      if isViewLoaded {
        myLabel.text = data
      }
    }
  }

  override func viewDidLoad() {
    super.viewDidLoad()

    //  It is safe to access `myLabel` in `viewDidLoad`. Outlets have been connected.
    if let data = data {
      myLabel.text = data
    }
  }
}
同样,我们不喜欢他的方法:

  • 仍在重复代码,并且必须检查是否加载了
    isViewLoaded
  • 你特别想使用协议,我们这里不这么做

我们可以通过在
SecondViewController
init
中提供数据来解决重复代码问题。但是,由于您使用的是segues,故事板将为您实例化目标视图控制器,您无法控制该控制器。现在,您可以使用segues进行剥离,但这很快就会远离您最初的问题,并且是一种完全不同的只使用代码的方法。所以这也不好

3. 使用协议,但正确应用委派模式

protocol DataProvider: class {
  func provideData() -> String?
}

protocol DataHandler: class {
  var providerDelegate: DataProvider? { get set }
}

class FirstViewController: UIViewController, DataProvider {

  @IBOutlet weak var textField: UITextField!

  func provideData() -> String? {
    return textField.text
  }

  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    super.prepare(for: segue, sender: sender)

    //  If the destination is a `DataHandler`, we can set yourselves as its provider.
    if let destination = segue.destination as? DataHandler {
      destination.providerDelegate = self
    }
  }
}

class SecondViewController: UIViewController, DataHandler {

  @IBOutlet weak var myLabel: UILabel!

  weak var providerDelegate: DataProvider?

  override func viewDidLoad() {
    super.viewDidLoad()

    if let data = providerDelegate?.provideData() {
      //  Safe to access `myLabel`, because we are in `viewDidLoad`.
      myLabel.text = data
    }
  }
}

这种方法是最通用的。双方都不关心处理程序和提供者到底是什么。请注意,在经典的委托模式中,您可能没有
DataHandler
协议并在
prepareForSegue
中检查具体类型(此处为
SecondViewController
)。然而,这种方法更加灵活,同时仍将代表团融入其中。从
SecondViewController
的角度来看,这种方法也是最稳健的。它不必在任何时候处理
passData
,而是可以自行决定何时向其委托人(
DataProvider
)请求数据。通过这种方式,
SecondViewController
可以推断其所有插座等何时初始化,处理数据是否安全。

您的代码有几个基本问题。 我认为,在授权和UIStoryboardSegue机制方面,您可能也存在一些误解。你可能应该好好读一读这方面的书

话虽如此,让我发布一个解决您问题的方法,用内联评论解释发生了什么

//  Has to be marked as a class protocol (`: class`) so that
//  `weak var delegate: PassDataDelegate?` can be weak.
protocol PassDataDelegate: class {
  func passData(data: String)
}

class FirstViewController: UIViewController {

  @IBOutlet weak var textField: UITextField!

  //  Important!
  //  Make this a `weak` var. In your case, you would fortunately not create a retain cycle
  //  but there is a big threat of creating those when using delegation patterns with non-weak delegates.
  //
  //  In your case, if you don't make this `weak`, `SecondViewController` would never be deallocated unless you
  //  cleared this var manually (setting it to `nil`).
  //
  //  Also note that, if you're using `PassDataDelegate` solely for forwarding some data to the next view controller,
  //  you can dismiss this var entirely. There is no need to have a reference to the second view controller hanging around.
  //  In fact, as mentioned above, it can be dangerous to do so.
  //  Additionally, you don't need to make the protocol `: class` then.
  private weak var delegate: PassDataDelegate?

  override func viewDidLoad() {
    super.viewDidLoad()

    //  It doesn't make any sense to create a `SecondViewController` here.
    //  The segue mechanism will create a new instance of `SecondViewController`.
//    delegate = SecondViewController()
  }

  @IBAction func sendDataButtonTapped(_ sender: Any) {
    //  `delegate?` is `nil` here.
//    delegate?.passData(data: textField.text!)
    performSegue(withIdentifier: "Go", sender: nil)
  }

  //  This is the proper 'hook' for you to forward data or do anything with a destination
  //  view controller presented using `UIStoryboardSegue`.
  //  This method will be called by the system following your call to `performSegue`.
  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    super.prepare(for: segue, sender: sender)

    //  `UITextField.text` can be `nil`, so safeguard for that here.
    //  If the destination implements our protocol, we can forward data to it.
    if let text = textField.text, let delegate = segue.destination as? PassDataDelegate {

      //  This is optional. You can hang on to the destination view controller here, but
      //  review the comments above to reason about whether this makes sense or not.
      self.delegate = delegate

      //  We can safely forward the data (text) here.
      delegate.passData(data: text)
    }
  }
}

SecondViewController
可以保持原样


更新 关于
授权


委托模式通常描述一个返回指针,它返回到一个发起实例。例如,使用
UITableView数据源
,一个
UITableView
与一个实现此协议的对象进行对话,以获取有关其数据的信息等等。
通过将数据转发到
SecondViewController
,您实际上在做相反的事情。如注释中所述,此代码甚至会中断,因为
SecondViewController
中的
passData
的实现正在使用尚未初始化的插座。

现在,您可以在此处执行以下三项操作之一:

1.
保留您现在使用的模式(准确地说是而不是委派),并更改
SecondViewController
以使事情正常进行

class SecondViewController: UIViewController, PassDataDelegate {

  @IBOutlet weak var myLabel: UILabel!

  private var data: String?

  override func viewDidLoad() {
    super.viewDidLoad()

    //  It is safe to access `myLabel` in `viewDidLoad`. Outlets have been connected.
    if let data = data {
      myLabel.text = data
    }
  }


  func passData(data: String) {
    self.data = data

    //  Only access `myLabel` if the view is loaded.
    if isViewLoaded {
      print("This came from first: \(data). Will change UI.")
      myLabel.text = data
    }
  }
}
这种方法实际上非常麻烦,因为您需要在
passData
可以随时调用的情况下进行调整。因此,您不知道您的门店是否已经初始化,这会导致代码臃肿和重复。糟糕

2. 完全剥离协议并使用更直接的方法

class FirstViewController: UIViewController {

  @IBOutlet weak var textField: UITextField!

  //  This is the proper 'hook' for you to forward data or do anything with a destination
  //  view controller presented using `UIStoryboardSegue`.
  //  This method will be called by the system following your call to `performSegue`.
  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    super.prepare(for: segue, sender: sender)

    //  `UITextField.text` can be `nil`, so safeguard for that here.
    //  If the destination is a `SecondViewController`, we know that is has `public var data: String` and we can forward data to it.
    if let text = textField.text, let destination = segue.destination as? SecondViewController {

      //  We can safely forward the data (text) here.
      destination.data = text
    }
  }
}

class SecondViewController: UIViewController {

  @IBOutlet weak var myLabel: UILabel!

  //  Deliberatly marking this a `public` to make clear that
  //  you're intented to set this from the 'outside'.
  public var data: String? {
    didSet {
      if isViewLoaded {
        myLabel.text = data
      }
    }
  }

  override func viewDidLoad() {
    super.viewDidLoad()

    //  It is safe to access `myLabel` in `viewDidLoad`. Outlets have been connected.
    if let data = data {
      myLabel.text = data
    }
  }
}
同样,我们不喜欢他的方法:

  • 仍在重复代码,并且必须检查是否加载了
    isViewLoaded
  • 你特别想使用协议,我们这里不这么做

我们可以通过在
SecondViewController
init
中提供数据来解决重复代码问题。但是,由于您使用的是segues,故事板将为您实例化目标视图控制器,您无法控制该控制器。现在,您可以使用segues进行剥离,但这很快就会远离您最初的问题,并且是一种完全不同的只使用代码的方法。所以这也不好

3. 使用协议,但正确应用委派模式

protocol DataProvider: class {
  func provideData() -> String?
}

protocol DataHandler: class {
  var providerDelegate: DataProvider? { get set }
}

class FirstViewController: UIViewController, DataProvider {

  @IBOutlet weak var textField: UITextField!

  func provideData() -> String? {
    return textField.text
  }

  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    super.prepare(for: segue, sender: sender)

    //  If the destination is a `DataHandler`, we can set yourselves as its provider.
    if let destination = segue.destination as? DataHandler {
      destination.providerDelegate = self
    }
  }
}

class SecondViewController: UIViewController, DataHandler {

  @IBOutlet weak var myLabel: UILabel!

  weak var providerDelegate: DataProvider?

  override func viewDidLoad() {
    super.viewDidLoad()

    if let data = providerDelegate?.provideData() {
      //  Safe to access `myLabel`, because we are in `viewDidLoad`.
      myLabel.text = data
    }
  }
}

这种方法是最通用的。双方都不关心处理程序和提供者到底是什么。请注意,在经典的委托模式中,您可能没有
DataHandler
协议并在
prepareForSegue
中检查具体类型(此处为
SecondViewController
)。怎么