iOS中的閉包(一)

轉(zhuǎn)載請注明出處:
http://www.reibang.com/p/431bddd44cdb
作者:紀小衰

一、基本用法

1.定義一個閉包類型

typedef void(^Call)(NSUInteger);

2.兩種方式聲明一個閉包

Call call;
NSUInteger(^sum)(NSUInteger i,NSUInteger j);

3.兩種方式創(chuàng)建一個閉包

call = ^(NSUInteger i){    
  NSLog(@"%ld",i);
};
sum = ^(NSUInteger i,NSUInteger j){    
  return i+j;
};

4.閉包作為函數(shù)參數(shù)

- (void)testBlock:(NSUInteger(^)(NSUInteger i, NSUInteger j))block{    
  NSLog(@"sum = %ld",block(1,2));
}

二弥奸、閉包的強引用

閉包內(nèi)可以直接使用閉包調(diào)用上下文的對象和方法丹喻,并且對在閉包中使用的對象進行一次強引用万伤。為了驗證這點敦姻,我們可以新建一個TestView類,繼承自UIView并重寫dealloc方法用來判斷是否被銷毀:

- (void)dealloc{
    NSLog(@"dealloc");
}

新建一個UIViewController街夭,聲明一個閉包的屬性焊切,讓UIViewController持有一個閉包

@interface ViewController : UIViewController
@property (strong, nonatomic) void(^testBlock)() ;
@end

在viewDidload中創(chuàng)建TestView添加到vc的view上扮授,設(shè)置屬性閉包,并且在閉包中輸出view专肪,然后移除TestView

- (void)viewDidLoad {
    [super viewDidLoad]; 
    TestView* testview = [[TestView alloc] init];
    [self.view addSubview:testview];
    [self setTestBlock:^{
        NSLog(@"%@",testview);
    }];
    [testview removeFromSuperview];
}

由于testview是一個局部變量刹勃,當我們把testView從添加到VC的view中時,VC的view會對testview產(chǎn)生一個強引用嚎尤,當我們移除以后強引用會移除荔仁,當viewDidLoad執(zhí)行完畢以后,應(yīng)該是沒有強引用了,但是testview的dealloc方法并沒有被調(diào)用乏梁,原因是UIViewController強引用了一個閉包testBlock次洼,閉包中會執(zhí)行輸出testview的操作,所以對于testview也進行了強引用掌呜,使得testview不能被銷毀滓玖,只能在UIViewcontroller銷毀->持有的閉包銷毀->testview強引用變成0才會真正被銷毀

三、閉包的循環(huán)引用

我們修改viewDidload方法质蕉,在閉包中不僅輸出testView,也輸出VC自身的view翩肌。重寫UIViewController的dealloc方法

- (void)viewDidLoad {
    [super viewDidLoad]; 
    TestView* testview = [[TestView alloc] init];
    [self.view addSubview:testview];
    [self setTestBlock:^{
        NSLog(@"%@",testview);
        NSLog(@"%@",self.view);//輸出自己的view
    }];
    [testview removeFromSuperview];
}
- (void)dealloc{
  NSLog(@"controller 被銷毀");
}

我們新建一個新的UIViewcontroller和一個UINavigationController,設(shè)置navigation的rootViewController為新建的UIViewcontroller模暗,然后push之前的viewController,最后再pop返回
這時候我們可以發(fā)現(xiàn)念祭,現(xiàn)在testView還是沒有被釋放兑宇,而且被pop的UIViewcontroller也沒有被釋放,此時被pop的vc和testview已經(jīng)變成了內(nèi)存泄露
原因則是UIViewController強引用了一個閉包粱坤,在閉包中使用了self.view隶糕,那么閉包會對self也就是UIViewController也產(chǎn)生了一個強引用,這樣導(dǎo)致了UIViewController和testBlock都不能被釋放站玄,閉包強引用的testeview也不能被釋放了枚驻,造成了內(nèi)存泄露


強引用關(guān)系

