第0行copy__destroy_helper_block_崩溃

时间:2014-09-02 10:11:09

标签: ios objective-c xcode crash objective-c-blocks

真的很难解决。当用户从他的帐户退出时,我的iOS应用程序(iOS版本6+,Xcode 5.1.1)崩溃了,但只有在它之前被暂停和后台运行时才会崩溃。

这是来自Testflight的崩溃日志:

SIGSEGV
APP_NAME copy__destroy_helper_block_ 
in CAPServiceManager.m on Line 0

#   Binary Image Name   Address Symbol
0   APP_NAME copy   0x0010b61c  testflight_backtrace
1   APP_NAME copy   0x0010ae5c  TFSignalHandler
2   libsystem_platform.dylib    0x33d0087a  _sigtramp
3   APP_NAME copy   0x000f180c  __destroy_helper_block_ in CAPServiceManager.m on Line 0
4   libsystem_blocks.dylib  0x33bdbae0  _Block_release
5   Foundation  0x27268eb8  
6   libobjc.A.dylib 0x33650d5e  
7   Foundation  0x272ff372  
8   libdispatch.dylib   0x33ba295e  
9   libdispatch.dylib   0x33ba5ba6  _dispatch_main_queue_callback_4CF
10  CoreFoundation  0x2655fbd8  
11  CoreFoundation  0x2655e2d8  
12  CoreFoundation  0x264ac610  CFRunLoopRunSpecific
13  CoreFoundation  0x264ac422  CFRunLoopRunInMode
14  GraphicsServices    0x2da060a8  GSEventRunModal
15  UIKit   0x29bf6484  UIApplicationMain
16  APP_NAME copy   0x0009587a  main in main.m on Line 16
17  libdyld.dylib   0x33bc0aae  

然而,在Xcode中,它在AppDelegate文件中作为EXC_BAD_ACCESS崩溃。 (没有提供更多细节)。启用NSZombie不起作用,因为它会阻止应用程序在启用时崩溃。

CAPServiceManager中与块相关的代码是:

- (void)executeService:(WCServiceType)service
                   pin:(NSString *)pin
               payLoad:(id)payload
            usingBlock:(void (^) (NSError *error))block
{
    [self addStartingServiceStatusForService:service];

    [[WCWebService sharedWebService]
     postAuthTokenForService:service
     pin:pin
     vehicle:_vehicle
     target:self
     usingBlock:^(NSError *error, id response) {
         if (!error) {
             [self executeService:service token:response payLoad:payload usingBlock:block];
         }
         else {
             if (error.code == kWCHTTPStatusCodeUnauthorized) {
                 _wrongPinCounter++;
             }

             [self failStartingServiceStatusForServiceType:service error:error serviceAlreadyStarted:NO];
             block(error);
         }
     }];
}

- (void)executeService:(WCServiceType)service
                 token:(NSString *)token
               payLoad:(id)payload
            usingBlock:(void (^) (NSError *error))block
{
    [self addStartingServiceStatusForService:service];

    [[WCWebService sharedWebService]
     postStartServiceWithTarget:self
     service:service
     payLoad:payload
     token:token
     vehicle:_vehicle
     usingBlock:^(id target, NSError *error, WCServiceStatus *serviceStatus) {

         if (!error) {
             WCServiceStatus *startingServiceStatus = [self serviceStatusForService:service];

             NSManagedObjectContext *moc = _vehicle.managedObjectContext;
             [moc performBlockAndWait:^{
                 startingServiceStatus.sentPayload = payload;
                 [startingServiceStatus updateWithServiceStatus:serviceStatus];
             }];

             block(nil);
         }
         else {
             if (error.localizedDescription && [error.localizedDescription isEqualToString:@"Service is already started"]) {
                 [self serviceIsAlreadyStartedForServiceType:service block:^(NSError *error) {
                     if (error) {
                         [self failStartingServiceStatusForServiceType:service error:error serviceAlreadyStarted:YES];
                     }
                     block(error);
                 }];
             }
             else {
                 [self failStartingServiceStatusForServiceType:service error:error serviceAlreadyStarted:NO];
                 block(error);
             }
         }
     }];
}

