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) {// somethingep.emit("template", template);
});
$.get("data", function (data) {// somethingep.emit("data", data);
});
$.get("l10n", function (l10n) {// somethingep.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)intervalblock:(void(^)())blockrepeats:(BOOL)repeats;@end@implementation NSTimer (EOCBlocksSupport)+ (void)eoc_scheduledTimerWithTimeInterval:(NSTimeInterval)intervalblock:(void(^)())blockrepeats:(BOOL)repeats
{return [self scheduledTimerWithTimeInterval:intervaltarget:selfselector:@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内容进行一遍 梳理,所以不要因为我这两篇文章来决定你该不该买本书看看,我不想做推销,更不想黑。

转载于:https://www.cnblogs.com/zhidao-chen/p/3419492.html

Effective Objective-C [下]相关推荐

  1. [Effective Objective] 熟悉Objective-C

    了解 Objective-C Objective_C 是一种面向对象的语言.但与jave.C++等语言不同,它使用了消息结构(messaging structure)而非函数调用(function c ...

  2. (转载)深入了解iOS中的OOM(低内存崩溃)

    英文原文:https://programmer.ink/think/learn-more-about-oom-low-memory-crash-in-ios.html 中文翻译:https://www ...

  3. 【精】iOS知识树,知识点(包括对象、Block、消息转发、GCD、运行时、runloop、动画、Push、KVO、tableview,UIViewController、提交AppStore)

    本文旨在总结iOS知识网络,知识点,该知识网络罗列出常见UIKit.Foundation的对象特点和一些使用经验,可以看成是一本书:文本编辑采用树的形式,对知识点进行罗列,并标注一些使用经验(★)希望 ...

  4. iOS 学习资料整理

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

  5. lt;转gt;iOSnbsp;学习资料整理

    阅读目录 视频教程(英文) 视频教程(中文) 书籍 博客 文章 相关网站 社区 工具/插件 GitHub Top 50 简介 邮件订阅 文档 指南 Awesome 系列 知乎上的讨论 Quora 上的 ...

  6. iOS 学习资料整理(中文版)

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

  7. iOS 学习资料大全

    转:  http://segmentfault.com/a/1190000002473595 这份学习资料是为 iOS 初学者所准备的, 旨在帮助 iOS 初学者们快速找到适合自己的学习资料, 节省他 ...

  8. 深入了解iOS中的OOM(低内存崩溃)

    在iOS开发过程或者用户反馈中,可能会经常看到这样的情况,用着用着就崩溃了,而在后台查看崩溃栈的时候,找不到崩溃日志.其实这大多数的可能是系统产生了低内存崩溃,也就是OOM(还有一种可能是主线程卡死, ...

  9. 计算机工程与应用 效率,多目标进化算法搜索鲁棒最优解效率研究-计算机工程与应用.PDF...

    多目标进化算法搜索鲁棒最优解效率研究-计算机工程与应用 Computer Engineering and Applications 计算机工程与应用 2011 ,47 (23 ) 29 ⦾研究.探讨⦾ ...

  10. 论文翻译 —— Model Free Episodic Control

    标题:Model Free Episodic Control 文章链接:Model Free Episodic Control 代码实现:sudeepraja Model-Free-Episodic- ...

最新文章

  1. 机器学习模型部署都有哪些坑?剑桥研究者梳理了99篇相关研究
  2. 周志华:“数据、算法、算力”人工智能三要素,在未来还要加上“知识”
  3. UVA 1515 - Pool construction(最小割)
  4. 【内网穿透】生壳SSH映射 for Linux 使用教程
  5. linux – 我怎么知道我到somaxconn有多近?
  6. java arm 编译器下载_最全盘点:18款在线C/C++/Py/Java编译器,一个比一个强大(附地址)...
  7. riak php7,Laravel中服务提供者的register和boot分别是干什么
  8. 左移右移位运算_计算机硬件技术基础M2——计算机运算基础(二)
  9. php 添加 redis 扩展模块
  10. python list定义_Python中list总结
  11. oracle sequence sql server,SQL Server 实现oracle的sequence方法示例
  12. Mysql分区表的使用
  13. python做计量经济学的书籍_《计量经济学》教材书单
  14. 将SVG 转换为png -- ImageMagick 转换 svg 为透明png 图
  15. 如何注册ocx文件。。。
  16. 阿里云服务器使用freessl配置免费证书Nginx
  17. vue项目运行npm install报错
  18. 路径规划-Minimum snap轨迹优化
  19. js实现模拟自动点击按钮,并且在10秒倒计时之后疯狂点击
  20. 笔记本电脑下面的任务栏一直转圈圈

热门文章

  1. 作文第一次用计算机350,第一次做饭的作文350字
  2. python怎么找资源_查找目标文件太慢了,用好搜索引擎,让你比Python找资源更快捷...
  3. 开发日记-20190705 关键词 读书笔记 《Perl语言入门》Day 2
  4. TCP/IP 详解卷一 - TCP CWR、ECE、URG、ACK、PSH、RST、SYN、FIN控制位
  5. mongodb分片介绍—— 基于范围(数值型)的分片 或者 基于哈希的分片
  6. python 中super方法的调用
  7. PyCharm没有run选项,只有run nosetests in XXX
  8. 2019春第六周作业Compile Summarize
  9. 《深入理解Java虚拟机》笔记01 -- 运行时数据区
  10. cocos2d-x 打包成so文件之后,假设出现错误,能够使用ndk-stack来查看里面的异常...