1. block基本概念
Block是C級別的語法和運(yùn)行時(shí)特性剃袍。Block是一種比較特殊的數(shù)據(jù)類型,它可以保存一段代碼,在合適的時(shí)候取出來調(diào)用寿桨。Block比較類似C函數(shù)司光,但是Block比之C函數(shù)琅坡,其靈活性體現(xiàn)在棧內(nèi)存、堆內(nèi)存的引用残家。Block是蘋果推薦的類型脑蠕,效率高,可以幫助我們組織獨(dú)立的代碼段跪削,并提高復(fù)用性和可讀性谴仙。主要是用來在運(yùn)行中封裝代碼和保存代碼用的,Block可以在任何時(shí)候被執(zhí)行。
從 xcode 4.0 開始碾盐,系統(tǒng)類庫中的函數(shù)越來越多的開始使用 block 作為參數(shù)晃跺,以下是在系統(tǒng)函數(shù)中使用block的部分情況:a.遍歷數(shù)組和字典b.排序c.視圖動(dòng)畫d.結(jié)束回調(diào)e.錯(cuò)誤處理f.多線程等
格式說明:(返回類型)(^塊名稱)(參數(shù)類型列表) = ^(形參列表) {代碼實(shí)現(xiàn)};
如果沒有參數(shù),等號后面參數(shù)列表的()可以省略
2. 需要注意的地方
a. block(代碼塊)是 oc中的一種數(shù)據(jù)類型毫玖,可以被當(dāng)做參數(shù)傳遞掀虎,可以有返回值,是一個(gè)能工作的代碼單元付枫,可以在任何需要的時(shí)候被執(zhí)行烹玉,就像調(diào)用函數(shù)一樣調(diào)用,在 ios 開發(fā)中廣泛使用阐滩。
b. ^ 是 block 的特有的標(biāo)記二打。
c. block? 熟練了解block 的定義(塊代碼的定義),記得實(shí)現(xiàn)代碼包含在 {} 之間掂榔。
d. block 是以內(nèi)聯(lián) inline 函數(shù)的方式被定義使用继效。
e. 本質(zhì)上是輕量級的匿名函數(shù)。
c. 塊代碼的使用注意點(diǎn)
? ?i. 默認(rèn)情況下装获,不允許在塊代碼內(nèi)部修改外部的變量的數(shù)值
? ?ii. __block瑞信,讓外部的變量能夠在block中修改
? ?iii. 循環(huán)引用的問題? __weak (ios5.0以下的版本使用__unsafe_unretained)
? ?iv. 默認(rèn)情況下,block 外部的變量穴豫,在 block 中是只讀的
? v. 塊代碼與代理的區(qū)別(代理和block是IOS上實(shí)現(xiàn)回調(diào)的兩種機(jī)制凡简。Block基本可以代替代理的功能,而且實(shí)現(xiàn)起來比較簡潔,比較推薦能用block的地方不要用代理秤涩;單就編程過程而言,block對開發(fā)者處理邏輯,編程效率,代碼閱讀都有積極影響帜乞。代理是一種很經(jīng)典的模式,我們很多人都已經(jīng)習(xí)慣了這種模式溉仑,若果對block的回調(diào)傳值的過程不是很理解的話挖函,建議使用代理,可以達(dá)到同樣地效果浊竟。)
3.block的修飾
? ?ARC情況下
1.如果用copy修飾Block怨喘,該Block就會存儲在堆空間。則會對Block的內(nèi)部對象進(jìn)行強(qiáng)引用振定,導(dǎo)致循環(huán)引用必怜。內(nèi)存無法釋放。
解決方法:新建一個(gè)指針(__weak typeof(Target) weakTarget = Target )指向Block代碼塊里的對象后频,然后用weakTarget進(jìn)行操作梳庆。就可以解決循環(huán)引用問題。
2.如果用weak修飾Block卑惜,該Block就會存放在椄嘀矗空間,不會出現(xiàn)循環(huán)引用問題露久。
? MRC情況下
用copy修飾后更米,如果要在Block內(nèi)部使用對象,則需要進(jìn)行(__block typeof(Target) blockTarget = Target )處理毫痕。在Block里面用blockTarget進(jìn)行操作征峦。
4.關(guān)于block引用外部變量操作的問題
block 使用,如果引用了外部變量消请,就會對外部變量做一個(gè)copy的操作,記錄住定義block時(shí)候的值栏笆。如果后續(xù)再修改外部變量的值,不會影響block內(nèi)部的數(shù)值的變化臊泰。
外部變量本來是在棧區(qū)中蛉加,block引用的那一刻,就將外部變量copy 到堆區(qū)中了因宇,block里面使用的時(shí)copy 后堆中的變量的值七婴。
所有在block 調(diào)用之前修改外部變量的值,不會影響block里面值的原因察滑。
(如果要驗(yàn)證:可以通過打印地址值的方式來驗(yàn)證,棧區(qū)是高位地址值修肠,相對于棧贺辰,堆在低位地址。通過打印地址發(fā)現(xiàn),block里面的變量的地址值比block外面的地址值要小很多饲化。)
在默認(rèn)的情況下莽鸭,是不允許在block內(nèi)部修改外部變量的值。(原因是:會破壞代碼的可讀性吃靠,不易于維護(hù))
如果我們一定要在block的內(nèi)部修改外部變量的值硫眨,必須在外部變量的前面添加 _ _block ,這樣才會允許修改巢块。使用__block礁阁,說明不再關(guān)心外部變量數(shù)值的具體變化。
為什么使用__block 會達(dá)到這個(gè)效果(可以通過跟蹤地址值發(fā)現(xiàn)問題)族奢?
在定義block時(shí)姥闭,如果引用了外部變量使用了__block的變量。block定義之后越走,外部變量同樣會被copy到堆中棚品,不同的是棧中的那一份沒有了,只保留了堆中的那一份廊敌。在block 中修改的那一份和保留的那一份是同一份铜跑,所以可以修改。
5.block的類型
block有幾種不同的類型骡澈,這里列出常見的三種類型:
_NSConcreteGlobalBlock:全局的靜態(tài)block锅纺,不會訪問任何外部變量,不會涉及到任何拷貝秧廉,比如一個(gè)空的block伞广。它既不在棧中,也不在堆中疼电,我理解為它可能在內(nèi)存的全局區(qū)嚼锄。
_NSConcreteStackBlock:保存在棧中的block,當(dāng)函數(shù)返回時(shí)被銷毀蔽豺。_NSConcreteStackBlock類型的block有閉包行為区丑,也就是有訪問外部變量,并且該block只且只有有一次執(zhí)行修陡,因?yàn)闂V械目臻g是可重復(fù)使用的沧侥,所以當(dāng)棧中的block執(zhí)行一次之后就被清除出棧了,所以無法多次使用魄鸦。
_NSConcreteMallocBlock:保存在堆中的block宴杀,當(dāng)引用計(jì)數(shù)為0時(shí)被銷毀。該類型的block都是由_NSConcreteStackBlock類型的block從棧中復(fù)制到堆中形成的拾因。