iOS面試題與核心基礎(chǔ)之block

block本質(zhì)

  • block本質(zhì)上是一個(gè)OC對(duì)象(內(nèi)部有個(gè)isa指針)
  • block是封裝了函數(shù)調(diào)用以及函數(shù)調(diào)用環(huán)境的OC對(duì)象
block的底層結(jié)構(gòu)

可以通過clang去編譯成c++源碼來驗(yàn)證

block的變量捕獲

  1. 局部變量
    靜態(tài)局部變量驼抹,捕獲指針还棱,即屬于指針傳遞饮亏;
    auto的基本數(shù)據(jù)類型局部變量慨绳,捕獲其值(直接拷貝值),屬于值傳遞被环;
    auto的對(duì)象類型連同所有權(quán)修飾符(引用修飾符)一起捕獲
  2. 全局變量
    不捕獲糙及,直接訪問

block的類型

block有3種類型,可以通過調(diào)用class方法或者isa指針查看具體類型筛欢,最終都是繼承自NSBlock類型

  • NSGlobalBlock ( _NSConcreteGlobalBlock )存在數(shù)據(jù)區(qū)
    沒有捕獲auto變量(局部非static變量)浸锨,對(duì)其copy,什么都不做
  • NSStackBlock ( _NSConcreteStackBlock )存在棧區(qū)
    捕獲auto變量版姑,對(duì)其copy柱搜,會(huì)由棧復(fù)制到堆
  • NSMallocBlock ( _NSConcreteMallocBlock )存在堆區(qū)
    NSStackBlock復(fù)制而來的,對(duì)其copy剥险,引用計(jì)數(shù)+1

block的copy

在ARC環(huán)境下聪蘸,編譯器會(huì)根據(jù)情況自動(dòng)將棧上的block復(fù)制到堆上,比如以下情況

  • block作為函數(shù)返回值時(shí)
  • 將block賦值給__strong指針時(shí)
  • block作為Cocoa API中方法名含有usingBlock的方法參數(shù)時(shí)
  • block作為GCD API的方法參數(shù)時(shí)
    在MRC環(huán)境下需要自己管理表制,自己實(shí)現(xiàn)copy才不會(huì)因?yàn)閎lock退棧銷毀導(dǎo)致的崩潰
// MRC下block屬性的建議寫法
@property (copy, nonatomic) void (^block)(void);
// ARC下block屬性也可以使用strong關(guān)鍵字
@property (strong, nonatomic) void (^block)(void);
@property (copy, nonatomic) void (^block)(void);

__block本質(zhì)

編譯器會(huì)將__block修飾的變量包裝成一個(gè)對(duì)象健爬;
當(dāng)block在棧上時(shí),forwarding是指向自身的指針么介;當(dāng)block被拷貝到堆上時(shí)娜遵,棧上的forwarding指針會(huì)指向堆上的block,堆上的forwarding指針還是指向自身夭拌。這樣不論訪問的是棧上的指針還是堆上的指針最終都能訪問到堆上的真正需要操作的變量魔熏。
使用 __block可以用于解決block內(nèi)部無法修改auto變量值的問題。 __block不能修飾全局變量鸽扁、靜態(tài)變量(static)蒜绽。全局變量能直接訪問修改,而靜態(tài)局部變量值指針訪問桶现,也能修改躲雅。

如何解決循環(huán)引用

  • __weak
__weak typeof(self) weakSelf = self;
self.block = ^{
    NSLog("%p",weakSelf);
}

使用上述方法時(shí),block內(nèi)部執(zhí)行時(shí)間比較長(zhǎng)骡和,在執(zhí)行時(shí)相赁,self突然被釋放了(例如self是控制器相寇,控制器返回了),而block是在堆空間上钮科,并不會(huì)被釋放唤衫,當(dāng)block內(nèi)部繼續(xù)訪問self,這個(gè)時(shí)候會(huì)出現(xiàn)野指針, 也就是說weakSelf變成了nil绵脯,極有可能導(dǎo)致崩潰佳励。
解決方案就是使用__strong在block內(nèi)部對(duì)weakSelf進(jìn)行強(qiáng)引用

__weak typeof(self) weakSelf = self;
self.block = ^{
__strong typeof(self) strongSelf = weakSelf;
    NSLog("%p",strongSelf);
}

block引用的外部變量的是__weak修飾的weakSelf對(duì)象,
所以block初始化并copy到堆上蛆挫,不會(huì)強(qiáng)引用self赃承。
但是執(zhí)行block的時(shí)候,其實(shí)是執(zhí)行一個(gè)靜態(tài)函數(shù)悴侵,
在執(zhí)行的過程中瞧剖,生成了strongSelf對(duì)象,這個(gè)時(shí)候可免,產(chǎn)生了閉環(huán)抓于。
但是這個(gè)strongSelf在棧空間上浇借,在函數(shù)執(zhí)行結(jié)束后毡咏,strongSelf會(huì)被系統(tǒng)回收,此時(shí)閉環(huán)被打破逮刨。
內(nèi)容來自:http://www.reibang.com/p/24c7e8563c56呕缭,未驗(yàn)證

  • __unsafe_unretained
__unsafe_unretained id weakSelf = self;
self.block = ^{
    NSLog("%p",weakSelf);
}

缺點(diǎn):weakSelf被釋放之后指針不會(huì)被設(shè)置為nil,訪問將引起崩潰

  • __block
