使用removeObserver:forKeyPath:在Foundation中崩溃

问题描述 投票:20回答:3

我对从Xcode的“崩溃”部分中检索到的以下崩溃日志有一些问题。此崩溃报告仅影响少数设备。

我已经分析了问题,但我认为这是Apple框架上的错误。但是我找不到复制它的方法。

这里有类似的讨论:Help with crash in removeObserver:forKeyPath:

任何提示?

线程0名称:线程0损坏:

0基金会0x23507591 _NSKeyValueReplaceObservationInfoForObject + 69(NSKeyValueObserving.m:1166)

1基金会0x23506fe7-[NSObject(NSKeyValueObserverRegistration)_removeObserver:forProperty:] + 327(NSKeyValueObserving.m:1552)

2基金会0x23506b03-[NSObject(NSKeyValueObserverRegistration)removeObserver:forKeyPath:] + 163(NSKeyValueObserving.m:1696)

3基金会0x235069a7-[NSObject(NSKeyValueObserverRegistration)removeObserver:forKeyPath:context:] + 219(NSKeyValueObserving.m:1663)

4 ApplicationName0x0002e233-[主管removeObjectObserver:forKeyPath:] + 115(Supervisor.m:344)

removeObjectObserver:forKeyPath:在哪里

- (void) removeObjectObserver:(id)object forKeyPath:(NSString *)keyPath { 

    @try {        
        [object removeObserver:self forKeyPath:keyPath context:PrivateKVOContext];

    } @catch (NSException *exception) { }
}
objective-c crash key-value-observing
3个回答
8
投票

Observers中的[Objective-C必须格外注意:不要将相同的观察者时间乘以相同的对象的属性,如果有一个,则将移除内容包装起来:

  if ([self observationInfo]) {
        @try {
            [self removeObserver:self forKeyPath:keyPath];
        }
        @catch (NSException *exception) {}
    }

您遇到崩溃是因为尝试删除两次观察者,或者正在移除不存在的观察者。

您应该以这种方式添加observers

[yourObject addObserver:self forKeyPath:keypath options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionInitial context:nil/yourContext];

编辑:您可能会删除已经取消分配的对象上的观察者,从而导致崩溃。

  if (object && [self observationInfo]) {
    @try {
                [self removeObserver:self forKeyPath:keyPath];
            }
            @catch (NSException *exception) {}
}

5
投票

通常,您有一个ivar,可以知道您是否正在观察对象的键路径。就像@property(...)BOOL textFieldTextObserving;并且您的添加/删除观察方法应该在添加/删除之前检查此属性,以避免两次添加/删除观察者。如果有许多观察对象和键路径(也可以将@(BOOL)保留为对象,将-identifiers保留为键),则也可以使用NSDictionary。

无论如何,不​​建议使用@ try-exception做事情。苹果文档说:

"You should not use a try-catch block in place of standard programming checks for Objective-C methods. In the case of an NSArray, for example, you should always check the array’s count to determine the number of items before trying to access an object at a given index. The objectAtIndex: method throws an exception if you make an out-of-bounds request so that you can find the bug in your code early in the development cycle—you should avoid throwing exceptions in an app that you ship to users."https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/ErrorHandling/ErrorHandling.html


0
投票

给出答案为时已晚,但我面临着同样的问题。所以我决定为别人写这个。

注意:崩溃的主要原因是您尝试在添加之前删除观察者。

我创建了一些扩展名,可以帮助您安全地删除观察者。雨燕5。

您现在可以在添加它之前将其删除,而不会崩溃。确保还删除了deinit中的观察者。

用法:

objectToObserve.safeRemoveObserver(self, keyPath: "myDate", context: &myContext)

扩展名:

extension NSRegularExpression {

convenience init(_ pattern: String) {
    do {
        try self.init(pattern: pattern)
    } catch {
        preconditionFailure("Illegal regular expression: \(pattern).")
    }
}

func matches(_ string: String) -> Bool {
    let range = NSRange(location: 0, length: string.utf16.count)
    return firstMatch(in: string, options: [], range: range) != nil
   }
 }

extension NSObject {

func safeRemoveObserver(_ observer: NSObject, keyPath: String, context: inout Int) {
    let result = checkIfAlreadyAdded(keyPath: keyPath, context: &context)

    if result {
        removeObserver(observer, forKeyPath: keyPath, context: &context)
    }
}

fileprivate func address(_ o: UnsafeRawPointer) -> Int {
    return Int(bitPattern: o)
}

fileprivate func checkIfAlreadyAdded(keyPath: String, context: inout Int) -> Bool {

    guard self.observationInfo != nil else { return false }

    let info = Unmanaged<AnyObject>
           .fromOpaque(self.observationInfo!)
           .takeUnretainedValue()

    let contextStr = NSString(format: "%p", address(&context))
    let infoStr = info.description ?? ""

    let regex = NSRegularExpression("\(keyPath).*[a-z].*\(contextStr)")
    let result = regex.matches(infoStr)

    return result
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.