Copy_destroy_helper_block_ crash on line 0

It was a very hard fall. My iOS app (iOS version 6+, Xcode 5.1.1) crashed when the user logged out of their account, but only when it is pre-configured and pre-installed.

This is the crash log from 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  

      

In Xcode, however, it crashes as EXC_BAD_ACCESS in the AppDelegate. (without giving more details). Enabling NSZombie does not work as it prevents the application from crashing while it is being enabled.

The code in CAPServiceManager related to blocks is as follows:

- (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);
             }
         }
     }];
}

      

At first I thought the error was self-related. But testing to store itself, since that doesn't work either:

__weak CAPServiceManager *weakSelf = self;

      

doesn't work either. I have also tried __block

as a modifier.

The blocks are being transferred as you can see. They are then stored in an instance variable in WCWebServiceRequest.m like this:

_block = [block copy];

      

... where _block

is defined as

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

      

... and later called with this code:

- (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];
        }];
    }];
}

      

... where WCWebServiceServiceStatusBlock_Invoke is defined as

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

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

      

... and _block are freed like this:

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

      

Any idea what might be wrong or how to debug this further?

Edit: I am using ARC and I cannot answer why it is implemented this way, I took over an existing project.

+3


source to share


1 answer


This problem has been associated with many problems. Here are the steps I took to finally get rid of the problem:

  • Make sure all NSNotificationsCenter registrations removed in dealloc
  • Make sure KVO listeners are "ignored" (via BOOL + check) before they are removed (so as not to run code that interfered with the logout)
  • Moved registration code (including clearing CoreData and unregistering notifications and network request) to split queues:


Related new code:

- (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;
        }];
    }];
}

      

+2


source







All Articles