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) {// 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 [下]相关推荐
- [Effective Objective] 熟悉Objective-C
了解 Objective-C Objective_C 是一种面向对象的语言.但与jave.C++等语言不同,它使用了消息结构(messaging structure)而非函数调用(function c ...
- (转载)深入了解iOS中的OOM(低内存崩溃)
英文原文:https://programmer.ink/think/learn-more-about-oom-low-memory-crash-in-ios.html 中文翻译:https://www ...
- 【精】iOS知识树,知识点(包括对象、Block、消息转发、GCD、运行时、runloop、动画、Push、KVO、tableview,UIViewController、提交AppStore)
本文旨在总结iOS知识网络,知识点,该知识网络罗列出常见UIKit.Foundation的对象特点和一些使用经验,可以看成是一本书:文本编辑采用树的形式,对知识点进行罗列,并标注一些使用经验(★)希望 ...
- iOS 学习资料整理
这份学习资料是为 iOS 初学者所准备的, 旨在帮助 iOS 初学者们快速找到适合自己的学习资料, 节省他们搜索资料的时间, 使他们更好的规划好自己的 iOS 学习路线, 更快的入门, 更准确的定位的 ...
- lt;转gt;iOSnbsp;学习资料整理
阅读目录 视频教程(英文) 视频教程(中文) 书籍 博客 文章 相关网站 社区 工具/插件 GitHub Top 50 简介 邮件订阅 文档 指南 Awesome 系列 知乎上的讨论 Quora 上的 ...
- iOS 学习资料整理(中文版)
这份学习资料是为 iOS 初学者所准备的, 旨在帮助 iOS 初学者们快速找到适合自己的学习资料, 节省他们搜索资料的时间, 使他们更好的规划好自己的 iOS 学习路线, 更快的入门, 更准确的定位的 ...
- iOS 学习资料大全
转: http://segmentfault.com/a/1190000002473595 这份学习资料是为 iOS 初学者所准备的, 旨在帮助 iOS 初学者们快速找到适合自己的学习资料, 节省他 ...
- 深入了解iOS中的OOM(低内存崩溃)
在iOS开发过程或者用户反馈中,可能会经常看到这样的情况,用着用着就崩溃了,而在后台查看崩溃栈的时候,找不到崩溃日志.其实这大多数的可能是系统产生了低内存崩溃,也就是OOM(还有一种可能是主线程卡死, ...
- 计算机工程与应用 效率,多目标进化算法搜索鲁棒最优解效率研究-计算机工程与应用.PDF...
多目标进化算法搜索鲁棒最优解效率研究-计算机工程与应用 Computer Engineering and Applications 计算机工程与应用 2011 ,47 (23 ) 29 ⦾研究.探讨⦾ ...
- 论文翻译 —— Model Free Episodic Control
标题:Model Free Episodic Control 文章链接:Model Free Episodic Control 代码实现:sudeepraja Model-Free-Episodic- ...
最新文章
- 机器学习模型部署都有哪些坑?剑桥研究者梳理了99篇相关研究
- 周志华:“数据、算法、算力”人工智能三要素,在未来还要加上“知识”
- UVA 1515 - Pool construction(最小割)
- 【内网穿透】生壳SSH映射 for Linux 使用教程
- linux – 我怎么知道我到somaxconn有多近?
- java arm 编译器下载_最全盘点:18款在线C/C++/Py/Java编译器,一个比一个强大(附地址)...
- riak php7,Laravel中服务提供者的register和boot分别是干什么
- 左移右移位运算_计算机硬件技术基础M2——计算机运算基础(二)
- php 添加 redis 扩展模块
- python list定义_Python中list总结
- oracle sequence sql server,SQL Server 实现oracle的sequence方法示例
- Mysql分区表的使用
- python做计量经济学的书籍_《计量经济学》教材书单
- 将SVG 转换为png -- ImageMagick 转换 svg 为透明png 图
- 如何注册ocx文件。。。
- 阿里云服务器使用freessl配置免费证书Nginx
- vue项目运行npm install报错
- 路径规划-Minimum snap轨迹优化
- js实现模拟自动点击按钮,并且在10秒倒计时之后疯狂点击
- 笔记本电脑下面的任务栏一直转圈圈
热门文章
- 作文第一次用计算机350,第一次做饭的作文350字
- python怎么找资源_查找目标文件太慢了,用好搜索引擎,让你比Python找资源更快捷...
- 开发日记-20190705 关键词 读书笔记 《Perl语言入门》Day 2
- TCP/IP 详解卷一 - TCP CWR、ECE、URG、ACK、PSH、RST、SYN、FIN控制位
- mongodb分片介绍—— 基于范围(数值型)的分片 或者 基于哈希的分片
- python 中super方法的调用
- PyCharm没有run选项,只有run nosetests in XXX
- 2019春第六周作业Compile Summarize
- 《深入理解Java虚拟机》笔记01 -- 运行时数据区
- cocos2d-x 打包成so文件之后,假设出现错误,能够使用ndk-stack来查看里面的异常...