我对从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) { }
}
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) {}
}
通常,您有一个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
给出答案为时已晚,但我面临着同样的问题。所以我决定为别人写这个。
注意:崩溃的主要原因是您尝试在添加之前删除观察者。
我创建了一些扩展名,可以帮助您安全地删除观察者。雨燕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
}
}