【问题标题】:copy__destroy_helper_block_ crash on row 0第 0 行的 copy__destroy_helper_block_ 崩溃
【发布时间】:2014-09-02 10:11:08
【问题描述】:

遇到了一个很难解决的崩溃问题。我的 iOS 应用程序(iOS 版本 6+,Xcode 5.1.1)在用户从他的帐户注销时崩溃,但只有在它之前被前台和后台关闭时才会崩溃。

这是来自 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中与block相关的代码如下:

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

我首先认为错误与自我有关。但是测试存储 self 也不起作用:

__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,我无法回答为什么要这样实现,我接手了一个现有项目。

【问题讨论】:

  • 1.来自(void (^) (NSError *error))block的块是否在WCWebServiceServiceStatusBlock_Invoke中被调用?
  • 2.为什么不将ivar _block定义为WCWebServiceServiceStatusBlock
  • 3.如果你使用ARC,那么你不需要copy,也不需要dealloc。如果不是,那么 dealloc 将是:- (void) dealloc { [_block release]; [super dealloc];}
  • 感谢您的回复。 1.是的,它被称为,请参阅我问题中的倒数第二个代码部分。 2. 我会试试的。 3.将尝试删除dealloc并复制。然而,类似的问题似乎与 not 复制您的块有关。 IE。 stackoverflow.com/questions/23573114/…
  • 删除 [block copy] 或删除 dealloc 对我的问题没有任何影响。我无法更改 _block 的类型,因为它并不总是存储 WCWebServiceServiceStatusBlock(其他类继承该类)。

标签: ios objective-c xcode crash objective-c-blocks


【解决方案1】:

这个问题与许多问题有关。以下是我最终解决问题所采取的步骤:

  • 确保在 dealloc 中删除所有 NSNotificationsCenter 注册
  • 确保 KVO 侦听器在被删除之前被“忽略”(通过 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;
        }];
    }];
}

【讨论】:

    猜你喜欢
    • 2019-05-21
    • 2015-07-16
    • 2022-11-02
    • 2015-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多