stephen
stephen

Reputation: 597

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

}

Upvotes: 1

Views: 571

Answers (2)

D4ttatraya
D4ttatraya

Reputation: 3404

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

Upvotes: 2

iSashok
iSashok

Reputation: 2446

You added addObserver in viewWillAppear method, so it means you added it every time when your screen is show. For this case you need call removeObserver in viewWillDisappear method

override func viewWillDisappear(animated: Bool) {
    self.child1.removeObserver(self, forKeyPath: "name")
    self.child1.removeObserver(self, forKeyPath: "age")
    super.viewWillDisappear(animated)
}

Upvotes: 0

Related Questions