Effective Objective-C [下]
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 [下]的更多相关文章
- 工作组环境下管理windows.
此处指的是windows7 1.防火墙设置 开启wmi,remote admin,防火墙远程管理 可以使用命令行 netsh advfirewall export "C:\temp\WFco ...
- iOS 学习资源
这份学习资料是为 iOS 初学者所准备的, 旨在帮助 iOS 初学者们快速找到适合自己的学习资料, 节省他们搜索资料的时间, 使他们更好的规划好自己的 iOS 学习路线, 更快的入门, 更准确的定位的 ...
- iOS学习资料整理
视频教程(英文) 视频 简介 Developing iOS 7 Apps for iPhone and iPad 斯坦福开放教程之一, 课程主要讲解了一些 iOS 开发工具和 API 以及 iOS S ...
- iOS 学习
iOS 学习资料 (适合初学者) 本文资料来源于GitHub 一.视频教程(英文) Developing iOS 7 Apps for iPhone and iPad斯坦福开放教程之一, 课程主要讲解 ...
- iOS 学习资料汇总
(适合初学者入门) 本文资料来源于GitHub 一.视频教程(英文) Developing iOS 7 Apps for iPhone and iPad斯坦福开放教程之一, 课程主要讲解了一些 iOS ...
- iOS Learning
转载自:http://www.cocoachina.com/ios/20150111/10894.html iOS 学习资料整理 2015-01-11 20:20 编辑: suiling 分类:iOS ...
- BlocksKit的使用
一.引言 众所周知Block已被广泛用于iOS编程.它们通常被用作可并发执行的逻辑单元的封装,或者作为事件触发的回调.Block比传统回调函数有2点优势: 允许在调用点上下文书写执行逻辑,不用分离函数 ...
- iOS书写高质量代码之耦合的处理
原创 2016-12-26 MrPeak MrPeak杂货铺 耦合是每个程序员都必须面对的话题,也是容易被忽视的存在,怎么处理耦合关系到我们最后的代码质量.今天Peak君和大家聊聊耦合这个基本功话题, ...
- 【iOS】关联属性存取数据
有时候我们需要在现有的类存放一些额外的信息,通常的做法是继承一个子类,然后定义新增加的属性,然而如果我们为每个需要的类都添加一个类显得太麻烦了,objc提供了一个关联属性的特性,可以给一个对象关联一个 ...
- Automake
Automake是用来根据Makefile.am生成Makefile.in的工具 标准Makefile目标 'make all' Build programs, libraries, document ...
随机推荐
- 分布式_理论_06_ 一致性算法 Raft
一.前言 五.参考资料 1.分布式理论(六)—— Raft 算法 2.分布式理论(六) - 一致性协议Raft
- Spring_总结_03_装配Bean(一)_自动装配
一.前言 本文承接上一节:Spring_总结_02_依赖注入 在上一节我们了解到依赖注入的实质就是装配. 这一节我们来学习下装配Bean的相关知识. 二.Bean的装配机制 1.三种装配机制 Spri ...
- 条款47:请使用traits class表示类型信息
在stl的算法中,我们的希望往往是根据不同的迭代器类型进行不同的更有效率的操作: template<typename IterT, typename DistT> void advance ...
- 高可用-软件heartbeat的入门介绍
注:参考互联网整理. 一.简介Linux-HA的全称是High-Availability Linux,它是一个开源项目,这个开源项目的目标是:通过社区开发者的共同努力,提供一个增强linux可靠性(r ...
- Cash Machine(多重背包二进制转换)
个人心得:多重背包,自己根据转换方程写总是TLE,后面去网上看了二进制转换,不太理解: 后面仔细想了下,用自己的思想理解下把,就是将对应number,cash总和用二进制拆分, 然后全部装入到一个数组 ...
- bzoj 3709: [PA2014]Bohater 贪心
题目: 在一款电脑游戏中,你需要打败\(n\)只怪物(从\(1\)到\(n\)编号).为了打败第\(i\)只怪物,你需要消耗\(d_i\)点生命值,但怪物死后会掉落血药,使你恢复\(a_i\)点生命值 ...
- webrtc自带client的音频引擎创建代码走读
src\webrtc\examples\peerconnection\client\conductor.cc1.bool Conductor::InitializePeerConnection()1. ...
- laravel的批量插入
在日常开发中,用到批量插入的操作还是挺多的.记得很早很早以前,我还是在循环中写sql插入,结果被项目经理按在地上摩擦.好吧,性能这东西,用不到的时候还好,万一性能成为瓶颈,那代码优化,数据库优化就首当 ...
- Task Crontab
Crontab 1.查看任务 crontab -l 2.编辑任务 1)对应用户登录后编辑其下的作业 crontab -e 2)删除指定用户任务 crontab -u user -r 3)删除用户下指定 ...
- distinct可以用in代替(小技巧)
distinct可以用in代替,in的好处是直接能获取所有数据,而distunct只能获取distinct的字段,不过效率肯定高一些.