從C#到Objective-C,按部就班學習蘋果開發(4)--代碼塊(block)和錯誤異常處理的理解

本隨筆系列主要介紹從一個Windows平臺從事C#開發到Mac平臺蘋果開發的一系列感想和體驗歷程,本系列文章是在起步階段逐步積累的,但願帶給你們更好,更真實的轉換歷程體驗。本文繼續上一篇隨筆《從C#到Objective-C,按部就班學習蘋果開發(3)--分類(category)和協議Protocal的理解》,繼續對比介紹它們二者之間的差別,以便咱們從C#陣營過來的人員加深印象,深刻了解Objective-C語言的特性。本篇隨筆主要針對Objective-C裏面的代碼塊(block)和異常處理概念的理解進行介紹。html

一、Object C的代碼塊(block)

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;

至於代碼塊如何簡化同步調用的問題,讓給讀者本身去了解研究了,我感受也有點頭暈了。哈哈。

 

二、Object C的錯誤及異常處理

咱們知道,在開發各類應用程序或者系統的時候,錯誤確定難以免,有效處理錯誤異常就是你一個頗有必要的內容。在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#並沒有太多的不一樣,咱們瞭解就好,具體碰到什麼問題,在查看下幫助文檔,水來土掩,洗洗睡去吧。

相關文章
相關標籤/搜索