從mac os x 10.6以及ios 4開始,蘋果在gcc和clang編譯器中為c語言引入了乙個新擴充套件:blocks,使得程式設計師可以在c、objective-c、c++和objective-c中使用閉包。blocks有點像函式,但是它可以在其它函式或方法中進行宣告和定義,同時它還是匿名的(匿名函式),並可以捕獲其所在作用域中的變數(閉包特性)。
blocks和c語言中的函式指標有點類似,如果你了解函式指標的話你會發現blocks的會很容易掌握。下面分別是乙個c函式指標和乙個blocks的宣告:
123
int(*
foo)(
int,
int);
int(
^foo
)(int
,int
);
它們都是接受兩個int
型別的引數並且返回乙個int
值,唯一的區別就是函式指標宣告中的*
變成了^
。根據蘋果的說法,之所以選用^
是因為它是c++中唯一不能被過載的運算符號。此外,由於兩者的宣告都過於煩瑣,所以你可以像c中一樣利用typedef
為該型別起乙個別名,方便你在**中使用:
12345
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沒有接受任何引數,括號的部分也可以省略;
123
block
sum=^(
intx
,inty)
void(^
bar)(
void)=
^
執行乙個block和呼叫乙個c函式一樣
1
sum(2
,3);// 輸出 5
此外,在objective-c中乙個block同時也是乙個物件,它也有乙個isa指標指向它的類物件。這意味著你能夠對它傳送諸如-copy
、-release
和retain
等訊息。
1
[
sumcopy
];
blocks具有閉包的特性,所以可以用它來捕獲其所在作用域中的變數:
1234567
891011
void
testblock();
printf
("%d\n"
,ablock
());
// 輸出 3a=
0;printf
("%d\n"
,ablock
());
// 還是輸出 3
}
需要注意的是,兩次輸出的值都為3,即使在第二次輸出前我們已經將a的值賦為0。這是因為在定義ablock
時編譯器已經對a
和b
的值作了乙個const
拷貝(你不能在ablock
中修改a的值)並儲存,導致後續外部對a
的修改沒有影響到ablock
的執行結果。如果想在ablock
中通過引用訪問a
或者修改a
的值,你需要在a
的宣告前加上乙個限定詞__block
:
1234567
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的私有儲存空間失效:
1234567
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
:
123456
-
(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...