本随笔系列主要介绍从一个Windows平台从事C#开发到Mac平台苹果开发的一系列感想和体验历程,本系列文章是在起步阶段逐步积累的,但愿带给你们更好,更真实的转换历程体验。本文继续上一篇随笔《从C#到Objective-C,按部就班学习苹果开发(3)--分类(category)和协议Protocal的理解》,继续对比介绍它们二者之间的差别,以便咱们从C#阵营过来的人员加深印象,深刻了解Objective-C语言的特性。本篇随笔主要针对Objective-C里面的代码块(block)和异常处理概念的理解进行介绍。html
Objective-C的代码块从刚刚学习的时候,感受有点奇怪,慢慢感受它在C#里面也有点熟悉,它在Objective-C里面的引入,好像是主要用来解决代码回调和同步调用的问题的,说到这里,若是熟悉C#的特性的,可能会联想到了C#里的Action<T>和Func<T>的概念了吧,没错,他们就是一丘之貉,哈哈。编程
代码块本质上是和其余变量相似。不一样的是,代码块存储的数据是一个函数体。使用代码块是,你能够像调用其余标准函数同样,传入参数数,并获得返回值,字符(^)是代码块的语法标记。c#
以下面的例子就是一个代码块的定义网络
void (^simpleBlock)(void) = ^{ NSLog(@"This is a block"); };
定义后,你就能够经过相似函数的方式进行使用了,看了下面的代码是否是感受很熟悉的样子呢。app
simpleBlock();
固然,对于这样的东西,它也是能够接受参数的,即便是多个参数也没问题,这个若是是带参数的,应该就是和C#的Func<T>很类似了,下面是一个两个参数的代码块例子。less
double (^multiplyTwoValues)(double, double) = ^(double firstValue, double secondValue) { return firstValue * secondValue; }; double result = multiplyTwoValues(2,4);
这样的代码块,它还能够获取类里面定义的局部变量,可是因为它的特殊性,好像若是不加特殊处理,它获取到的变量或者属性的值,是在它出现的那瞬间的快照。dom
下面一个例子,很好介绍代码块里面获取内容是快照的现实。编程语言
int anInteger = 42; void (^testBlock)(void) = ^{ NSLog(@"Integer is: %i", anInteger); }; anInteger = 84; testBlock();
上面代码块里面,打印出来的值,是42,而非84,由于它在代码块出现的那瞬间,就拿到了局部变量,以后就没有跟随大部队变化了。函数
那这种方式有无变通的方法,让它能够根据变量的变化而自动变化呢?固然有了,须要特殊处理便可,答案就是使用__block进行标识,它就能够跟随大部队的步伐了。post
若是上面的代码块里面变量的定义使用了这个关键字,那么值就彷佛84了,以下代码块所示。
__block int anInteger = 42; void (^testBlock)(void) = ^{ NSLog(@"Integer is: %i", anInteger); }; anInteger = 84; testBlock();
这个__block功能很强大,告诉编译器,它能够获取变量的最新值,也能够在代码块里面对值进行修改(危险吗?反正你知道就好)
前面说道了,Objective-C里面的代码块相似c#里面lambda的Action 和 Func 那么举个例子来大体介绍下把。
对比一下下面两组代码,其一是Objective-C的代码块
typedef void (^MethodBlock)(int); - (void) fooWithBlock:(MethodBlock)block { int a = 5; block(a); } - (void) regularFoo { [self fooWithBlock:^(int val) { NSLog(@"%d", val); }]; }
接着是C#里面的代码例子,感受它们很接近吧。在这里,你可能会感叹,编程语言这个世界里,很小,世界都趋向于大同了。
void Foo(Action<int> m) { int a = 5; m(a); } void RegularFoo() { Foo(val => // Or: Foo(delegate(int val) { Console.WriteLine(val); }); }
不过代码块的使用,你会慢慢感受它虽然很强大,可是不少地方也不是很容易理解,毕竟对于咱们这些入门没有很深根基的人来讲,要慢慢消化。
再来看看下面这个例子代码,这个方法里面的代码块定义,颇有意思。
- (void)beginTaskWithCallbackBlock:(void (^)(void))callbackBlock { ... callbackBlock(); }
再来看看下面这个代码块,你可能会更晕,没事,晕了就对了,说明你是一个正常的人。
void (^(^complexBlock)(void (^)(void)))(void) = ^ (void (^aBlock)(void)) { ... return ^{ ... }; };
最后记得,若是是一个方法有多个参数,记得把代码块的参数放到最后来定义。
- (void)beginTaskWithName:(NSString *)name completion:(void(^)(void))callback;
至于代码块如何简化同步调用的问题,让给读者本身去了解研究了,我感受也有点头晕了。哈哈。
咱们知道,在开发各类应用程序或者系统的时候,错误确定难以免,有效处理错误异常就是你一个颇有必要的内容。在C#里面,咱们若是须要抛出异常,咱们使用throw方法进行,全部的错误都以异常对象Exception做为基类进行扩展,包括各类各样的异常对象,而对错误异常的捕捉是经过try {} catch(Exception ex) finally {}这样的代码或者相似处理进行的,对于Objective-C来讲,它又是如何处理错误异常的呢?
其实Objective-C对错误处理的机制也差很少,它对异常的支持包括四个编译器指令: @try
, @catch
, @throw
以及 @finally。是否是又一次感受到语言的大同了,这个东西和C#的处理几乎没有什么差异。
另外Objective-C还引入了一个NSError的东西,这个东西和NSException有什么关系呢?这个东西有点相似于咱们在C#开发的时候,增长一个out的输入参数,用来把程序内部的错误信息传递出去,而后交给调用者,让它们爱怎么用就怎么用,反正我处理完成了,有无错误我都告诉你了。因为NSError能够传递的信息比较丰富,通常来讲这样对程序的处理也很方便。
如网络链接的异常,你能够经过下面的代码把它传递出来。
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
下面咱们来看看一个写文件的错误如何处理的,首先定义一个函数,包含了NSError的参数的,注意通常这个参数是放到最后的,这点好像和咱们有些这样处理的C#约定也是同样的。
- (BOOL)writeToURL:(NSURL *)aURL
options:(NSDataWritingOptions)mask
error:(NSError **)errorPtr;
那咱们调用这个writeToURL的函数的时候,有错误发生就应该处理,错误发生的时候,它执行完毕了,而且返回一个NO的值
NSError *anyError; BOOL success = [receivedData writeToURL:someLocalFileURL options:0 error:&anyError]; if (!success) { NSLog(@"Write failed with error: %@", anyError); // present error to user }
为了表示错误的了来源,NSError有一个domain的属性,约定通常以公司的名称(或特别的名称)来进行区分。
com.iqidi.appOrFrameworkName.ErrorDomain
如构造一个NSError的代码大概以下所示。
NSString *domain = @"com.iqidi.MyApplication.ErrorDomain"; NSString *desc = NSLocalizedString(@"Unable to…", @""); NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc }; NSError *error = [NSError errorWithDomain:domain code:-101 userInfo:userInfo];
而常规的异常,咱们通常仍是经过NSException进行处理,异常就是发生问题的时候,停下来第一时间请示如何处理,若是有处理的线路就按照处理的线路进行,不然就一级级往上推了。
它的处理和C#差很少,咱们都很熟悉了,代码结构以下所示。
@try { // code that throws an exception ... } @catch (CustomException *ce) { // most specific type // handle exception ce ... } @catch (NSException *ne) { // less specific type // do whatever recovery is necessary at his level ... // rethrow the exception so it's handled at a higher level @throw; } @catch (id ue) { // least specific type // code that handles this exception ... } @finally { // perform tasks necessary whether exception occurred or not ... }
异常的构造和抛出代码和C#的也很相似
NSException* myException = [NSException exceptionWithName:@"FileNotFoundException" reason:@"File Not Found on System" userInfo:nil]; @throw myException;
若是在处理异常的时候,须要处理一些对象的内存释放,那么通常是把它放到@finally包含的代码块里面。
这个和C#相似,虽然C#不会须要处理内存的释放问题,可是对于一些耗时的操做对象,如Connection,通常最好也放到finally里面确保关闭,处理相似。
- (void)doSomething { NSMutableArray *anArray = nil; array = [[NSMutableArray alloc] initWithCapacity:0]; @try { [self doSomethingElse:anArray]; } @finally { [anArray release]; } }
若是要抛出原汁原味的异常,这点也和C#类似,经过@throw;方法便可。
@try { NSException *e = [NSException exceptionWithName:@"FileNotFoundException" reason:@"File Not Found on System" userInfo:nil]; @throw e; } @catch(NSException *e) { @throw; // rethrows e implicitly }
看到这里,发现大多数的处理机制和语法使用,和C#并没有太多的不一样,咱们了解就好,具体碰到什么问题,在查看下帮助文档,水来土掩,洗洗睡去吧。