美文网首页
[Note] Effective OC - Item 40~42

[Note] Effective OC - Item 40~42

作者: _lemon | 来源:发表于2015-12-21 09:14 被阅读44次

    Chapter 6. Blocks and Grand Central Dispatch

    <br />


    Item 40: Avoid Retain Cycles Introduced by Blocks Referencing the Object Owning Them

    <br />
    这一条讲避免因为使用block而出现保留环的问题。
    保留环的出现是因为当block里引用了某个对象的实例变量的时候,这个对象也会被引用。这是一种不太容易被发现的情况。如果是直接引用控制器对象,控制器对象又引用block的话,就很容易发现。环中涉及三个以上对象的时候就容易混乱,我一般得通过画图分析。
    以前只知道用weak关键字来打破保留环,这篇提供了一个别的思路,就是在所执行的任务结束时,适时地把不再用的对象释放掉,这样环里的一个箭头就不存在了,整个环也就被打破了。例子给的比较长,这里只放一下打破环的部分:

    [_networkFetcher startWithCompletionHandler:^(NSData *data) {
         NSLog(@“Request for URL %@ finished”, _networkFetcher.url);
         _fetchedData = data;
         _networkFetcher = nil;
    }];
    

    这里是数据取得以后,fetcher就没有用了,于是及时释放掉。
    还有一个例子:

    - (void)p_requestCompleted {
        if (_completionHandler) {
            _completionHandler(_downloadedData);
        }
        self.completionHandler = nil;
    }
    

    这里直接释放了block,也就是说如果block用完就没有用了,所以不再引用它。
    总之是有很多灵活的方法来打破保留环,可以具体问题具体分析。
    <br />


    Item 41: Prefer Dispatch Queues to Locks for Synchronization

    <br />
    这一节其实讲的是dispatch queue的用法。
    基本的同步与异步就不说了。这里有一个小细节:

    - (void)setSomeString:(NSString *)someString {
        dispatch_async(_syncQueue, ^{
            _someString = someString;
        });
    }
    

    这是一个setter,里面是异步实现的。文中提到这里可能会有性能问题,因为使用异步派发的话,block会被copy,这里有一个copy时间。所以需要权衡copy花的时间和同步派发所花的时间,如果前者更多,就没必要使用异步派发。
    文中还介绍了设计getter和setter的思想,获取是可以并发的,而设置需要同步执行,也就是说设置一个变量的时候,不应该再有其他的读写操作。
    具体实现是采用barrier,常见的有两个函数:

    • dispatch_barrier_async
    • dispatch_barrier_sync

    共同点:都是当队列里正在执行的block执行完毕后,再开始执行这个操作,执行时不会有别的block同时执行,当这个操作执行完毕后别的操作才会再开始执行。
    关于不同点,文档里是这么说的:

    dispatch_barrier_async: Calls to this function always return immediately after the block has been submitted and never wait for the block to be invoked.
    dispatch_barrier_sync: Submits a barrier block to a dispatch queue for synchronous execution. Unlike dispatch_barrier_async, this function does not return until the barrier block has finished. Calling this function and targeting the current queue results in deadlock.

    所以区别是在于是不是立即返回。但是dispatch_barrier_sync里提到的这个死锁情况我不是特别明白。(好了我现在明白了!sync都是会有这种死锁出现,阻塞了当前线程同时又在等当前线程的任务执行。)
    文中的setter和getter的写法:

    _syncQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    - (NSString *) someString {
        __block NSString *localSomeString;
        dispatch_sync(_syncQueue, ^{
            localSomeString = _someString;
        });
        return localSomeString;
    }
    
    - (void)setSomeString:(NSString *)someString {
        dispatch_barrier_async(_syncQueue, ^{
            _someString = someString;
        });
    }
    

    <br />


    Item 42: Prefer GCD to performSelector and Friends

    <br />
    这一节讲performSelector的局限性。
    performSelector确实挺少见的,除了在runtime讲动态绑定的部分见过,平时比较少见,不知道它有多线程相关的功能。它的局限性在于返回值和参数,返回值是id,参数最多只有两个,不够灵活。并且在runtime动态绑定时,由于ARC不再根据方法名采用自动释放,还有内存泄露的可能性存在。
    所以采用GCD办法来代替performSelector相关的方法。如果需要延后执行,应该选择dispatch_after,而不是performSelector:withObject:afterDelay:。如果是主线程执行,应该选择dispatch_get_main_queue()作为dispatch queue,而不是调用performSelectorOnMainThread:withObject:waitUntilDone:

    相关文章

      网友评论

          本文标题:[Note] Effective OC - Item 40~42

          本文链接:https://www.haomeiwen.com/subject/tbsmhttx.html