Ios swift中的KVO绑定失败
这个错误让我真的很沮丧。我使用KVO的方式如下:Ios swift中的KVO绑定失败,ios,swift,exc-bad-access,key-value-observing,Ios,Swift,Exc Bad Access,Key Value Observing,这个错误让我真的很沮丧。我使用KVO的方式如下: dynamic var product: NSNumber? { get { return (defaults.objectForKey("product") != nil ? defaults.integerForKey("product") : nil) } set(newValue) { if newValue != product { willChangeVa
dynamic var product: NSNumber? {
get {
return (defaults.objectForKey("product") != nil ? defaults.integerForKey("product") : nil)
}
set(newValue) {
if newValue != product {
willChangeValueForKey("product")
if let value = newValue {
defaults.setInteger(value.integerValue, forKey: "product")
} else {
defaults.removeObjectForKey("product")
}
didChangeValueForKey("product")
reader.product = newValue?.integerValue
refresh()
}
}
}
override class func automaticallyNotifiesObserversForKey(key: String) -> Bool {
return true
}
另一个对象修改属性,如下所示:
info.product = NSNumber(short: prodId)
调用时,结果是以下错误:
Thread 1: EXC_BAD_ACCESS(code=1, address = 0x0)
和堆栈跟踪:
#0 0x37c4cf66 in objc_msgSend ()
#1 0x2acca772 in NSKeyValuePushPendingNotificationPerThread ()
#2 0x2acbae3c in NSKeyValueWillChange ()
#3 0x2aca7c04 in -[NSObject(NSKeyValueObserverNotification) willChangeValueForKey:] ()
#4 0x2acdafc0 in _NSSetObjectValueAndNotify ()
#5 0x002a3c44 in Module.MyView.pickerView (Module.MyView)(ObjectiveC.UIPickerView, didSelectRow : Swift.Int, inComponent : Swift.Int) -> () at /<***>/MyView.swift:122
编辑此解决方案最初有效,但后来的更改使其无效 我刚刚找到了一个解决办法,对我来说,这似乎真的很愚蠢。我所要做的就是像这样移动
willChangeValueForKey
和didChangeValueForKey
方法调用
dynamic var product: NSNumber? {
get {
return (defaults.objectForKey("product") != nil ? defaults.integerForKey("product") : nil)
}
set(newValue) {
willChangeValueForKey("product")
if newValue != product {
if let value = newValue {
defaults.setInteger(value.integerValue, forKey: "product")
} else {
defaults.removeObjectForKey("product")
}
reader.product = newValue?.integerValue
refresh()
}
didChangeValueForKey("product")
}
}
猜猜看,我会说实现是聪明的。如果它没有将调用willChangeValueForKey
视为mutator方法中的第一件事,那么它会在进入mutator之前调用它,并破坏调用。移动了这些调用后,代码现在不会进行那些默认调用,并且所有调用都正常工作
required init(coder aDecoder: NSCoder) {
}
deinit {
if annotation != nil {
Static.info.removeObserver(self, forKeyPath: "product")
}
}
override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) {
switch keyPath {
case "product":
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0)) {
self.refreshText()
self.refreshImages()
}
default:
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
}
override var annotation: MKAnnotation! {
get {
return super.annotation
}
set(newAnnotation) {
if annotation != nil {
Static.info.removeObserver(self, forKeyPath: "product")
}
super.annotation = newAnnotation
if let annot = newAnnotation as? MyAnnotation {
Static.info.addObserver(self, forKeyPath: "product", options: NSKeyValueObservingOptions.allZeros, context: nil)
self.refreshText()
self.refreshImages()
}
}
}
这听起来既聪明又愚蠢。其他意见?编辑此解决方案最初有效,但后来的更改使其无效 我刚刚找到了一个解决办法,对我来说,这似乎真的很愚蠢。我所要做的就是像这样移动
willChangeValueForKey
和didChangeValueForKey
方法调用
dynamic var product: NSNumber? {
get {
return (defaults.objectForKey("product") != nil ? defaults.integerForKey("product") : nil)
}
set(newValue) {
willChangeValueForKey("product")
if newValue != product {
if let value = newValue {
defaults.setInteger(value.integerValue, forKey: "product")
} else {
defaults.removeObjectForKey("product")
}
reader.product = newValue?.integerValue
refresh()
}
didChangeValueForKey("product")
}
}
猜猜看,我会说实现是聪明的。如果它没有将调用willChangeValueForKey
视为mutator方法中的第一件事,那么它会在进入mutator之前调用它,并破坏调用。移动了这些调用后,代码现在不会进行那些默认调用,并且所有调用都正常工作
required init(coder aDecoder: NSCoder) {
}
deinit {
if annotation != nil {
Static.info.removeObserver(self, forKeyPath: "product")
}
}
override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) {
switch keyPath {
case "product":
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0)) {
self.refreshText()
self.refreshImages()
}
default:
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
}
override var annotation: MKAnnotation! {
get {
return super.annotation
}
set(newAnnotation) {
if annotation != nil {
Static.info.removeObserver(self, forKeyPath: "product")
}
super.annotation = newAnnotation
if let annot = newAnnotation as? MyAnnotation {
Static.info.addObserver(self, forKeyPath: "product", options: NSKeyValueObservingOptions.allZeros, context: nil)
self.refreshText()
self.refreshImages()
}
}
}
这听起来既聪明又愚蠢。其他意见?您必须确保致电didChangeValueForKey:和willChangeValueForKey:。您不需要重写getter和setter来触发KVO通知。我不知道extact实现,但是看看你的类,如果info类必须触发产品更改通知,那么它会是这样的
class MyInfoClass {
var product: NSNumber?{
willSet{
self.willChangeValueForKey("product")
}
didSet{
self.didChangeValueForKey("product")
}
}
override class func automaticallyNotifiesObserversForKey(key: String) -> Bool {
if key == "product"{
return true
}else{
return automaticallyNotifiesObserversForKey(key)
}
}
}
class MyInfoClass:NSObject{
dynamic var product: NSNumber?
}
但是,如果您希望使用动态,则不需要覆盖kvo方法自动通知服务器查询:,也不需要触发willChangeValueForKey:和didChangeValueForKey:。请注意,您的类应该是NSObject的子类。在这种情况下,您的类将简单地如下所示
class MyInfoClass {
var product: NSNumber?{
willSet{
self.willChangeValueForKey("product")
}
didSet{
self.didChangeValueForKey("product")
}
}
override class func automaticallyNotifiesObserversForKey(key: String) -> Bool {
if key == "product"{
return true
}else{
return automaticallyNotifiesObserversForKey(key)
}
}
}
class MyInfoClass:NSObject{
dynamic var product: NSNumber?
}
您必须确保调用didChangeValueForKey:和willChangeValueForKey:。您不需要重写getter和setter来触发KVO通知。我不知道extact实现,但是看看你的类,如果info类必须触发产品更改通知,那么它会是这样的
class MyInfoClass {
var product: NSNumber?{
willSet{
self.willChangeValueForKey("product")
}
didSet{
self.didChangeValueForKey("product")
}
}
override class func automaticallyNotifiesObserversForKey(key: String) -> Bool {
if key == "product"{
return true
}else{
return automaticallyNotifiesObserversForKey(key)
}
}
}
class MyInfoClass:NSObject{
dynamic var product: NSNumber?
}
但是,如果您希望使用动态,则不需要覆盖kvo方法自动通知服务器查询:,也不需要触发willChangeValueForKey:和didChangeValueForKey:。请注意,您的类应该是NSObject的子类。在这种情况下,您的类将简单地如下所示
class MyInfoClass {
var product: NSNumber?{
willSet{
self.willChangeValueForKey("product")
}
didSet{
self.didChangeValueForKey("product")
}
}
override class func automaticallyNotifiesObserversForKey(key: String) -> Bool {
if key == "product"{
return true
}else{
return automaticallyNotifiesObserversForKey(key)
}
}
}
class MyInfoClass:NSObject{
dynamic var product: NSNumber?
}
我无法重现您的问题(可能是因为我没有
reader
对象或refresh()
函数),但您的代码正在为产品生成两个(嵌套)KVN。如果automaticallyNotifiesObserversForKey
返回true
(这意味着产品将自动发生KVN),则不应手动为该属性发布KVN
有两种选择:
您可以关闭产品的自动KVN,然后自己动手:
dynamic var product: NSNumber? {
get {
return defaults.objectForKey("product") as? NSNumber
}
set {
if newValue != product {
willChangeValueForKey("product")
if let value = newValue {
defaults.setInteger(value.integerValue, forKey: "product")
} else {
defaults.removeObjectForKey("product")
}
didChangeValueForKey("product")
// reader.product = newValue?.integerValue
// refresh()
}
}
}
override class func automaticallyNotifiesObserversForKey(key: String) -> Bool {
if key == "product" {
return false
}
return super.automaticallyNotifiesObserversForKey(key) // prudent to call super rather than just returning `true`
}
dynamic var product: NSNumber? {
get {
return defaults.objectForKey("product") as? NSNumber
}
set {
if newValue != product {
if let value = newValue {
defaults.setInteger(value.integerValue, forKey: "product")
} else {
defaults.removeObjectForKey("product")
}
// reader.product = newValue?.integerValue
// refresh()
}
}
}
// we don't need this at all if all we're going to do is to return the super value
//
// override class func automaticallyNotifiesObserversForKey(key: String) -> Bool {
// return super.automaticallyNotifiesObserversForKey(key)
// }
您可以让自动KVN保持打开状态,然后不要自己执行:
dynamic var product: NSNumber? {
get {
return defaults.objectForKey("product") as? NSNumber
}
set {
if newValue != product {
willChangeValueForKey("product")
if let value = newValue {
defaults.setInteger(value.integerValue, forKey: "product")
} else {
defaults.removeObjectForKey("product")
}
didChangeValueForKey("product")
// reader.product = newValue?.integerValue
// refresh()
}
}
}
override class func automaticallyNotifiesObserversForKey(key: String) -> Bool {
if key == "product" {
return false
}
return super.automaticallyNotifiesObserversForKey(key) // prudent to call super rather than just returning `true`
}
dynamic var product: NSNumber? {
get {
return defaults.objectForKey("product") as? NSNumber
}
set {
if newValue != product {
if let value = newValue {
defaults.setInteger(value.integerValue, forKey: "product")
} else {
defaults.removeObjectForKey("product")
}
// reader.product = newValue?.integerValue
// refresh()
}
}
}
// we don't need this at all if all we're going to do is to return the super value
//
// override class func automaticallyNotifiesObserversForKey(key: String) -> Bool {
// return super.automaticallyNotifiesObserversForKey(key)
// }
我无法重现您的问题(可能是因为我没有reader
对象或refresh()
函数),但您的代码正在为产品生成两个(嵌套)KVN。如果automaticallyNotifiesObserversForKey
返回true
(这意味着产品将自动发生KVN),则不应手动为该属性发布KVN
有两种选择:
您可以关闭产品的自动KVN,然后自己动手:
dynamic var product: NSNumber? {
get {
return defaults.objectForKey("product") as? NSNumber
}
set {
if newValue != product {
willChangeValueForKey("product")
if let value = newValue {
defaults.setInteger(value.integerValue, forKey: "product")
} else {
defaults.removeObjectForKey("product")
}
didChangeValueForKey("product")
// reader.product = newValue?.integerValue
// refresh()
}
}
}
override class func automaticallyNotifiesObserversForKey(key: String) -> Bool {
if key == "product" {
return false
}
return super.automaticallyNotifiesObserversForKey(key) // prudent to call super rather than just returning `true`
}
dynamic var product: NSNumber? {
get {
return defaults.objectForKey("product") as? NSNumber
}
set {
if newValue != product {
if let value = newValue {
defaults.setInteger(value.integerValue, forKey: "product")
} else {
defaults.removeObjectForKey("product")
}
// reader.product = newValue?.integerValue
// refresh()
}
}
}
// we don't need this at all if all we're going to do is to return the super value
//
// override class func automaticallyNotifiesObserversForKey(key: String) -> Bool {
// return super.automaticallyNotifiesObserversForKey(key)
// }
您可以让自动KVN保持打开状态,然后不要自己执行:
dynamic var product: NSNumber? {
get {
return defaults.objectForKey("product") as? NSNumber
}
set {
if newValue != product {
willChangeValueForKey("product")
if let value = newValue {
defaults.setInteger(value.integerValue, forKey: "product")
} else {
defaults.removeObjectForKey("product")
}
didChangeValueForKey("product")
// reader.product = newValue?.integerValue
// refresh()
}
}
}
override class func automaticallyNotifiesObserversForKey(key: String) -> Bool {
if key == "product" {
return false
}
return super.automaticallyNotifiesObserversForKey(key) // prudent to call super rather than just returning `true`
}
dynamic var product: NSNumber? {
get {
return defaults.objectForKey("product") as? NSNumber
}
set {
if newValue != product {
if let value = newValue {
defaults.setInteger(value.integerValue, forKey: "product")
} else {
defaults.removeObjectForKey("product")
}
// reader.product = newValue?.integerValue
// refresh()
}
}
}
// we don't need this at all if all we're going to do is to return the super value
//
// override class func automaticallyNotifiesObserversForKey(key: String) -> Bool {
// return super.automaticallyNotifiesObserversForKey(key)
// }
我决定尝试另一种方法
dynamic var product: NSNumber? = NSUserDefaults.standardUserDefaults().objectForKey("product") as? NSNumber
override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) {
switch keyPath {
case "product":
if let value = product {
defaults.setInteger(value.integerValue, forKey: "product")
} else {
defaults.removeObjectForKey("product")
}
default:
break
}
}
init() {
self.addObserver(self, forKeyPath: "product", options: NSKeyValueObservingOptions.allZeros, context: nil)
}
deinit {
self.removeObserver(self, forKeyPath: "product")
}
动态var产品:NSNumber?=NSUserDefaults.standardUserDefaults().objectForKey(“产品”)作为?数字对象
重写func observeValueForKeyPath(键路径:字符串,对象对象:AnyObject,更改:[NSObject:AnyObject],上下文:UnsafeMutablePointer){
开关键路径{
案例“产品”:
如果let值=乘积{
defaults.setInteger(value.integerValue,forKey:“产品”)
}否则{
默认值。removeObjectForKey(“产品”)
}
违约:
打破
}
}
init(){
addObserver(self,forKeyPath:“产品”,选项:NSKeyValueObservingOptions.allZeros,上下文:nil)
}
脱硝{
self.removeObserver(self,forKeyPath:“产品”)
}
这种变化的结果是完全相同的;代码在消息分派期间崩溃。唯一的优点是,我认为这是比原来更干净的代码。我决定尝试另一种方法
dynamic var product: NSNumber? = NSUserDefaults.standardUserDefaults().objectForKey("product") as? NSNumber
override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) {
switch keyPath {
case "product":
if let value = product {
defaults.setInteger(value.integerValue, forKey: "product")
} else {
defaults.removeObjectForKey("product")
}
default:
break
}
}
init() {
self.addObserver(self, forKeyPath: "product", options: NSKeyValueObservingOptions.allZeros, context: nil)
}
deinit {
self.removeObserver(self, forKeyPath: "product")
}
动态var产品:NSNumber?=NSUserDefaults.standardUserDefaults().objectForKey(“产品”)作为?数字对象
重写func observeValueForKeyPath(键路径:字符串,对象对象:AnyObject,更改:[NSObject:AnyObject],上下文:UnsafeMutablePointer){
开关键路径{
案例“产品”:
如果let值=乘积{
defaults.setInteger(value.integerValue,forKey:“产品”)
}否则{
默认值。removeObjectForKey(“产品”)
}
违约:
打破
}
}
init(){
addObserver(self,forKeyPath:“产品”,选项:NSKeyValueObservingOptions.allZeros,上下文:nil)
}
脱硝{
self.removeObserver(self,forKeyPath:“produ