标签:stderr oca capacity 路径 处理器 tab 服务 node const
for (size_t y = 0; y < height; ++y) { for (size_t x = 0; x < width; ++x) { // Do something with x and y here } }
dispatch_apply(height, dispatch_get_global_queue(0, 0), ^(size_t y) { for (size_t x = 0; x < width; x += 2) { // Do something with x and y here } });代码运行良好的程度取决于你在循环内部做的操作。
很多时候,你发现需要将异步的 block 组合起来去完成一个给定的任务。这些任务中甚至有些是并行的。现在,如果你想要在这些任务都执行完成后运行一些代码,"groups" 可以完成这项任务。看这里的例子:
dispatch_group_t group = dispatch_group_create(); dispatch_queue_t queue = dispatch_get_global_queue(0, 0); dispatch_group_async(group, queue, ^(){ // Do something that takes a while [self doSomeFoo]; dispatch_group_async(group, dispatch_get_main_queue(), ^(){ self.foo = 42; }); }); dispatch_group_async(group, queue, ^(){ // Do something else that takes a while [self doSomeBar]; dispatch_group_async(group, dispatch_get_main_queue(), ^(){ self.bar = 1; }); }); // This block will run once everything above is done: dispatch_group_notify(group, dispatch_get_main_queue(), ^(){ NSLog(@"foo: %d", self.foo); NSLog(@"bar: %d", self.bar); });
需要注意的重要事情是,所有的这些都是非阻塞的。我们从未让当前的线程一直等待直到别的任务做完。恰恰相反,我们只是简单的将多个 block 放入队列。由于代码不会阻塞,所以就不会产生死锁。同时需要注意的是,在这个小并且简单的例子中,我们是怎么在不同的队列间进切换的。
一旦你将 groups 作为你的工具箱中的一部分,你可能会怀疑为什么大多数的异步API不把 dispatch_group_t 作为一个可选参数。这没有什么无法接受的理由,仅仅是因为自己添加这个功能太简单了,但是你还是要小心以确保自己使用 groups 的代码是成对出现的。
举例来说,我们可以给 Core Data 的 -performBlock: API 函数添加上 groups,就像这样:
- (void)withGroup:(dispatch_group_t)group performBlock:(dispatch_block_t)block{ if (group == NULL) { [self performBlock:block]; } else { dispatch_group_enter(group); [self performBlock:^(){ block(); dispatch_group_leave(group); }]; } }当 Core Data 上的一系列操作(很可能和其他的代码组合起来)完成以后,我们可以使用 dispatch_group_notify 来运行一个 block 。
很明显,我们可以给 NSURLConnection 做同样的事情:
+ (void)withGroup:(dispatch_group_t)group sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler{ if (group == NULL) { [self sendAsynchronousRequest:request queue:queue completionHandler:handler]; } else { dispatch_group_enter(group); [self sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){ handler(response, data, error); dispatch_group_leave(group); }]; } }
为了能正常工作,你需要确保:
dispatch_group_enter() 必须要在 dispatch_group_leave()之前运行。
dispatch_group_enter() 和 dispatch_group_leave() 一直是成对出现的(就算有错误产生时)。
NSRunningApplication *mail = [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.mail"]; if (mail == nil) { return; } pid_t const pid = mail.processIdentifier; self.source = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, pid, DISPATCH_PROC_EXIT, DISPATCH_TARGET_QUEUE_DEFAULT); dispatch_source_set_event_handler(self.source, ^(){ NSLog(@"Mail quit."); }); dispatch_resume(self.source);当 Mail.app 退出的时候,这个程序会打印出 Mail quit.。
NSURL *directoryURL; // assume this is set to a directory int const fd = open([[directoryURL path] fileSystemRepresentation], O_EVTONLY); if (fd < 0) { char buffer[80]; strerror_r(errno, buffer, sizeof(buffer)); NSLog(@"Unable to open \"%@\": %s (%d)", [directoryURL path], buffer, errno); return; } dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, DISPATCH_VNODE_WRITE | DISPATCH_VNODE_DELETE, DISPATCH_TARGET_QUEUE_DEFAULT); dispatch_source_set_event_handler(source, ^(){ unsigned long const data = dispatch_source_get_data(source); if (data & DISPATCH_VNODE_WRITE) { NSLog(@"The directory changed."); } if (data & DISPATCH_VNODE_DELETE) { NSLog(@"The directory has been deleted."); } }); dispatch_source_set_cancel_handler(source, ^(){ close(fd); }); self.source = source; dispatch_resume(self.source);
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, DISPATCH_TARGET_QUEUE_DEFAULT); dispatch_source_set_event_handler(source, ^(){ NSLog(@"Time flies."); }); dispatch_time_t start dispatch_source_set_timer(source, DISPATCH_TIME_NOW, 5ull * NSEC_PER_SEC, 100ull * NSEC_PER_MSEC); self.source = source; dispatch_resume(self.source);
0: HTTP/1.1 200 OK\r\nDate: Mon, 23 May 2005 22:38 1: :34 GMT\r\nServer: Apache/1.3.3.7 (Unix) (Red-H 2: at/Linux)\r\nLast-Modified: Wed, 08 Jan 2003 23 3: :11:55 GMT\r\nEtag: "3f80f-1b6-3e1cb03b"\r\nCon 4: tent-Type: text/html; charset=UTF-8\r\nContent- 5: Length: 131\r\nConnection: close\r\n\r\n<html>\r 6: \n<head>\r\n <title>An Example Page</title>\r\n 7: </head>\r\n<body>\r\n Hello World, this is a ve如果你是在寻找 HTTP 的头部,将所有数据块组合成一个大的缓冲区并且从中查找 \r\n\r\n 是非常简单的。但是这样做,你会大量地复制这些数据。大量 旧的 C 语言 API 存在的另一个问题就是,缓冲区没有所有权的概念,所以函数不得不将数据再次拷贝到自己的缓冲区中——又一次的拷贝。拷贝数据操作看起来是无关紧要的,但是当你正在做大量的 I/O 操作的时候,你会在 profiling tool(Instruments) 中看到这些拷贝操作大量出现。即使你仅仅每个内存区域拷贝一次,你还是使用了两倍的存储带宽并且占用了两倍的内存缓存。
dispatch_data_t a; // Assume this hold some valid data dispatch_data_t b; // Assume this hold some valid data dispatch_data_t c = dispatch_data_create_concat(a, b);数据对象 c 并不会将 a 和 b 拷贝到一个单独的,更大的内存区域里去。相反,它只是简单地 retain 了 a 和 b。你可以使用 dispatch_data_apply 来遍历对象 c 持有的内存区域:
dispatch_data_apply(c, ^bool(dispatch_data_t region, size_t offset, const void *buffer, size_t size) { fprintf(stderr, "region with offset %zu, size %zu\n", offset, size); return true; });类似的,你可以使用 dispatch_data_create_subrange 来创建一个不做任何拷贝操作的子区域。
dispatch_io_t dispatch_io_create(dispatch_io_type_t type, dispatch_fd_t fd, dispatch_queue_t queue, void (^cleanup_handler)(int error));这将返回一个持有文件描述符的创建好的通道。在你通过它创建了通道之后,你不准以任何方式修改这个文件描述符。
_isolation = dispatch_queue_create([[self description] UTF8String], 0); _nativeSocket = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP); struct sockaddr_in sin = {}; sin.sin_len = sizeof(sin); sin.sin_family = AF_INET6; sin.sin_port = htons(port); sin.sin_addr.s_addr= INADDR_ANY; int err = bind(result.nativeSocket, (struct sockaddr *) &sin, sizeof(sin)); NSCAssert(0 <= err, @""); _eventSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, _nativeSocket, 0, _isolation); dispatch_source_set_event_handler(result.eventSource, ^{ acceptConnection(_nativeSocket); });当接受了连接,我们创建一个I/O通道:
typedef union socketAddress { struct sockaddr sa; struct sockaddr_in sin; struct sockaddr_in6 sin6; } socketAddressUnion; socketAddressUnion rsa; // remote socket address socklen_t len = sizeof(rsa); int native = accept(nativeSocket, &rsa.sa, &len); if (native == -1) { // Error. Ignore. return nil; } _remoteAddress = rsa; _isolation = dispatch_queue_create([[self description] UTF8String], 0); _channel = dispatch_io_create(DISPATCH_IO_STREAM, native, _isolation, ^(int error) { NSLog(@"An error occured while listening on socket: %d", error); }); //dispatch_io_set_high_water(_channel, 8 * 1024); dispatch_io_set_low_water(_channel, 1); dispatch_io_set_interval(_channel, NSEC_PER_MSEC * 10, DISPATCH_IO_STRICT_INTERVAL); socketAddressUnion lsa; // remote socket address socklen_t len = sizeof(rsa); getsockname(native, &lsa.sa, &len); _localAddress = lsa;
dispatch_io_read(_channel, 0, SIZE_MAX, _isolation, ^(bool done, dispatch_data_t data, int error){ if (data != NULL) { if (_data == NULL) { _data = data; } else { _data = dispatch_data_create_concat(_data, data); } [self processData]; } });如果所有你想做的只是读取或者写入一个文件,GCD 提供了两个方便的封装: dispatch_read 和 dispatch_write 。你需要传递给 dispatch_read 一个文件路径和一个在所有数据块读取后调用的 block。类似的,dispatch_write 需要一个文件路径和一个被写入的 dispatch_data_t 对象。
在 GCD 的一个不起眼的角落,你会发现一个适合优化代码的灵巧小工具:
uint64_t dispatch_benchmark(size_t count, void (^block)(void));
dispatch_benchmark函数是(Grand Central Dispatch) 的一部分,但是这个方法并没有被公开声明,所以必须要自己声明。把这个声明放到你的代码中,你就能够测量给定的代码执行的平均的纳秒数。例子如下:
// 声明GCD中dispatch_benchmark函数原型 extern uint64_t dispatch_benchmark(size_t count, void (^block)(void)); void test1(void); void test2(void); int main(int argc, const char * argv[]) { @autoreleasepool { test1(); test2(); } return 0; } void test1(void) { // 执行次数 size_t count = 1000; // 调用dispatch_benchmark函数,传入执行次数和待测试的代码块 NSUInteger length = 1000000; uint64_t time = dispatch_benchmark(count, ^{ @autoreleasepool { NSMutableArray *mutableArray = [NSMutableArray array]; for (NSUInteger i = 0; i < length; i++) { [mutableArray addObject:@(i)]; } } }); NSLog(@"[[NSMutableArray array] addObject:] Avg. Runtime: %llu ns", time); } void test2(void) { // 执行次数 size_t count = 1000; NSUInteger length = 1000000; // 调用dispatch_benchmark函数,传入执行次数和待测试的代码块 uint64_t time = dispatch_benchmark(count, ^{ @autoreleasepool { NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:length]; for (NSUInteger i = 0; i < length; i++) { [mutableArray addObject:@(i)]; } } }); NSLog(@"[[NSMutableArray arrayWithCapacity:] addObject:] Avg. Runtime: %llu ns", time); }
[[NSMutableArray array] addObject:] Avg. Runtime: 31488580 ns
[[NSMutableArray arrayWithCapacity:] addObject:] Avg. Runtime: 31989987 ns
也就是说两种方法添加1000个对象到 NSMutableArray 分别消耗了31488580纳秒和31989987纳秒。不要把它放到发布代码中,事实上,这是无意义的,它是私有API。它只是在调试和性能分析上起作用。
void * sharedBuffer(void){ static void * buffer; if (buffer == NULL) { void * newBuffer = calloc(1, 1024); if (!OSAtomicCompareAndSwapPtrBarrier(NULL, newBuffer, &buffer)) { free(newBuffer); } } return buffer; }
@interface MyTableViewCell : UITableViewCell @property (readonly, nonatomic, copy) NSDictionary *amountAttributes; @end @implementation MyTableViewCell{ NSDictionary *_amountAttributes; } - (NSDictionary *)amountAttributes{ if (_amountAttributes == nil) { static __weak NSDictionary *cachedAttributes = nil; static OSSpinLock lock = OS_SPINLOCK_INIT; OSSpinLockLock(&lock); _amountAttributes = cachedAttributes; if (_amountAttributes == nil) { NSMutableDictionary *attributes = [[self subtitleAttributes] mutableCopy]; attributes[NSFontAttributeName] = [UIFont fontWithName:@"ComicSans" size:36]; attributes[NSParagraphStyleAttributeName] = [NSParagraphStyle defaultParagraphStyle]; _amountAttributes = [attributes copy]; cachedAttributes = _amountAttributes; } OSSpinLockUnlock(&lock); } return _amountAttributes; }就上面的例子而言,或许用不着这么麻烦,但它演示了一种理念。我们使用了ARC的 __weak 来确保一旦 MyTableViewCell 所有的实例都不存在, amountAttributes 会调用 dealloc 。因此在所有的实例中,我们可以持有字典的一个单独实例。
标签:stderr oca capacity 路径 处理器 tab 服务 node const
原文地址:http://blog.csdn.net/youshaoduo/article/details/70598006