IOS開發 Blocks詳解

2021-06-12 03:23:58 字數 4571 閱讀 5940

從mac os x 10.6以及ios 4開始,蘋果在gcc和clang編譯器中為c語言引入了乙個新擴充套件:blocks,使得程式設計師可以在c、objective-c、c++和objective-c中使用閉包。blocks有點像函式,但是它可以在其它函式或方法中進行宣告和定義,同時它還是匿名的(匿名函式),並可以捕獲其所在作用域中的變數(閉包特性)。

blocks和c語言中的函式指標有點類似,如果你了解函式指標的話你會發現blocks的會很容易掌握。下面分別是乙個c函式指標和乙個blocks的宣告:

1

23

int(*

foo)(

int,

int);

int(

^foo

)(int

,int

);

它們都是接受兩個int型別的引數並且返回乙個int值,唯一的區別就是函式指標宣告中的*變成了^。根據蘋果的說法,之所以選用^是因為它是c++中唯一不能被過載的運算符號。此外,由於兩者的宣告都過於煩瑣,所以你可以像c中一樣利用typedef為該型別起乙個別名,方便你在**中使用:

123

45

typedef

int(*fp

)(int

,int

);fp

foo;

typedef

int(

^block

)(int

,int

);block

foo;

接下來看看如何定義乙個block:

1

block

sum=

int^

(intx,

inty);

其中^標誌著這是乙個block定義,在它前面是其返回值的型別,括號中是其接受的各個引數,而block的主體則位於{}中。由於編譯器可以自動推斷block的返回型別,所以^前面的返回型別可以略去不寫,同時,如果該block沒有接受任何引數,括號的部分也可以省略;

1

23

block

sum=^(

intx

,inty)

void(^

bar)(

void)=

^

執行乙個block和呼叫乙個c函式一樣

1

sum(2

,3);// 輸出 5

此外,在objective-c中乙個block同時也是乙個物件,它也有乙個isa指標指向它的類物件。這意味著你能夠對它傳送諸如-copy-releaseretain等訊息。

1

[

sumcopy

];

blocks具有閉包的特性,所以可以用它來捕獲其所在作用域中的變數:

123

4567

891011

void

testblock();

printf

("%d\n"

,ablock

());

// 輸出 3a=

0;printf

("%d\n"

,ablock

());

// 還是輸出 3

}

需要注意的是,兩次輸出的值都為3,即使在第二次輸出前我們已經將a的值賦為0。這是因為在定義ablock時編譯器已經對ab的值作了乙個const拷貝(你不能在ablock中修改a的值)並儲存,導致後續外部對a的修改沒有影響到ablock的執行結果。如果想在ablock中通過引用訪問a或者修改a的值,你需要在a的宣告前加上乙個限定詞__block

123

4567

89

void

testblock();

a=0;

printf

("%d\n"

,ablock

());

// 輸出 2

}

這樣,使用該限定詞的變數會通過引用的方式傳入block,使得它的值可以在block執行後被修改。這樣的變數通常是儲存在棧中的,但是如果引用該變數的block被拷貝,它也會隨之被拷貝到堆中。

編譯後block中**的儲存和載入方式其實和普通的函式一樣,但是它還需要額外的空間去儲存其所捕獲的變數,也就是說,block中所引用的變數需要被拷貝到一塊其私有的記憶體中去。當你宣告定義了乙個block時,它的這塊私有記憶體空間是分配在棧中。所以預設情況下當定義block的方法或函式返回後這塊記憶體也會隨之失效。所以當你需要返回乙個block時,你需要顯式地用block_copy()(如果該block已經在堆中,它的retain count將會加1)將拷貝到堆中。由於在objective-c中block也是乙個物件,所以你也可以對它傳送-copy訊息來達到同樣地效果。既然有乙個拷貝的過程,那麼當你使用完畢的時候也需要呼叫用block_release()進行對應的釋放操作,同理,在objective-c中也可對其傳送-release訊息。需要注意的一點是下面這種情況也會導致乙個block的私有儲存空間失效:

123

4567

891011

12

typedef

void(^

block

)(void

);void

foo();}

else;}

...// 此時ablock已經指向一塊無效的記憶體

}

上面講到在objective-c中block也是乙個物件,所以你可以對其傳送-copy-retain-copy甚至-autorelease等訊息進行對應的記憶體管理。但是在objective-c中block有一點不同,它會對所引用的nsobject物件自動進行retain操作(當block銷毀時這些物件也會被release),包括其它block。所以你也可以利用這個特性使得乙個非繼承自nsobject的物件被自動retain,方法就是在該物件的宣告加上__attribute__((nsobject))。 需要注意的是,如果你引用的是乙個例項變數,它會直接對self進行retain,這有時候有可能會產生乙個引用環(兩個或以上的物件之間直接或間接地互相引用)並導致記憶體洩露。解決的方法是:當需要在block中訪問例項變數的時候,建立乙個指向self的指標,並對其使用__block修飾符,這樣self不會被自動retain

123

456

-

(void

)foo

}

因為使用__block修飾符的物件指標的值在block中是可以被修改的,如果block自動retain該指標指向的物件,一旦其指標值被修改時應該怎麼辦呢?而蘋果的做法就是乾脆不自動retain它。

在objective-c中還要注意,雖然block物件可以接受-retain訊息,但是對於乙個存在於棧中的block傳送該訊息是沒有效果的。所以,當你要將blocks物件儲存到字典或陣列中去之前,需要先執行相應的拷貝操作(因為nsarray或nsdictionary之類容器會自動retain存入的物件)。同理,對乙個已經拷貝到堆中的block傳送-copy訊息也是不會真正執行拷貝,只是將其引用計數加1,這也意味著拷貝乙個block和重新建立乙個一樣的block是不一樣的,通過拷貝得到的block會共享所有宣告為__block的變數,所以如果你想要乙個全新的block,你需要重新建立一次。

iOS開發 Block詳解

block是乙個非常有特色的語法,它可以把乙個 塊作為乙個變數來儲存,也可以通過函式傳遞變數,然後讓其他的物件來執行這一 塊。可以儲存傳遞並在其他地方執行的 塊,這是我對block的理解,也是我覺得block最吸引我的地方。直接定義 返回block指標 返回型別 可省略 引數型別 引數 沒有引數可省...

IOS開發之屬性詳解

在開發過程中經常要用到定義屬性,property和 synthesize是經常用到的關鍵字,那麼到底該如何正確定義乙個屬性呢,我們需要了解其中用到的關鍵字。atomic 原子操作 原子性是指事務的乙個完整操作,操作成功就提交,反之就回滾.原子操作就是指具有原子性的操作 在objective c 屬性...

iOS索引列開發詳解

ios索引列開發,這有篇文章 下面是我自己的 import contactsmodel.h import xdsearchbar.h 這兩個是我自定義的類,乙個聯絡人model 乙個搜尋欄,import import contactsmodel.h import xdsearchbar.h inte...