2014-09-02 336 views
3

遇到了一个非常难以解决的崩溃。我的iOS应用程序(iOS版本6+,Xcode 5.1.1)在用户从他的帐户注销时崩溃,但仅当它在前一个被禁止接地和后台接入时才会崩溃。copy__destroy_helper_block_第0行崩溃

这是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,我无法回答为什么它是以这种方式实现的,我接管了一个现有的项目。

+0

1.在'WCWebServiceServiceStatusBlock_Invoke'中调用'(void(^)(NSError * error))block'吗? – 2014-09-02 11:27:16

+0

2.为什么不将ivar _block定义为'WCWebServiceServiceStatusBlock'? – 2014-09-02 11:28:48

+0

3.如果你使用ARC,那么你不需要'copy'并且不需要dealloc。如果没有,那么dealloc将是:' - (void)dealloc {[_block release]; [super dealloc];}' – 2014-09-02 11:30:43

回答

2

此问题与许多问题有关。下面是我的步骤,终于摆脱了问题:

  • 确保所有NSNotificationsCenter注册其中的dealloc删除
  • 确信志愿听众,他们在那里之前,其中“忽略”(通过AA 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; 
     }]; 
    }]; 
}