http://esoftmobile.com/2013/08/17/effective-objective-c-2/

Chapter 6: Blocks and Grand Central Dispatch

Item 37: Understand Blocks

《Ry’s Objective-C Tutorial》# Blocks

Item 38: Create typedefs for Common Block Types

当我们程序中要使用一些具有共性的Block时(返回值类型、参数个数和类型相同),我们可以给这种Block定义一个类型:

typedef NSComparisonResult (^NSComparator)(id obj1, id obj2);
//...
- (NSArray *)sortedArrayUsingComparator:(NSComparator)cmptr;
- (void)sortUsingComparator:(NSComparator)cmptr;
//...
// Simplified with typedef
typedef void(^EOCCompletionHandler)(NSData *data, NSError *error);
- (void)startWithCompletionHandler:(EOCCompletionHandler)completion;

国内比较有名的开源框架BeeFramework中就大量应用到Block,并通过类型定义的Block作为属性,实现类似于很多脚本语言方法调用:self.HTTP_GET(URL).PARAM(postDict);, 笔者之前在TouchXML基础上封装了一层W3C标准DOM API时也尝试过这种实现,最后在Objective-C中可以直接这样调用:document.getElementById(@"xxx").setAttribute(@"class", @"xxx"); 是不是有点写JS的赶脚。

Item 39: Use Handler Blocks to Reduce Code Separation

当我们要执行一个异步操作,比如异步请求时,通常需要在操作(或请求)完成后将结果返回,在Objective-C中一般有两种实现方式:代理和Block回调。

代理使用起来比较麻烦,有定义协议,申明代理方法,代理回调、设置代理、实现代理方法等一些列流程,而使用Block回调要简洁得多,我们通常可以申明一个Block类型的属性,在异步操作执行完后调用一下该Block。

//CXMLHttpRequest.h
typedef void (^CXMLHttpRequestCallbackBlock) (CXMLHttpRequest *request);
@interface CXMLHttpRequest : NSObject
//...
@property (nonatomic, copy) CXMLHttpRequestCallbackBlock onreadystatechange;
//...
@end //CXMLHttpRequest.m
//call when request state changed.
_onreadystatechange(self); //User CXMLHttpRequest
CXMLHttpRequest *request = [CXMLHttpRequest new];
request.onreadystatechange = ^(CXMLHttpRequest *req) {
if (req.state == 4 && req.statusCode == 200) {
//get req.responseText.
}
};
//...

推荐项目:BlocksKit

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

由于Block会强引用里面出现的对象,如果Block中使用成员变量,则self本身会被Block强引用,所以稍不注意就会出现Retain Cycle。所以通常避免的方法是在Block中引用对象的值而非对象本身,在非ARC下,可以使用__block关键字来申明需要在Block中引用的对象,这样该对象就不会被Block retain,然后在Block结束时将引用对象设为nil:

MyViewController * __block myController = [[MyViewController alloc] init…];
// ...
myController.completionHandler = ^(NSInteger result) {
[myController dismissViewControllerAnimated:YES completion:nil];
myController = nil;
};

在ARC模式下,则也可以用__weak(iOS5.0一下版本用__unsafe_unretained)关键字申明一个弱引用对象:

MyViewController *__weak weakSelf = self;
self.completionHandler = ^(NSData *data) {
//...
[weakSelf clearUp];
};

Item 41: Prefer Dispatch Queues to Locks for Synchronization

在多线程环境下,为了保证某些资源操作的可控性,需要给一些方法加锁,保证同时只响应一个对象的调用,通常可以用@synchronized()NSLock

// @synchronized block
- (void)synchronisedMethod {
@synchronized(self) {
// Safe
}
}
// NSLock
_lock = [[NSLock alloc] init]; - (void)synchronisedMethod {
[_lock lock];
// Safe
[_lock unlock];
}

我们还可以使用dispatch queue来保证同步操作,首先创建一个dispatch queue,然后将同步操作在该queue中执行:

