2014-09-02 62 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; 
     }]; 
    }]; 
}