__block id weakSelf = self;
self.block = ^{
    NSLog("%p",weakSelf);
    weakSelf = nil;
}

缺點(diǎn):block必須執(zhí)行之后weakSelf = nil修己,才能打破循環(huán)引用恢总。

以上是ARC前提之下的解決方法,那么在MRC之下仍然可以使用__unsafe_unretained,但要注意weakSelf被釋放的時(shí)機(jī)睬愤。MRC下__block修飾的變量片仿,并不改變引用計(jì)數(shù),同時(shí)block內(nèi)部并不對(duì)引入的外部對(duì)象尤辱,更改引用計(jì)數(shù)砂豌。所以也可以使用__block來解決。

面試題

  1. 什么是block
    block是將函數(shù)及其執(zhí)行上下文(調(diào)用環(huán)境)封裝起來的對(duì)象光督。

  2. 下面代碼的打印結(jié)果是什么阳距?分析一下

int multiplier = 6;
int (^Block)(int) = ^int(int num) {
  return num * multiplier;
}
multiplier = 2;
NSLog(@"result is %d", Block(3));

局部基本數(shù)據(jù)類型,block直接誒捕獲其值结借,后續(xù)值的修改對(duì)block已經(jīng)捕獲的值沒影響筐摘。所以是結(jié)果是result is 18

  1. 什么場(chǎng)景下需要使用_block修飾符
    一般情況下,對(duì)捕獲的 局部變量 進(jìn)行賦值操作需要添加__block修飾符。(當(dāng)且僅當(dāng)對(duì)變量本身進(jìn)行修改時(shí)需要添加咖熟,比如被捕獲的變量是數(shù)組圃酵,對(duì)數(shù)組進(jìn)行增刪改數(shù)組元素則不需要,修改數(shù)組本身這個(gè)對(duì)象才需要添加馍管。)對(duì)于靜態(tài)局部變量郭赐、全局變量則不需要。靜態(tài)局部變量通過指針訪問确沸,全局變量則是直接訪問堪置,都能做到修改其值。

  2. 下面代碼的打印結(jié)果是什么张惹?分析一下

__block int multiplier = 6;
int (^Block)(int) = ^int(int num) {
  return num * multiplier;
}
multiplier = 2;
NSLog(@"result is %d", Block(3));

編譯器會(huì)將__block修飾的變量包裝成一個(gè)對(duì)象;
multiplier = 2; => 編譯之后變成 multiplier.__forwarding.multiplier = 2;
也就是說block執(zhí)行之前能對(duì)multiplier的值修改成功岭洲,結(jié)果是6

  1. 下面的代碼存在問題么宛逗?為什么?
__block MyBlockViewController* blockSelf = self
_myBlock = ^int(int num) {
     return num *blockSelf.multiplier
}
_myBlock(2)盾剩;

ARC下會(huì)產(chǎn)生循環(huán)引用雷激,MRC下則不會(huì)。
上述代碼中告私,block被賦值給『_block』實(shí)例變量屎暇,block被復(fù)制到了堆上,而堆上的block會(huì)對(duì)__block修飾的變量產(chǎn)生強(qiáng)引用驻粟,也就是對(duì)self產(chǎn)生了間接強(qiáng)引用根悼,self本身對(duì)『_block』實(shí)例變量是強(qiáng)引用故而導(dǎo)致了循環(huán)引用。解決方法是蜀撑,在block內(nèi)部使用完blockSelf之后釋放掉挤巡。但是如果block一直不被執(zhí)行的話,強(qiáng)引用就會(huì)一直存在酷麦。
MRC下矿卑,block自行管理,編譯器不會(huì)將block'復(fù)制到堆上沃饶,而棧上的block并不會(huì)對(duì)__block變量產(chǎn)生強(qiáng)引用(因block也可能被隨時(shí)釋放)母廷,故而沒有循環(huán)引用問題。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末糊肤,一起剝皮案震驚了整個(gè)濱河市琴昆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌馆揉,老刑警劉巖椎咧,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡勤讽,警方通過查閱死者的電腦和手機(jī)蟋座,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來脚牍,“玉大人向臀,你說我怎么就攤上這事≈钕粒” “怎么了券膀?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)驯遇。 經(jīng)常有香客問我芹彬,道長(zhǎng),這世上最難降的妖魔是什么叉庐? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任舒帮,我火速辦了婚禮,結(jié)果婚禮上陡叠,老公的妹妹穿的比我還像新娘玩郊。我一直安慰自己,他們只是感情好枉阵,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布译红。 她就那樣靜靜地躺著,像睡著了一般兴溜。 火紅的嫁衣襯著肌膚如雪侦厚。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天拙徽,我揣著相機(jī)與錄音假夺,去河邊找鬼。 笑死斋攀,一個(gè)胖子當(dāng)著我的面吹牛已卷,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播淳蔼,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼侧蘸,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了鹉梨?” 一聲冷哼從身側(cè)響起讳癌,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎存皂,沒想到半個(gè)月后晌坤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體逢艘,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年骤菠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了它改。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡商乎,死狀恐怖央拖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鹉戚,我是刑警寧澤鲜戒,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站抹凳,受9級(jí)特大地震影響遏餐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜赢底,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一失都、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧颖系,春花似錦、人聲如沸辩越。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽黔攒。三九已至趁啸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間督惰,已是汗流浹背不傅。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留赏胚,地道東北人访娶。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像觉阅,于是被迫代替她去往敵國(guó)和親崖疤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容