// Using GCD queue for synchronisation
_syncQueue = dispatch_queue_create("com.effectiveobjectivec.syncQueue", NULL); // … - (NSString*)someString {
__block NSString *localSomeString;
dispatch_sync(_syncQueue, ^{
localSomeString = _someString;
});
return localSomeString;
} - (void)setSomeString:(NSString*)someString {
dispatch_sync(_syncQueue, ^{
_someString = someString;
});
}

Item 42: Prefer GCD to performSelector and Friends

不在使用GCD时,如果一项任务需要分别在主线程和非主线程中执行,我们需要通过performSelector方法来改变执行的线程,我们还不得不把任务分解成不同的方法,某些方法内的代码在主线程执行,某些在非主线执行:

- (void)pulldown {
_indicator.hidden = NO;
[_indicator startAnimating];
[self performSelectorInBackground:@selector(download) withObject:nil];
} - (void)download {
NSURL *URL = [NSURL URLWithString:@"http://xxx."];
NSString *data = [NSString stringWithContentsOfURL:URL encoding:NSUTF8StringEncoding error:nil];
if (data) {
[self performSelectorOnMainThread:@selector(reloadData:) withObject:data waitUntilDone:NO];
}
} - (void)reloadData {
[_indicator stopAnimating];
_indicator.hidden = YES;
//refresh view with data.
}

而如果使用GCD,所有的操作就要简洁很多:

- (void)pulldown {
_indicator.hidden = NO;
[_indicator startAnimating];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL *URL = [NSURL URLWithString:@"http://xxx"];
NSString *data = [NSString stringWithContentsOfURL:URL encoding:NSUTF8StringEncoding error:nil];
if (data) {
dispatch_async(dispatch_get_main_queue(), ^{
[_indicator stopAnimating];
_indicator.hidden = YES;
//refresh view with data.
});
}
};
}

Item 43: Know When to Use GCD and When to Use Operation Queues

Item 44: Use Dispatch Groups to Take Advantage of Platform Scaling

很多情况下我们使用GCD来执行一些异步操作,但是异步操作就存在一个返回顺序问题,如我们需要异步下载3个数据,只有当3个数据都下载完成后才刷新视图,而3个异步下载返回顺序是未知的,这是我们可以使用dispatch group来管理这三个任务:

dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{
//下载数据1
});
dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{
//下载数据2
});
dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{
//下载数据3
}); dispatch_group_notify(group, dispatch_get_main_queue(), ^{
//刷新视图
});

其实熟悉JS或者说熟悉Node.js的人都了解,异步编程下的协同问题一直是比较受关注的话题,其中 Node大牛 @朴灵EventProxy,个人感觉和dispatch group有异曲同工之妙:

var ep = EventProxy.create("template", "data", "l10n", function (template, data, l10n) {
_.template(template, data, l10n);
}); $.get("template", function (template) {
// something
ep.emit("template", template);
});
$.get("data", function (data) {
// something
ep.emit("data", data);
});
$.get("l10n", function (l10n) {
// something
ep.emit("l10n", l10n);
});

Item 45: Use dispatch_once for Thread-Safe Single-Time Code Execution

// `dispatch_once' singleton initialisation
+ (id)sharedInstance {
static EOCClass *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}

Item 46: Avoid dispatch_get_current_queue

Chapter 7: The System Frameworks

Item 47: Familiarize Yourself with the System Frameworks

《iOS Technology Overview》# Cocoa Touch Frameworks

Item 48: Prefer Block Enumeration to for Loops

// Block enumeration
NSArray *anArray = /* … */;
[anArray enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop){
// Do something with `object’
if (shouldStop) {
*stop = YES;
}
}]; NSDictionary *aDictionary = /* … */;
[aDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id object, NSUInteger idx, BOOL *stop){
// Do something with `key’ and `object’
if (shouldStop) {
*stop = YES;
}
}]; NSSet *aSet = /* … */;
[aSet enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop){
// Do something with `object’
if (shouldStop) {
*stop = YES;
}
}];

