steve steve - 3 months ago 23
iOS Question

why the setter of var for KVO crashed (swift 2.2)?

I'm using KVO for manual notifications, but why the code crashed for the reason:


Thread 1:EXC_BAD_ACCESS (code=2, address=0x7fff577bcfa8)" when click
run?


Please see below the codes:

ChildrenViewController.swift
(class to be observed)

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

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

}

Answer

You are setting value of name in it's own setter by this line:

self.setValue(value, forKey: "name")

Why can't do this:

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