我遇到了从" Crashes"中检索到的以下崩溃日志的一些问题。 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 ApplicationName 0x0002e233 - [Supervisor 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) { }
}
答案 0 :(得分: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) {}
}
答案 1 :(得分:5)
通常你有一个ivar能够知道你对象的关键路径是否在此刻观察。像@property(...)BOOL textFieldTextObserving; 并且您的添加/删除观察方法应该在添加/删除之前检查此属性,以避免添加/删除观察者两次。 如果有许多观察对象和键路径(将@(BOOL)保留为对象和 - 标识符作为键),也可以使用NSDictionary。
无论如何,使用@ try-exception做事并不是推荐的Objective-C方式。 Apple文档说:
"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
答案 2 :(得分: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
}
}