Ios 目标-C在_销毁_辅助块上崩溃_

Ios 目标-C在_销毁_辅助块上崩溃_,ios,objective-c,objective-c-blocks,Ios,Objective C,Objective C Blocks,我有一个iOS应用程序在调用时崩溃,比如\uuu destroy\u helper\u block\u 253和\uuuu destroy\u helper\u block\u 278,我不确定“destroy\u helper\u block”指的是什么,或者后面的数字应该指向什么 有没有人对如何追踪这些崩溃可能发生的确切地点有什么建议 下面是一个回溯示例(请注意,带有\uuuuu destroy\u helper\u block的行仅引用它所包含的文件,而不引用其他内容,而通常也会包括行号)

我有一个iOS应用程序在调用时崩溃,比如
\uuu destroy\u helper\u block\u 253
\uuuu destroy\u helper\u block\u 278
,我不确定“destroy\u helper\u block”指的是什么,或者后面的数字应该指向什么

有没有人对如何追踪这些崩溃可能发生的确切地点有什么建议

下面是一个回溯示例(请注意,带有
\uuuuu destroy\u helper\u block
的行仅引用它所包含的文件,而不引用其他内容,而通常也会包括行号)

编辑1:下面是发生崩溃的文件中定义的一个块的示例(编辑掉了特定于应用程序的代码)

文件中还有许多其他块,但大多数块都作为方法的参数提供,而不是先定义然后再引用


编辑2:为上面的函数添加了更多的上下文。

我没有看到发布的代码有任何错误,并且认为错误在其他地方

此外,嵌套块似乎是不必要的,会使内存管理复杂化,可能会使查找崩溃原因变得更加困难


为什么不先将
ExampleBlock
中的代码直接移动到
completion
块?

这里没有太多内容,但我怀疑该块永远不会移动到堆中。默认情况下,将在堆栈上创建块。编译器通常可以确定何时将它们移动到堆中,但是从一个块到另一个块的处理方式可能永远不会这样做

我会添加一个
completionCopy=[completionCopy]
,将其强制放到堆上。然后使用
completionCopy
。请参阅关于在字典中存储块的说明。使用ARC,您不再需要调用
Block\u copy()
Block\u release()
,但我认为您仍然需要在此处调用
-copy