我首先认为错误与自我有关。但是测试存储自己也不起作用:

__weak CAPServiceManager *weakSelf = self;

也没用。我还尝试__block作为修饰语。

如您所见,传递块。然后将它们存储在WCWebServiceRequest.m中的实例变量中,如下所示:

_block = [block copy];

... _block定义为

@interface WCWebServiceRequest : NSObject <NSURLConnectionDelegate, NSURLConnectionDataDelegate> {
@protected
    id _block;
    //...

...后来用这段代码调用了:

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    NSOperationQueue *queue = [NSOperationQueue new];
    queue.name = [NSString stringWithFormat:@"%s Queue", __PRETTY_FUNCTION__];

    [queue addOperationWithBlock:^{
        NSError *error = nil;
        NSDictionary *attributes;
        __block WCServiceStatus *serviceStatus;

        if (_data) {
            attributes = [NSJSONSerialization JSONObjectWithData:_data options:0 error:&error];
        }

        if (self.response.statusCode == kWCHTTPStatusCodeAccepted || self.response.statusCode == kWCHTTPStatusCodeOK) {
            if ([attributes isKindOfClass:[NSDictionary class]]) {
                NSManagedObjectContext *moc = [WCStorage sharedStorage].moc;
                [moc performBlockAndWait:^{
                    serviceStatus = [WCServiceStatus makeServiceStatusWithAttributes:attributes moc:moc];
                }];
            }
            else {
                error = [NSError errorWithWebServiceErrorCode:kWCHTTPStatusCodeInvalidData];
            }
        }
        else {
            error = [NSError errorWithWebServiceErrorCode:self.response.statusCode errorInfo:[NSError errorInfoFromData:_data]];
        }

        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            if (!_cancelled) {
                WCWebServiceServiceStatusBlock_Invoke(_block, _target, error, serviceStatus);
            }

            [super connectionDidFinishLoading:connection];
        }];
    }];
}

...其中WCWebServiceServiceStatusBlock_Invoke定义为

#define WCWebServiceServiceStatusBlock_Invoke(block, target, error, serviceStatus) \
{ \
    WCWebServiceServiceStatusBlock block_ = (WCWebServiceServiceStatusBlock) block; \
    block_(target, error, serviceStatus); \
}

typedef void (^WCWebServiceServiceStatusBlock)              (id target, NSError *error, WCServiceStatus *serviceStatus);

...和_block如此解除分配:

- (void)dealloc
{
    if (_block) {
        _block = nil;
    }
}

知道可能出现什么问题或如何进一步调试?

编辑:我正在使用ARC,我无法回答为什么以这种方式实现,我接管了现有的项目。

1 个答案:

答案 0 :(得分:2)

此问题与许多问题有关。以下是我最终解决问题的步骤:

  • 确保所有NSNotificationsCenter注册都在dealloc
  • 中删除
  • 确保KVO听众在哪里被忽视&#34; (通过BOOL +检查)在删除之前(不触发干扰注销的代码)
  • 将取消注册代码(包括清除CoreData并取消注册通知和网络请求)移至单独的队列:

相关新代码:

- (void)userDidSignOut
{
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        [[NSNotificationCenter defaultCenter] removeObserver:self];
        [[WCWebService sharedWebService] signOut];

        [_weatherRefresher stop];
        [_TARefreshTimer invalidate];
        [CAPSVTMessageView hide];

        [[WCStorage sharedStorage].validV enumerateObjectsUsingBlock:^(WCV *obj, NSUInteger idx, BOOL *stop) {
            [self unregisterFromPushNotificationsForVehicle:obj];
            [obj.VHSRefresher kill];
            [obj.serviceManager kill];
        }];

        NSOperationQueue *queue = [NSOperationQueue new];
        queue.name = [NSString stringWithFormat:@"%s Queue", __PRETTY_FUNCTION__];

        [queue addOperationWithBlock:^{
            [[WCStorage sharedStorage] clearDatabase];
            [WCStorage sharedStorage].sessionPassword = nil;
            _signInFromBackround = NO;
        }];
    }];
}