정말 어려운 문제를 해결했습니다. 내 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를 사용하고 있는데 왜 이런 방식으로 구현되는지 대답 할 수 없으므로 기존 프로젝트를 인수했습니다.
1.'(void (^) (NSError * error)) 블록의 블록이'WCWebServiceServiceStatusBlock_Invoke'에서 호출됩니까? –
2. 왜 ivar _block을'WCWebServiceServiceStatusBlock'으로 정의하지 않습니까? –
3. ARC를 사용한다면'copy'가 필요없고 dealloc이 필요 없습니다. 그렇지 않다면 dealloc은 다음과 같을 것이다 :'- (void) dealloc {[_block release]; [super dealloc];} –