假设:

  • doSomethingWithCompletion:
    创建
    示例块。
  • 您可以启动一些异步网络操作
  • doSomethingWithCompletion:
    返回,并发布
    ExampleBlock
  • 异步网络操作完成,并调用
    ExampleBlock
  • 在这种情况下,指向块的指针将在解除分配后被解除引用。(这可能是间歇性的,取决于自动释放池是否已耗尽,或者附近的其他内存区域是否已释放。)

    3种可能的解决办法:

    1.将块存储在属性中 将块存储在特性中:

    @property (nonatomic, copy) returnType (^exampleBlock)(parameterTypes);
    
    然后在代码中

    self.exampleBlock = …
    
    这种方法的一个问题是,您只能有一个
    exampleBlock

    2.将块存储在数组中 要解决此问题,可以将块存储在集合中(如
    NSMutableArray
    ):

    然后在代码中:

    self.blockArray = [NSMutableArray array];
    
    // Later on…
    [self.blockArray addObject:exampleBlock];
    
    当可以取消分配块时,可以从阵列中删除该块

    3.通过简单地传递数据块来解决存储问题 与其管理存储和销毁块,不如重构代码,以便在各种方法之间传递
    exampleBlock
    ,直到操作完成


    或者,您可以对异步代码使用
    NSBlockOperation
    ,对响应完成的代码设置其
    completionBlock
    ,并将其添加到NSOperationQueue中。

    我认为完成会在异步调用中释放,这可能会导致崩溃

    我怀疑,问题不在您的代码中,而是在其他地方

    一个可能的问题是:

    如果在块
    completion
    中捕获了UIKit对象,则当在非主线程上执行该块时,可能会出现一个微妙的错误,并且该块保留对这些UIKit对象的最后一个强引用:


    当块
    completion
    完成时,它的块被释放,同时,所有导入的变量都被“销毁”,这意味着在指针可保留的情况下,它们会收到一条
    release
    消息。如果这是最后一个强引用,则捕获的对象将被解除分配,这将发生在非主线程中,这对UIKit对象来说可能是致命的。

    堆栈跟踪的每一帧都应该为您提供一条线索,说明libDispatch是如何导致崩溃的。自下而上:

    11 libsystem_pthread.dylib        0x000000018ffa16bc _pthread_wqthread
    10 libdispatch.dylib              0x000000018fe134fc _dispatch_worker_thread2 + 76
    
    这两个函数启动一个工作线程并运行它。在此过程中,它还为线程设置自动释放池

    9  libdispatch.dylib              0x000000018fe132b8 _dispatch_root_queue_drain + 556
    
    此函数表示队列销毁过程的开始。特定于线程的自动释放池被耗尽,在此过程中,该特定队列引用的所有变量都被释放。因为这是libDispatch,这意味着底层的mach对象和您提交的工作块必须去

    7  libsystem_blocks.dylib         0x000000018fe53908 _Block_release + 256
    6  Example App                    0x00000001000fda18 __destroy_helper_block_253 (TSExampleApp.m)
    5  libsystem_blocks.dylib         0x000000018fe53908 _Block_release + 25
    4  Example App                    0x00000001000fe5a4 __destroy_helper_block_278 (TSExampleApp.m)
    
    这正是这里发生的事情。数字7是外部块,因为它包含一个要销毁的非平凡对象(另一个块),编译器生成了一个析构函数(
    \uuu destroy\u helper\u block\u 253
    )来去除内部块。应用同样的逻辑,我们可以推断内部块还有一点非平凡的破坏要做

    3  libdispatch.dylib              0x000000018fe0c10c -[OS_dispatch_object _xref_dispose] + 60
    2  libdispatch.dylib              0x000000018fe0e928 _dispatch_dispose + 56
    1  libdispatch.dylib              0x000000018fe0e928 _dispatch_dispose + 56
    
    这些线路是你所有麻烦的根源。出于某种原因,您要么捕获了要重新调用的队列,要么捕获了一个对象,该对象保存了对队列的引用,这样当它走上恐龙的道路时,它就会带上它的队列。这会导致libDispatch假定队列已完成,并继续解除锁定,直到到达特定于信号量的dispose

    0  libdispatch.dylib              0x000000018fe0eb2c _dispatch_semaphore_dispose + 60
    
    在没有信号量释放的情况下,mach会抱怨到无法返回信号量销毁的
    KERN_SUCCESS
    ,这是libDispatch中的一个致命错误。事实上,在这种情况下,它将
    中止()
    
    7  libsystem_blocks.dylib         0x000000018fe53908 _Block_release + 256
    6  Example App                    0x00000001000fda18 __destroy_helper_block_253 (TSExampleApp.m)
    5  libsystem_blocks.dylib         0x000000018fe53908 _Block_release + 25
    4  Example App                    0x00000001000fe5a4 __destroy_helper_block_278 (TSExampleApp.m)
    
    3  libdispatch.dylib              0x000000018fe0c10c -[OS_dispatch_object _xref_dispose] + 60
    2  libdispatch.dylib              0x000000018fe0e928 _dispatch_dispose + 56
    1  libdispatch.dylib              0x000000018fe0e928 _dispatch_dispose + 56
    
    0  libdispatch.dylib              0x000000018fe0eb2c _dispatch_semaphore_dispose + 60
    
    - (void)doSomethingWithCompletion:(void (^)())completion {
        void (^ExampleBlock)(NSString *) = [^{
            NSNotification *notification = [NSNotification notificationWithName:kExampleNotificationName object:nil userInfo:nil];
            [[NSNotificationCenter defaultCenter] postNotification:notification];
    
            if (completion) {
                completion();
            }
        } copy];
    
        // Async network call that calls ExampleBlock on either success or failure below...
    }