Item 49: Use Toll-Free Bridging for Collections with Custom Memory-Management Semantics

// No-ops for non-retaining objects.
static const void* EOCRetainNoOp(CFAllocatorRef allocator, const void *value) { return value; }
static void EOCReleaseNoOp(CFAllocatorRef allocator, const void *value) { } NSMutableArray* EOCNonRetainArray(){
CFArrayCallBacks callbacks = kCFTypeArrayCallBacks;
callbacks.retain = EOCRetainNoOp;
callbacks.release = EOCReleaseNoOp;
return (NSMutableArray *)CFArrayCreateMutable(nil, 0, &callbacks);
} NSMutableDictionary* EOCNonRetainDictionary(){
CFDictionaryKeyCallBacks keyCallbacks = kCFTypeDictionaryKeyCallBacks;
CFDictionaryValueCallBacks callbacks = kCFTypeDictionaryValueCallBacks;
callbacks.retain = EOCRetainNoOp;
callbacks.release = EOCReleaseNoOp;
return (NSMutableDictionary *)CFDictionaryCreateMutable(nil, 0, &keyCallbacks, &callbacks);
}

Item 50: Use NSCache Instead of NSDictionary for Caches

Item 51: Keep initialize and load Implementations Lean

+ (void)load;

Invoked whenever a class or category is added to the Objective-C runtime; implement this method to perform class-specific behavior upon loading.

+ (void)initialize;

Initializes the receiver before it’s used (before it receives its first message).

Item 52: Remember that NSTimer Retains Its Target

NSTimer会对retain它的Target,所以不要在Target的dealloc中销毁(invalidate)NSTimer对象,因 为Timer和Target之间已经形成了Retain cycle,需要在dealloc前就破坏这个Retain cycle。

我们可以对NSTimer拓展,让它支持调用Block方法:

// Block support for NSTimer
#import <Foundation/Foundation.h> @interface NSTimer (EOCBlocksSupport) + (void)eoc_scheduledTimerWithTimeInterval:(NSTimeInterval)interval
block:(void(^)())block
repeats:(BOOL)repeats; @end @implementation NSTimer (EOCBlocksSupport) + (void)eoc_scheduledTimerWithTimeInterval:(NSTimeInterval)interval
block:(void(^)())block
repeats:(BOOL)repeats
{
return [self scheduledTimerWithTimeInterval:interval
target:self
selector:@selector(eoc_blockInvoke:)
userInfo:[block copy]
repeats:repeats];
} + (void)eoc_blockInvoke:(NSTimer*)timer {
void (^block)() = timer.userInfo;
if (block) {
block();
}
}
@end

总结

到这里,全部的代码都过了一遍了,网友@Alfred_Kwong说原书很多内容没有在代码中体现,建议还是读一读原书。其实也是,即使原书所有的 内容在代码中都有体现,我也不可能两篇博文就把所有东西总结出来。我更多的是通过该书的52个主题,结合代码,自己对Objective-C内容进行一遍 梳理,所以不要因为我这两篇文章来决定你该不该买本书看看,我不想做推销,更不想黑。

