多线程block中的循环引用

block中的循环引用

一、程序示例

比如有如下代码

@interface DemoObj()

@property (nonatomic, strong) NSOperationQueue *queue;

@end

@implementation DemoObj

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.queue = [[NSOperationQueue alloc] init];
    }

    return self;
}

- (void)dealloc
{
    NSLog(@"demoobj dealloc");
}

- (void)demoOp:(id)obj
{
    NSLog(@"%@ %@", [NSThread currentThread], obj);
}

- (void)demoBlockOp
{  
    [self.queue addOperationWithBlock:^{
        [self demoOp:@"test"];
    }];
}

self 指 DemoObj,queue定义时使用的是strong,即DemoObj对queue有一个强引用;
queue使用了addOperationWithBlock方法,即queue对block有了一个强引用;
block里面有self,即block对DemoObj又有一个强引用。

他们构成了一个循环应用:
多线程block中的循环引用_第1张图片

但程序运行时,打印结果如下:
打印结果

如果真的存在循环引用,DemoObj应该不会被释放才对,但它却成功被释放了,这是为什么呢?

因为block中的代码是在子线程中执行的,子线程运行完毕即被销毁。block指向DemoObj的引用也被销毁,也就构不成循环引用了。

(注意这时候block里的self不能用weak self,不然可能还没打印,demoobj就被释放了,因为weak self只要出了作用域就会被释放,而刚刚开启的线程可能还未执行完毕)

为什么子线程运行完毕就被销毁,而主线程不会呢?这是因为主线程存在RunLoop。

二、RunLoop

Run Loop提供了一种异步执行代码的机制,不能并行执行任务。在主队列中,Main Run Loop直接配合任务的执行,负责处理UI事件、计时器,以及其它内核相关事件。Run Loop的主要目的是保证程序执行的线程不会被系统终止。

它的工作示意图如下:
多线程block中的循环引用_第2张图片

如图所示,当用户点击app的icon,application: didFinishLaunchingWithOptions: 方法执行完毕后,主线程中的runloop就会运行起来,且除非退出程序或程序进入后台,否则RunLoop永远不会停止。

Run Loop的工作特点
1. 当有事件发生时,Run Loop会根据具体的事件类型通知应用程序做出响应
2. 当没有事件发生时,Run Loop会进入休眠状态,从而达到省电的目的
3. 当事件再次发生时,Run Loop会被重新唤醒,处理事件

一般在开发中很少会主动创建RunLoop,而通常会把事件添加到RunLoop中。

iOS程序的主线程默认已经配置好了Run Loop,其他线程默认情况下没有设置Run Loop,所以上面的代码不会形成循环引用。

你可能感兴趣的:(多线程,循环引用,block,self,runloop)