前言:
最近遇到了一個block的循環(huán)引用的問題,才發(fā)現(xiàn)我對block還一知半解,為此,對自己的理解做了一下正整理。
目錄:
1、什么是block ?有什么作用?
2、block為什么使用copy修飾?
3、block為什么會造成循環(huán)引用?怎么解決?
4、block中基本數(shù)據(jù)類型,數(shù)值無法更改。
1、什么是block?有什么作用?
代碼塊Block是蘋果在iOS4開始引入的對C語言的擴展,用來實現(xiàn)匿名函數(shù)的特性,Block是一種特殊的數(shù)據(jù)類型,其可以正常定義變量、作為參數(shù)、作為返回值,特殊地,Block還可以保存一段代碼,在需要的時候調(diào)用,目前Block已經(jīng)廣泛應用于iOS開發(fā)中,常用于GCD、動畫、排序及各類回調(diào)。存儲的數(shù)據(jù)是一個函數(shù)體。使用Block,就可以像其他標準函數(shù)一樣,傳入?yún)?shù),并得到返回值。
block的格式:
a:Block的返回值類型,可以為空(void);
b:Block對象名稱,可以理解為變量名;
^:塊的語法標記,聲明b為一個Block對象;
c:第一個參數(shù)類型
d:第二個參數(shù)類型
name1,name2:參數(shù)名;
{}:Block代碼塊的主題部分。
block的聲明:
例子:控制器A,控制器B,在A中點擊button push到B,pop時從B傳值到A。block為B中的屬性和方法。
方式一:
typedef void(^testBlock)(void); // 定義類型,取別名
@property (nonatomic, copy) testBlock Block; // 再聲明屬性
實現(xiàn):
B中:
_testBlock(@"1");
A中:
b.testBlock = ^(NSString *text) {
nslog(@"%@",text);
};
方式二:
@property (nonatomic, copy) void(^test1Block)(NSString *text); // 直接聲明屬性
實現(xiàn):
B中:
_test1Block(@"1");
A中:
b.test1Block = ^(NSString *text) {
nslog(@"%@",text);
};
方式三:
-(void)testWithBlock:(void(^)(NSString *text))block; // 作為函數(shù)參數(shù)
實現(xiàn):
B中:
- (void)testWithBlock:(void (^)(NSString *))block{
block(@"1");
}
A中:
[a testWithBlock:^(NSString *text) {
nslog(@"%@",text);
}];
2、block為什么使用copy修飾?
a、block在創(chuàng)建的時候默認分配的內(nèi)存是在棧上,而不是在堆上。這樣的話其本身的作用域是屬于創(chuàng)建時候的作用域,一旦在創(chuàng)建的作用域之外調(diào)用就會導致程序的崩潰。所以使用了copy將其拷貝到堆內(nèi)存上。
b、block創(chuàng)建在棧上,而block的代碼中可能會用到本地的一些變量,只有將其拷貝到堆上,才能用這些變量
3、block為什么會造成循環(huán)引用?怎么解決?
self 與 block 相互持有,形成保留環(huán),無法釋放,導致循環(huán)引用內(nèi)存泄露
例子一:
self.b.test1Block = ^(NSString *text) {
nslog(@"%@",text);
[self.test1_btn setTitle:text forState:UIControlStateNormal];
};
情況:未執(zhí)行dealloc 內(nèi)存泄露 self->b->block->self
解決辦法:打破retain cycle
__weak typeof(self) weakSelf = self;
[weakSelf.test1_btn setTitle:text forState:UIControlStateNormal];
例子二:
LHQTest2ViewController *b = [[LHQTest2ViewController alloc]init];
[self.navigationController pushViewController:b animated:YES];
b.test2Block = ^(NSString *text) {
[self.test1_btn setTitle:text forState:UIControlStateNormal];
};
情況:執(zhí)行dealloc 不泄露 b->block->self
例子三:
b = [[LHQTest2ViewController alloc]init]; //test2為局部變量
[self.navigationController pushViewController:test2 animated:YES];
b.test2Block = ^(NSString *text) {
[self.test2_btn setTitle:text forState:UIControlStateNormal];
};
情況:未執(zhí)行dealloc 內(nèi)存泄露 self->b->block->self
解決辦法:與例子一一樣
例子四:
self.b.test2Block = ^(NSString *text) {
_msg = text; // _msg未局部變量
};
情況:未執(zhí)行dealloc 內(nèi)存泄露 self->b->block->self
解決辦法:打破retain cycle
__weak typeof(self) weakSelf = self;
self.b.test2Block = ^(NSString *text) {
__strong typeof(weakSelf) strongSelf = weakSelf;
// strongSelf和原來的self并沒有直接關系,因為strongSelf是通過weakSelf得來的,而weakSelf又沒有強引用原來的sel
strongSelf->_msg = text;
};
4、block中基本數(shù)據(jù)類型,數(shù)值無法更改。
對于基本數(shù)據(jù)類型,進入到block中會被當做常量處理
//如果需要在block中對num進行修改,需要加上關鍵字__block
//(我們也可以用static關鍵字進行修飾)
__block int num = 10; // 使進入到block塊中的變量不被當做常量來使用
b.test2Block = ^(NSString *text) {
num += 1;
NSLog(@"num is %d",num);
};