Effective Objective-C [下]的更多相关文章

  1. 工作组环境下管理windows.

    此处指的是windows7 1.防火墙设置 开启wmi,remote admin,防火墙远程管理 可以使用命令行 netsh advfirewall export "C:\temp\WFco ...

  2. iOS 学习资源

    这份学习资料是为 iOS 初学者所准备的, 旨在帮助 iOS 初学者们快速找到适合自己的学习资料, 节省他们搜索资料的时间, 使他们更好的规划好自己的 iOS 学习路线, 更快的入门, 更准确的定位的 ...

  3. iOS学习资料整理

    视频教程(英文) 视频 简介 Developing iOS 7 Apps for iPhone and iPad 斯坦福开放教程之一, 课程主要讲解了一些 iOS 开发工具和 API 以及 iOS S ...

  4. iOS 学习

    iOS 学习资料 (适合初学者) 本文资料来源于GitHub 一.视频教程(英文) Developing iOS 7 Apps for iPhone and iPad斯坦福开放教程之一, 课程主要讲解 ...

  5. iOS 学习资料汇总

    (适合初学者入门) 本文资料来源于GitHub 一.视频教程(英文) Developing iOS 7 Apps for iPhone and iPad斯坦福开放教程之一, 课程主要讲解了一些 iOS ...

  6. iOS Learning

    转载自:http://www.cocoachina.com/ios/20150111/10894.html iOS 学习资料整理 2015-01-11 20:20 编辑: suiling 分类:iOS ...

  7. BlocksKit的使用

    一.引言 众所周知Block已被广泛用于iOS编程.它们通常被用作可并发执行的逻辑单元的封装,或者作为事件触发的回调.Block比传统回调函数有2点优势: 允许在调用点上下文书写执行逻辑,不用分离函数 ...

  8. iOS书写高质量代码之耦合的处理

    原创 2016-12-26 MrPeak MrPeak杂货铺 耦合是每个程序员都必须面对的话题,也是容易被忽视的存在,怎么处理耦合关系到我们最后的代码质量.今天Peak君和大家聊聊耦合这个基本功话题, ...

  9. 【iOS】关联属性存取数据

    有时候我们需要在现有的类存放一些额外的信息,通常的做法是继承一个子类,然后定义新增加的属性,然而如果我们为每个需要的类都添加一个类显得太麻烦了,objc提供了一个关联属性的特性,可以给一个对象关联一个 ...

  10. Automake

    Automake是用来根据Makefile.am生成Makefile.in的工具 标准Makefile目标 'make all' Build programs, libraries, document ...

随机推荐

  1. BEC translation exercise 2

    Forest fires are a regular feature of Chile's hot, arid summers.智利夏天炙热.干燥,常发生森林火灾.A nearly decade-lo ...

  2. [Unity3D]EZGUI 操作简单介绍

    官方的GUI根本无法跟EZGUI比,无论是资源还是易用性还是速度.EZGUI基于Mesh不占DrawCall.EZGUI是自动合并Mesh成为一个物体,并且贴图自动制作Atlas.所以效率高,CPU消 ...

  3. linux 下安装rar解压

    在liunx下原本是不支持rar文件的,需要安装liunx下的winrar版本,操作如下 wget http://www.rarsoft.com/rar/rarlinux-4.0.1.tar.gz t ...

  4. HDU4585 Shaolin (STL和treap)

    Shaolin HDU - 4585       Shaolin temple is very famous for its Kongfu monks.A lot of young men go to ...

  5. tableau学习笔记—1

    第一部分 第一章 数据可视化 1.1 用数据讲故事 1.2 数据不只是数字 1.3 在数据中寻找什么(关系.模式.异常) 第二章 Tableau概述 2.1 Tableau概述 2.2 产品简介 第三 ...

  6. Python Indentation

    In Python, code blocks don't have explicit begin/end or curly braces to mark beginning and end of th ...

  7. Sentry入门

    指令类型: 创建.删除角色: CREATE ROLE sentry_all; droop role admin_role;   角色授权/撤销权限: 数据库级别角色授权 GRANT ALL ON DA ...

  8. docker 镜像自动升级脚本

    #!/bin/bash # Let's finish it like a flash ARGS=`getopt -o v:"$@"` echo $# if [ $# != 2 ]; ...

  9. HTML(超文本标记语言)

    学习地址:https://developer.mozilla.org/zh-CN/docs/Web/Html

  10. Activiti:MalformedByteSequenceException: 3 字节的 UTF-8 序列的字节 3 无效。

    在win下开发,有时编译或运行项目会报3字节的UTF-8序列的字节3无效. 解决该问题的办法 1.将xml头文件改为GBK编码方式 ,我这里不OK <?xml version="1.0 ...