为什么 KVO 的 var setter 崩溃了 (swift 2.2)?
why the setter of var for KVO crashed (swift 2.2)?
我正在使用 KVO 进行手动通知,但为什么代码崩溃的原因是:
Thread 1:EXC_BAD_ACCESS (code=2, address=0x7fff577bcfa8)" when click
run?
请看下面的代码:
ChildrenViewController.swift
(class待观察)
import UIKit
class ChildrenViewController: UIViewController {
dynamic var name: String? {
get {
return ""
}
set {
willChangeValueForKey("name")
guard let value = newValue else {return}
self.setValue(value, forKey: "name") //crashed here!SAID "Thread 1:EXC_BAD_ACCESS (code=2, address=0x7fff577bcfa8)"
didChangeValueForKey("name")
}
}
dynamic var age = 0
var child: ChildrenViewController?
override class func automaticallyNotifiesObserversForKey(key: String) -> Bool {
if key == "name" {
return false
}
return super.automaticallyNotifiesObserversForKey(key)
}
}
ViewController.swift
(观察者)
import UIKit
private var child1Context = 1
class ViewController: UIViewController {
var child1 = ChildrenViewController()
override func viewDidLoad() {
super.viewDidLoad()
self.child1.setValue("George", forKey: "name")
self.child1.setValue(15, forKey: "age")
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
child1.addObserver(self, forKeyPath: "name", options: [.New,.Old], context: &child1Context)
child1.addObserver(self, forKeyPath: "age", options: [.New, .Old], context: &child1Context)
self.child1.name = "Michael" //set the name String
self.child1.setValue(20, forKey: "age")
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
self.child1.removeObserver(self, forKeyPath: "name")
self.child1.removeObserver(self, forKeyPath: "age")
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if context == &child1Context {
if keyPath == "name" {
print("The name of FIRST has been changed, \(change)")
}
if keyPath == "age" {
print("The age of FIRST has been changed, \(change)")
}
}
}
}
您在viewWillAppear
方法中添加了addObserver
,这意味着您在每次显示屏幕时都添加了它。对于这种情况,您需要在 viewWillDisappear
方法
中调用 removeObserver
override func viewWillDisappear(animated: Bool) {
self.child1.removeObserver(self, forKeyPath: "name")
self.child1.removeObserver(self, forKeyPath: "age")
super.viewWillDisappear(animated)
}
您正在通过此行在 它自己的 setter 中设置 name
的值:
self.setValue(value, forKey: "name")
为什么不能这样做:
private var _name: String?//create private variable to hold value
dynamic var name: String? {
get {
return _name
}
set {
willChangeValueForKey("name")
guard let value = newValue else {return}
_name = value
didChangeValueForKey("name")
}
}
我正在使用 KVO 进行手动通知,但为什么代码崩溃的原因是:
Thread 1:EXC_BAD_ACCESS (code=2, address=0x7fff577bcfa8)" when click run?
请看下面的代码:
ChildrenViewController.swift
(class待观察)
import UIKit
class ChildrenViewController: UIViewController {
dynamic var name: String? {
get {
return ""
}
set {
willChangeValueForKey("name")
guard let value = newValue else {return}
self.setValue(value, forKey: "name") //crashed here!SAID "Thread 1:EXC_BAD_ACCESS (code=2, address=0x7fff577bcfa8)"
didChangeValueForKey("name")
}
}
dynamic var age = 0
var child: ChildrenViewController?
override class func automaticallyNotifiesObserversForKey(key: String) -> Bool {
if key == "name" {
return false
}
return super.automaticallyNotifiesObserversForKey(key)
}
}
ViewController.swift
(观察者)
import UIKit
private var child1Context = 1
class ViewController: UIViewController {
var child1 = ChildrenViewController()
override func viewDidLoad() {
super.viewDidLoad()
self.child1.setValue("George", forKey: "name")
self.child1.setValue(15, forKey: "age")
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
child1.addObserver(self, forKeyPath: "name", options: [.New,.Old], context: &child1Context)
child1.addObserver(self, forKeyPath: "age", options: [.New, .Old], context: &child1Context)
self.child1.name = "Michael" //set the name String
self.child1.setValue(20, forKey: "age")
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
self.child1.removeObserver(self, forKeyPath: "name")
self.child1.removeObserver(self, forKeyPath: "age")
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if context == &child1Context {
if keyPath == "name" {
print("The name of FIRST has been changed, \(change)")
}
if keyPath == "age" {
print("The age of FIRST has been changed, \(change)")
}
}
}
}
您在viewWillAppear
方法中添加了addObserver
,这意味着您在每次显示屏幕时都添加了它。对于这种情况,您需要在 viewWillDisappear
方法
removeObserver
override func viewWillDisappear(animated: Bool) {
self.child1.removeObserver(self, forKeyPath: "name")
self.child1.removeObserver(self, forKeyPath: "age")
super.viewWillDisappear(animated)
}
您正在通过此行在 它自己的 setter 中设置 name
的值:
self.setValue(value, forKey: "name")
为什么不能这样做:
private var _name: String?//create private variable to hold value
dynamic var name: String? {
get {
return _name
}
set {
willChangeValueForKey("name")
guard let value = newValue else {return}
_name = value
didChangeValueForKey("name")
}
}