2014-09-02 1 views
3

정말 어려운 문제를 해결했습니다. 내 iOS 앱 (iOS 버전 6 이상, Xcode 5.1.1)은 사용자가 자신의 계정에서 로그 아웃 할 때 충돌이 발생하지만 이전에 forgrounded 및 backgrounded 상태 인 경우에만 충돌합니다.copy__destroy_helper_block_ 행 0의 충돌

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 

은 엑스 코드에서이 그러나 AppDelegate에 파일에 EXC_BAD_ACCESS으로 충돌한다 :

는 Testflight에서 crashlog입니다. (더 자세한 정보는 제공하지 않음). NSZombie를 사용하도록 설정하면 앱이 켜져있는 동안 앱이 다운되는 것을 방지하므로 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이 코드에

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

정의 ... 이상이라고

_block = [block copy]; 

을 ... :

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

...

... 그리고 _block는 다음과 같이 해제 :

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

잘못하거나하는 방법이 더 디버깅하기 위해 어떤 생각?

편집 : ARC를 사용하고 있는데 왜 이런 방식으로 구현되는지 대답 할 수 없으므로 기존 프로젝트를 인수했습니다.

+0

1.'(void (^) (NSError * error)) 블록의 블록이'WCWebServiceServiceStatusBlock_Invoke'에서 호출됩니까? –

+0

2. 왜 ivar _block을'WCWebServiceServiceStatusBlock'으로 정의하지 않습니까? –

+0

3. ARC를 사용한다면'copy'가 필요없고 dealloc이 필요 없습니다. 그렇지 않다면 dealloc은 다음과 같을 것이다 :'- (void) dealloc {[_block release]; [super dealloc];} –

답변

2

이 문제는 많은 문제와 관련이 있습니다. 할당 해제 그들이 어디에 전에 (금주 모임 BOOL의 + 체크를 통해) "무시"

  • 제작해야합니다 KVO 리스너를 제거 곳

    • 모든 NSNotificationsCenter 등록 확인 : 여기에 내가 찍은 단계는 결국 문제를 제거 할 수 있습니다

    관련 새로운 코드 :

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