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
)。怎么