我們可以消除testBlock對于UIViewController的強引用來消除循環(huán)引用,在閉包中需要使用對象而不需要持有株旷,我們可以使用該對象的弱引用再登。在本例中testBlock不必要持有UIViewController,因為當UIViewController被銷毀的時候晾剖,testBlock此后也不會被執(zhí)行了

- (void)viewDidLoad {
    [super viewDidLoad];
    
    TestView* view = [[TestView alloc] init];
    [self.view addSubview:view];
    typeof(self) weakSelf = self;
    [self setTestBlock:^{
        NSLog(@"%@",view);
        NSLog(@"%@",weakSelf.view);//使用弱引用調(diào)用方法或?qū)傩?    }];
    [view removeFromSuperview];
注:
不要想著使用_view的方式對對象的屬性進行直接訪問來消除對self的強引用锉矢。不建議的原因有兩點:
1.直接調(diào)用變量代碼層面真心不好看。
2.使用“_變量名”的方式并沒有消除強引用齿尽,在調(diào)用的時還是會對self進行一次間接的強引用

四沽损、閉包的判空

方法doSomething需要在執(zhí)行操作完執(zhí)行完成的閉包,例如封裝http請求循头,請求完成后需要執(zhí)行的操作

- (void)doSomething:(void(^)())finished{
  NSLog(@"I am doing something...");
  finished();
}

正常方法調(diào)用:

[self doSomething:^{
  NSLog(@"finished");
}];

如果我們在調(diào)用doSomething的時候不想傳入任務(wù)完成的閉包可以:

[self doSomething:nil];

實際上上面的代碼會發(fā)生EXC_BAD_ACCESS崩潰绵估,原因是傳入的閉包為空。調(diào)用閉包和調(diào)用oc對象的方法并不同贷岸,oc對象調(diào)用方法是通過消息發(fā)送的形式壹士,當oc對象為nil則不對消息做出反應(yīng),但是當閉包為空的時候并不能進行調(diào)用偿警。我們可以修改方法doSomething躏救,對閉包進行判空操作:

- (void)doSomething:(void(^)())finished{
    NSLog(@"I am doing something...");
    if(finished){
        finished();
    }
}

當然,如果閉包會調(diào)用多次,則會多次判空盒使,if的過多使用會導(dǎo)致代碼的不緊湊崩掘,如果你和我一樣是代碼完美主義者,可以使用下面的方法進行判空:

- (void)doSomething:(void(^)())finished{
    NSLog(@"I am doing something...");
    !finished?:finished();
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末少办,一起剝皮案震驚了整個濱河市苞慢,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌英妓,老刑警劉巖挽放,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蔓纠,居然都是意外死亡辑畦,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門腿倚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來纯出,“玉大人,你說我怎么就攤上這事敷燎≡蒹荩” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵硬贯,是天一觀的道長焕襟。 經(jīng)常有香客問我,道長澄成,這世上最難降的妖魔是什么胧洒? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮墨状,結(jié)果婚禮上卫漫,老公的妹妹穿的比我還像新娘。我一直安慰自己肾砂,他們只是感情好列赎,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著镐确,像睡著了一般包吝。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上源葫,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天诗越,我揣著相機與錄音,去河邊找鬼息堂。 笑死嚷狞,一個胖子當著我的面吹牛块促,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播床未,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼竭翠,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了薇搁?” 一聲冷哼從身側(cè)響起斋扰,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎啃洋,沒想到半個月后传货,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡宏娄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年损离,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绝编。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖貌踏,靈堂內(nèi)的尸體忽然破棺而出十饥,到底是詐尸還是另有隱情,我是刑警寧澤祖乳,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布逗堵,位于F島的核電站,受9級特大地震影響眷昆,放射性物質(zhì)發(fā)生泄漏蜒秤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一亚斋、第九天 我趴在偏房一處隱蔽的房頂上張望作媚。 院中可真熱鬧,春花似錦帅刊、人聲如沸纸泡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽女揭。三九已至,卻和暖如春栏饮,著一層夾襖步出監(jiān)牢的瞬間吧兔,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工袍嬉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留境蔼,地道東北人。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像欧穴,于是被迫代替她去往敵國和親民逼。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

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