NSOperationQueue 导致 Crashlytics 崩溃(QOS:未指定)
Crashlytics Crash with NSOperationQueue (QOS: UNSPECIFIED)
我遇到了崩溃,在 Crashlytics 中报告过,我不知道如何重现错误,它是随机发生的,所以很难用 Xcode 进行调试。有什么想法吗?
Crashed: NSOperationQueue 0x280419200 (QOS: UNSPECIFIED)
0 libobjc.A.dylib 0x22c471430 objc_retain + 16
1 CoreFoundation 0x22d2b5888 __CFBasicHashAddValue + 1480
2 CoreFoundation 0x22d1e64ac CFDictionarySetValue + 260
3 Foundation 0x22dd04888 _encodeObject + 732
4 myAPI 0x1062b44b0 -[DataCore encodeWithCoder:] (DataCore.m:236)
5 myAPI 0x1062909c4 -[DataHandle encodeWithCoder:] (DataHandle.m:53)
6 Foundation 0x22dd04aa8 _encodeObject + 1276
7 Foundation 0x22dc69c6c +[NSKeyedArchiver archivedDataWithRootObject:] + 168
8 myAPI 0x106288a34 __77+[CachableObject addObjectToCache:withCacheName:withTTL:withCompletionBlock:]_block_invoke (CachableObject.m:162)
9 Foundation 0x22dd198bc NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK + 16
10 Foundation 0x22dc21ab8 -[NSBlockOperation main] + 72
11 Foundation 0x22dc20f8c -[__NSOperationInternal _start:] + 740
12 Foundation 0x22dd1b790 __NSOQSchedule_f + 272
13 libdispatch.dylib 0x22ccc16c8 _dispatch_call_block_and_release + 24
14 libdispatch.dylib 0x22ccc2484 _dispatch_client_callout + 16
15 libdispatch.dylib 0x22cc6582c _dispatch_continuation_pop$VARIANT$mp + 412
16 libdispatch.dylib 0x22cc64ef4 _dispatch_async_redirect_invoke + 600
17 libdispatch.dylib 0x22cc71a18 _dispatch_root_queue_drain + 376
18 libdispatch.dylib 0x22cc722c0 _dispatch_worker_thread2 + 128
19 libsystem_pthread.dylib 0x22cea517c _pthread_wqthread + 472
20 libsystem_pthread.dylib 0x22cea7cec start_wqthread + 4
DataCore.m 中的代码如下所示
- (void)encodeWithCoder:(NSCoder *)coder {
[super encodeWithCoder:coder];
[coder encodeObject:programFormatPlayInfo forKey:@"ProgramFormatPlayInfo"];
[coder encodeObject:bigScreenPlayInfo forKey:@"BigScreenPlayInfo"];
[coder encodeObject:pivotHandle forKey:@"PivotHandle"];
[coder encodeInteger:pivotDataLinkId forKey:@"PivotDataLinkId"];
[coder encodeInteger:viewContextId forKey:@"ViewContextId"];
[coder encodeBool:suppressImagePivot forKey:@"SuppressImagePivot"];
[coder encodeObject:attributeIds forKey:@"AttributeIds"];
[coder encodeObject:self.overflow forKey:@"Overflow"];
[coder encodeObject:self.cacheNameWithUser forKey:@"CacheNameWithUser"];
[coder encodeObject:self.metaData forKey:@"Metadata"];
}
这里是我尝试将对象添加到缓存的地方,
不确定是解码失败还是跟后台队列有关。
+ (void)addObjectToCache:(CachableObject*)object withCacheName:(NSString*)cacheName withTTL:(CacheTime)cacheTimeSeconds withCompletionBlock:(void(^)()) block {
CachableObject* theObject = object;
[_backgroundQueue addOperationWithBlock:^{
@autoreleasepool {
@try {
NSString * path = [CachableObject pathForCachedObject:cacheName];
NSDate * date = [NSDate date];
[object setCacheDate:date];
[object setTtlSeconds:[NSNumber numberWithInteger:cacheTimeSeconds]];
[object setApiVersion:APIVERSION];
// Add to NSCache
[[CachableObject objectCache] setObject:theObject forKey:cacheName];
// Add to file system
NSError* err = nil;
NSData * data = [NSKeyedArchiver archivedDataWithRootObject:theObject];
if (data) {
[data writeToFile:path options:NSDataWritingAtomic error:&err];
}
// Add to dynamic cache
unwrapObjectAndComplyWithClass(object, [DataHandle class], ^(id unwrappedObject) {
DataHandle *objectUnwrapped = unwrappedObject;
DataFrame *objectFrame = objectUnwrapped.frame;
for (NSString *eachDependencyName in objectFrame.dependencies) {
[[VVIDynamicCacheManager sharedManager]addDependencyToStore:eachDependencyName withCacheName:cacheName];
}
}, ^{
/*Not a data handle*/
});
} @catch (NSException* ex) {
NSLog(@"CachableObject: got an exception %@", ex);
} @finally {
if (block) {
block();
}
}
}
}];
}
这里有一些想法。
首先,您正在使用@try/@catch。我相信 NSKeyedArchiver
在它实际上应该 return NSError
对象时抛出异常。所以,也许这就是你这样做的原因。但是,您必须记住,none 的 Apple 框架保证是 exception-safe。这意味着捕获异常可能会使 Apple 的代码(当然还有您自己的代码)处于不一致的状态,这将阻止它在未来正常工作。
我强烈建议您删除 @catch,或者在 NSKeyedArchiver
代码周围非常严格地限定它的范围,如果这就是您使用它的原因。这可能会无意中将各种其他错误引入您的应用程序。
现在,我们来谈谈具体的崩溃。它发生在运行时的内存管理代码中。这非常强烈地指向堆损坏。这意味着您有一个指针未指向内存中的有效 Objective-C 对象。发生这种情况的原因有很多,极为常见。最常见的原因称为悬挂指针。但是,它也可能是由过度释放引起的。而且,如果可以使用 @catch 来触发过度释放,我一点也不会感到惊讶。 (我知道,我在喋喋不休,但我已经看到 所以 许多问题是由这种模式引起的)
在这些情况下我通常推荐的是:
- 寻找其他看起来与堆损坏相关的崩溃
- 在 Instruments
中试用 Zombies
- 试试 malloc scribble 或 guardmalloc,这是另外两个不错的内存调试工具
很难,通常甚至不可能推断出堆损坏。复制错误也是不可能的,因为内存损坏通常是不确定的。
因此,请尝试尽可能多地查找和解决问题。完全有可能其中之一导致了各种崩溃,其中之一可能就是这个。
我遇到了崩溃,在 Crashlytics 中报告过,我不知道如何重现错误,它是随机发生的,所以很难用 Xcode 进行调试。有什么想法吗?
Crashed: NSOperationQueue 0x280419200 (QOS: UNSPECIFIED)
0 libobjc.A.dylib 0x22c471430 objc_retain + 16
1 CoreFoundation 0x22d2b5888 __CFBasicHashAddValue + 1480
2 CoreFoundation 0x22d1e64ac CFDictionarySetValue + 260
3 Foundation 0x22dd04888 _encodeObject + 732
4 myAPI 0x1062b44b0 -[DataCore encodeWithCoder:] (DataCore.m:236)
5 myAPI 0x1062909c4 -[DataHandle encodeWithCoder:] (DataHandle.m:53)
6 Foundation 0x22dd04aa8 _encodeObject + 1276
7 Foundation 0x22dc69c6c +[NSKeyedArchiver archivedDataWithRootObject:] + 168
8 myAPI 0x106288a34 __77+[CachableObject addObjectToCache:withCacheName:withTTL:withCompletionBlock:]_block_invoke (CachableObject.m:162)
9 Foundation 0x22dd198bc NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK + 16
10 Foundation 0x22dc21ab8 -[NSBlockOperation main] + 72
11 Foundation 0x22dc20f8c -[__NSOperationInternal _start:] + 740
12 Foundation 0x22dd1b790 __NSOQSchedule_f + 272
13 libdispatch.dylib 0x22ccc16c8 _dispatch_call_block_and_release + 24
14 libdispatch.dylib 0x22ccc2484 _dispatch_client_callout + 16
15 libdispatch.dylib 0x22cc6582c _dispatch_continuation_pop$VARIANT$mp + 412
16 libdispatch.dylib 0x22cc64ef4 _dispatch_async_redirect_invoke + 600
17 libdispatch.dylib 0x22cc71a18 _dispatch_root_queue_drain + 376
18 libdispatch.dylib 0x22cc722c0 _dispatch_worker_thread2 + 128
19 libsystem_pthread.dylib 0x22cea517c _pthread_wqthread + 472
20 libsystem_pthread.dylib 0x22cea7cec start_wqthread + 4
DataCore.m 中的代码如下所示
- (void)encodeWithCoder:(NSCoder *)coder {
[super encodeWithCoder:coder];
[coder encodeObject:programFormatPlayInfo forKey:@"ProgramFormatPlayInfo"];
[coder encodeObject:bigScreenPlayInfo forKey:@"BigScreenPlayInfo"];
[coder encodeObject:pivotHandle forKey:@"PivotHandle"];
[coder encodeInteger:pivotDataLinkId forKey:@"PivotDataLinkId"];
[coder encodeInteger:viewContextId forKey:@"ViewContextId"];
[coder encodeBool:suppressImagePivot forKey:@"SuppressImagePivot"];
[coder encodeObject:attributeIds forKey:@"AttributeIds"];
[coder encodeObject:self.overflow forKey:@"Overflow"];
[coder encodeObject:self.cacheNameWithUser forKey:@"CacheNameWithUser"];
[coder encodeObject:self.metaData forKey:@"Metadata"];
}
这里是我尝试将对象添加到缓存的地方, 不确定是解码失败还是跟后台队列有关。
+ (void)addObjectToCache:(CachableObject*)object withCacheName:(NSString*)cacheName withTTL:(CacheTime)cacheTimeSeconds withCompletionBlock:(void(^)()) block {
CachableObject* theObject = object;
[_backgroundQueue addOperationWithBlock:^{
@autoreleasepool {
@try {
NSString * path = [CachableObject pathForCachedObject:cacheName];
NSDate * date = [NSDate date];
[object setCacheDate:date];
[object setTtlSeconds:[NSNumber numberWithInteger:cacheTimeSeconds]];
[object setApiVersion:APIVERSION];
// Add to NSCache
[[CachableObject objectCache] setObject:theObject forKey:cacheName];
// Add to file system
NSError* err = nil;
NSData * data = [NSKeyedArchiver archivedDataWithRootObject:theObject];
if (data) {
[data writeToFile:path options:NSDataWritingAtomic error:&err];
}
// Add to dynamic cache
unwrapObjectAndComplyWithClass(object, [DataHandle class], ^(id unwrappedObject) {
DataHandle *objectUnwrapped = unwrappedObject;
DataFrame *objectFrame = objectUnwrapped.frame;
for (NSString *eachDependencyName in objectFrame.dependencies) {
[[VVIDynamicCacheManager sharedManager]addDependencyToStore:eachDependencyName withCacheName:cacheName];
}
}, ^{
/*Not a data handle*/
});
} @catch (NSException* ex) {
NSLog(@"CachableObject: got an exception %@", ex);
} @finally {
if (block) {
block();
}
}
}
}];
}
这里有一些想法。
首先,您正在使用@try/@catch。我相信 NSKeyedArchiver
在它实际上应该 return NSError
对象时抛出异常。所以,也许这就是你这样做的原因。但是,您必须记住,none 的 Apple 框架保证是 exception-safe。这意味着捕获异常可能会使 Apple 的代码(当然还有您自己的代码)处于不一致的状态,这将阻止它在未来正常工作。
我强烈建议您删除 @catch,或者在 NSKeyedArchiver
代码周围非常严格地限定它的范围,如果这就是您使用它的原因。这可能会无意中将各种其他错误引入您的应用程序。
现在,我们来谈谈具体的崩溃。它发生在运行时的内存管理代码中。这非常强烈地指向堆损坏。这意味着您有一个指针未指向内存中的有效 Objective-C 对象。发生这种情况的原因有很多,极为常见。最常见的原因称为悬挂指针。但是,它也可能是由过度释放引起的。而且,如果可以使用 @catch 来触发过度释放,我一点也不会感到惊讶。 (我知道,我在喋喋不休,但我已经看到 所以 许多问题是由这种模式引起的)
在这些情况下我通常推荐的是:
- 寻找其他看起来与堆损坏相关的崩溃
- 在 Instruments 中试用
- 试试 malloc scribble 或 guardmalloc,这是另外两个不错的内存调试工具
Zombies
很难,通常甚至不可能推断出堆损坏。复制错误也是不可能的,因为内存损坏通常是不确定的。
因此,请尝试尽可能多地查找和解决问题。完全有可能其中之一导致了各种崩溃,其中之一可能就是这个。