zoukankan      html  css  js  c++  java
  • Objective-C异步编程

    1. 不要阻塞主线程

    不管在进行iOS还是OS X开发中,主线程都只应该处理用户交互和界面布局,好的程序通常能够随时快速响应用户的操作,所以CPU密集型或者会阻塞线程的代码应该在其他位置去执行,我指的是其他线程。

    2. 在后台线程中执行

    为了不阻塞主线程,我们应该把更多的操作放到后台中去执行,只有在不得不在主线程中执行时(更新UI等)才回到主线程,GCD是最适合这种线程之间切换的:

    //Main Thread
    dispatch_queue_t queue;
    queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{
        [self renderThumbnails];
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.thumbnailView setNeedsDisplay:YES];
        });
    });
    

    3. 不要阻塞太多后台线程

    如果我们要在后台线程中请求一系列的数据,然后将它们显示到界面上,你可能写出下面的代码:

    //Main Thread
    dispatch_queue_t queue;
    queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    for (NSURL *url in [self.imageStore URLs]) {
        dispatch_async(queue, ^{
            NSData *data = [NSData dataWithContentsOfURL:url];
            
            dispatch_async(dispatch_get_main_queue(), ^{
                [self.imageStore setImageData:data forURL:url];
            });
        });
    }
    

    这段代码肯定是有问题的,因为获取数据NSData *data = [NSData dataWithContentsOfURL:url];是同步的,台线程被这段代码阻塞调,系统会自动创建新的线程去执行下一个循环,最终结果会是获取多少次数据将创建了多少个后台线程。而创建线程本身是有成本的,所以如果创建太多的后台线程会占用大量的系统资源,这时应该用dispatch I/O来解决:

    //Main Thread
    for (NSURL *url in [self.imageStore URLs]) {
        dispatch_io_t io = dispatch_io_create_with_path(DISPATCH_IO_RANDOM, [[url path] fileSystemRepresentation], 0_RDONLY, 0, NULL, NULL);
        dispatch_io_set_low_water(io, SIZE_MAX);
        
        dispatch_io_read(io, 0, SIZE_MAX, dispatch_get_main_queue(), ^(bool done, dispatch_data_t data, int error) {
            [self.imageStore setImageData:data forURL:url];
        });
    }
    

    4. 与主循环(Main Runloop)结合

    通常我们一系列后台执行代码结束后,需要将结果反馈到主线程中,我们可以直接调用 dispatch_get_main_queue() 获取主线程,并在其中执行代码。

    还有一些API是带有基于runloop的回调的,如NSTimer、一些performSeletor:方法和代理方法回调,所有这些API都会默认回调函数所在的runloop,所以在使用这些API时应该知道回调方法的runloop是属于哪个线程。还应该注意两点:

    • 不要在自动分配的工作线程中调用这些API
    • 不要阻塞在main runloop中的回调函数
    - (void)downloadFromRemotePictureViewer:(NSString *)name {
        //Main Thread
        NSNetService *service = [[NSNetService alloc] initWithDomain:@"" type:@"_pictureviewer._tcp" name:name];
        [service setDelegate:self];
        [service resolveWithTimeout:5.0];
    }
    
    - (void)netServiceDidResolveAddress:(NSNetService *)service {
        [self downloadFromRemoteService:service];
    }
    

    在上面初始化和发起NetService请求都应该在主线程执行,如果你通过GCD让它在后台运行,那么它的代码回调函数是永远也不会被调用,与此类似的还有NSURLConnection。代理方法也默认是在主线程中调用的,所以为了不阻主线程,我们应该将回调里面的处理放在后台:

    - (void)netServiceDidResolveAddress:(NSNetService *)service {
        dispatch_async(self.downloadQueue, ^{
            [self downloadFromRemoteService:service];
        });
    }
    

    5. 为每个子系统对应一个队列

    通常我们应该将程序分割成多个独立的子系统,通过对应的调度队列来控制每个部分,界面部分由主队列(Main Queue)控制。

    如我们一项任务需要涉及数据下载,数据存储,视图渲染和界面展现几个流程,我们可以分别创建downloadQueuestoreQueue, renderQueue,界面展现则只需要使用“main queue”。

    - (void)netServiceDidResolveAddress:(NSNetService *)service {
    	dispatch_async(self.downloadQueue, ^{
    	    NSData *data = [self downloadFromRemoteService:service];
    	    
    	    dispatch_async(self.storeQueue, ^{
    	        int img = [self.imageStore addImage:data];
    	        
    	        dispatch_saync(self.renderQueue, ^{
    	            [self renderThumbnail:img];
    	            
    	            dispatch_async(dispatch_get_main_queue(), ^{
    	                [[self thumbnailViewForId:img] setNeedsDisplay:YES];
    	            });
    	        });
    	    });
    	});
    }
    

    6. 通过读写访问提升效率

    我们在设计读写时通常允许并发同步的的读(read),串行异步的写(write),并且读写不能同时进行。

    self.concurrentQuene = dispatch_queue_create("com.example.current", DISPATCH_QUEUE_CONCURRENT);
    
    - (id)objectAtIndex:(NSUInteger)index {
        __block id obj;
        dispatch_sync(self.concurrentQueue, ^{
           obj = [self.array objectAtIndex:index];
        });
        return obj;
    }
    
    - (void)insertObject:(id)obj atIndex:(NSUInteger)index {
        dispatch_barrier_async(self.concurrentQueue, ^{
            [self.array insertObject:obj atIndex:index];
        });
    }
    

    7. 区分控制和数据流

    调度队列(dispatch queue)并不是为一般的数据存储而设计的,它没有取消操作和随机存储,所以需要合理使用数据结构。

    假设我们有一组图片需要渲染,如果我们每渲染一张图片时都去存储队列中读取对应的数据,那个渲染队列和存储队列就会因为依赖的大大降低执行效率。我们可以合理的利用数据结构,如我们可以每次从存储队列中取多个图片然后渲染,完后再去存储队列中取,这样就大大减少了依赖,而且也避免了频繁的队列切换。

    8. 异步的更新状态

    有时候我们先知道队列中操作执行的进度,并通过状态显示出来,如通过progress view显示当前图片渲染的进度,我们可以使用GCD的dispatch source。

    //先设置接受到数据的处理(类似监听)
    self.source = dispatch_source_create(DISPATCH_SOURCE_TYPE_ADD, 0, 0, dispatch_get_main_queue());
    
    dispatch_source_set_event_handler(self.source, ^{
        self.progress += dispatch_source_get_data(self.source);
        [self.progressView setProgress:(self.progress/self.total) animated:YES];
    });
    dispatch_resume(self.source);
    
    //在渲染的时候将数据传递给dispatch source
    dispatch_async(self.renderQueue, ^{
        //...
        dispatch_source_merge_data(self.source, 1);
    });
    
    //可以取消掉dispatch source的处理
    dispatch_source_cancel(self.source);
    

    Posted by TracyYih - Aug 28 2013
    如需转载,请注明: 本文来自 Esoft Mobile

  • 相关阅读:
    C语言-if语句
    C语言-表达式
    C语言-基础
    Java for LeetCode 187 Repeated DNA Sequences
    Java for LeetCode 179 Largest Number
    Java for LeetCode 174 Dungeon Game
    Java for LeetCode 173 Binary Search Tree Iterator
    Java for LeetCode 172 Factorial Trailing Zeroes
    Java for LeetCode 171 Excel Sheet Column Number
    Java for LeetCode 169 Majority Element
  • 原文地址:https://www.cnblogs.com/tracy-e/p/3411478.html
Copyright © 2011-2022 走看看