简体   繁体   English

在dispatch_async中使用“freed”self时的EXC_BAD_ACCESS

[英]EXC_BAD_ACCESS when using “freed” self in dispatch_async

I have a game written using the new SpriteKit in iOS7. 我在iOS7中使用新的SpriteKit编写了一个游戏。 I have a customised SKSpriteNode which would fetch and display a Facebook profile picture. 我有一个定制的SKSpriteNode ,可以获取并显示Facebook个人资料图片。 However, since it may take some time to load the picture. 但是,因为加载图片可能需要一些时间。 I tried to load the picture in background when I initialised the node and display it only when the picture is loaded. 我初始化节点时尝试在后台加载图片,并仅在加载图片时显示。 Here is code snippet I wrote: 这是我写的代码片段:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{


    // Code to load Facebook Profile picture
    // ...
    SKSpriteNode *fbFrame = [SKSpriteNode spriteNodeWithTexture:facebookPicTexture];
    dispatch_async(dispatch_get_main_queue(), ^{


        // self is my customised SKSpriteNode - so here I just add a sprite node of
        // a facebook picture to myself as a child     
        [self addChild:fbFrame];
    });

});

It works fine normally. 它正常工作正常。 However, if the loading of Facebook profile pic is slow, the user may have already switch to another screen when the picture is loaded. 但是,如果Facebook个人资料图片的加载速度很慢,则用户可能已经在加载图片时切换到另一个屏幕。 In such case, self will actually be removed from the scene hierarchy and no reference will be made to it. 在这种情况下,实际上将从场景层次结构中删除self并且不会对其进行引用。

When I read the block doc, I think the async block will retain self and so I presume it will still be valid when the main thread block is called. 当我读取块文档时,我认为异步块将保留self ,因此我认为在调用主线程块时它仍然有效。 It turns out from time to time if the pic loading is really slow and the second dispatch_async is called when self is removed from the hierarchy, a bad access error will occur at the line [self addChild:fbFrame] . 事实证明,如果pic加载非常慢,并且当从层次结构中删除self时调用第二个dispatch_async,则会在行[self addChild:fbFrame]发生错误的访问错误。

Am I understand the block memory management incorrectly? 我是否理解块内存管理不正确? And is there a way to solve that kind of problem? 有没有办法解决这类问题?

Your understanding of the memory management is correct, that the presence of self in this block will retain self until the dispatched block is completed. 您对内存管理的理解是正确的,此块中self的存在将保留self直到调度块完成。 The solution is to not retain self while the block runs, by using a weak reference to self inside the block: 解决方案是在块运行时不保留self ,方法是在块内使用对selfweak引用:

__weak typeof(self) weakSelf = self;

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    // do some stuff in background here; when done, then:

    dispatch_async(dispatch_get_main_queue(), ^{
        [weakSelf addChild:fbFrame];
    });
});

You should review that block for any references to self (either explicit or implicitly by referencing an ivar directly), and replace them with weakSelf . 您应该查看该块以获取对self任何引用(通过直接引用ivar显式或隐式),并将其替换为weakSelf

Or, (unlikely in this case) sometimes you'll see the weakSelf / strongSelf pattern where you must ensure that self isn't released during the execution of that nested block: 或者,(在这种情况下不太可能)有时您会看到weakSelf / strongSelf模式,您必须确保在执行该嵌套块期间不释放self

__weak typeof(self) weakSelf = self;

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    // do some stuff in background here; when done, then:

    dispatch_async(dispatch_get_main_queue(), ^{
        typeof(self) strongSelf = weakSelf;
        if (strongSelf) {
            // stuff that requires that if `self` existed at the start of this block, 
            // that it won't be released during this block
        }
    });
});

By the way, the other approach is to cancel the network request when the view controller is dismissed. 顺便说一下,另一种方法是在取消视图控制器时取消网络请求。 This requires a more significant change (using a NSOperation -based approach rather than GCD; or use NSURLSessionTask -based approach that allows you to cancel the request), but it is the other way to tackle this. 这需要更大的改变(使用基于NSOperation的方法而不是GCD;或者使用基于NSURLSessionTask的方法来允许您取消请求),但这是解决此问题的另一种方法。

I think you need to write like this: 我想你需要这样写:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);

objc_setAssociatedObject(self, @"yourTag", @"Alive", OBJC_ASSOCIATION_RETAIN);

dispatch_async(queue, ^{    
    // Code to load Facebook Profile picture
    // ...
    SKSpriteNode *fbFrame = [SKSpriteNode spriteNodeWithTexture:facebookPicTexture];
    dispatch_async(dispatch_get_main_queue(), ^{

        NSString *strAlive = (NSString *)objc_getAssociatedObject(self, @"yourTag");
        if (strAlive)
        {
            // self is my customised SKSpriteNode - so here I just add a sprite node of
            // a facebook picture to myself as a child     
            [self addChild:fbFrame];
        }
    });

});
dispatch_release(queue);

When you dont want the dispatch to continue procedure when the self (ViewController) is not visible anymore so write this: 当你不希望调度继续执行过程时,自我(ViewController)不再可见,所以写下:

objc_setAssociatedObject(self, @"yourTag", nil, OBJC_ASSOCIATION_RETAIN);

In: - viewWillDisapear Or - viewDidDisapear And renew calling the dispatch when you come back to than screen. 在: - viewWillDisapear- viewDidDisapear当你回到屏幕时更新调用调度。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用dispatch_async时为EXC_BAD_ACCESS - EXC_BAD_ACCESS when using dispatch_async 在Objective-C中的dispatch_async中传递参数时为EXC_BAD_ACCESS - EXC_BAD_ACCESS when passing a parameter in dispatch_async in objective-C GCD dispatch_async DISPATCH_QUEUE_PRIORITY_DEFAULT EXC_BAD_ACCESS崩溃 - GCD dispatch_async DISPATCH_QUEUE_PRIORITY_DEFAULT EXC_BAD_ACCESS crash 使用FMDB将数据插入SQLite时,在dispatch_async上为EXC_BAD_ACCESS - EXC_BAD_ACCESS on dispatch_async while inserting data into SQLite with FMDB captureOutput 中的 Dispatch_async:(AVCaptureOutput*)captureOutput didOutputSampleBuffer,样本缓冲区上的 EXC_BAD_ACCESS 错误 - Dispatch_async in captureOutput:(AVCaptureOutput*)captureOutput didOutputSampleBuffer, EXC_BAD_ACCESS error on sample buffer 使用dispatch_block_t时为EXC_BAD_ACCESS - EXC_BAD_ACCESS when using dispatch_block_t 使用异步请求时,exc_bad_access - exc_bad_access when using a async request 使用dispatch_async奇怪的bad_access - Weird bad_access with dispatch_async 使用ABPersonViewControllerDelegate时为EXC_BAD_ACCESS - EXC_BAD_ACCESS when using an ABPersonViewControllerDelegate 使用UITapGestureRecognizer时为EXC_BAD_ACCESS - EXC_BAD_ACCESS when using UITapGestureRecognizer
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM