Xcodeの「クラッシュ」セクションから取得した次のクラッシュログに問題があります。このクラッシュレポートの影響を受けるデバイスはごくわずかです。
問題を分析しましたが、Appleフレームワークのバグだと思います。しかし、それを再現する方法が見つかりません。
ここで同様の議論: 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) { }
}
Observers
in Objective-C
は特に注意して使用する必要があります。同じオブザーバーを同じオブジェクトのプロパティに複数回追加しないでください。削除があればラップします。
if ([self observationInfo]) {
@try {
[self removeObserver:self forKeyPath:keyPath];
}
@catch (NSException *exception) {}
}
オブザーバーを2回削除しようとしたか、存在しないオブザーバーを削除しようとしたために、クラッシュが発生しています。
この方法で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;また、add/remove-observingメソッドは、オブザーバーを2回追加/削除しないように、追加/削除する前にこのプロパティを確認する必要があります。監視オブジェクトとキーパスが多数ある場合は、NSDictionaryを使用することもできます(@(BOOL)をオブジェクトとして保持し、-識別子をキーとして保持するため)。
とにかく、@ 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
答えるには遅すぎますが、私は同じ問題に直面しています。だから私は他の人のためにこれを書くことにしました。
注:クラッシュの主な理由は、追加する前にオブザーバーを削除しようとすることです。
オブザーバーを安全に削除するのに役立つ拡張機能をいくつか作成しました。 Swift 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
}
}