一、什么是block?
按照蘋果官方文檔的說法把敞,block是一個(gè)oc對(duì)象。(原文:Blocks are Objective-C objects, which means they can be added to collections like NSArray or NSDictionary. )其實(shí)术唬,本質(zhì)就是一個(gè)函數(shù)域醇。
當(dāng)然,block也可以理解成oc的一種數(shù)據(jù)類型弧械,我個(gè)人認(rèn)為把block理解成一種數(shù)據(jù)類型更便于我們理解和記憶八酒。下面我就從這個(gè)角度詳細(xì)介紹block。
二刃唐、block的介紹
我們?cè)诙x一個(gè)變量的時(shí)候羞迷,都包含了以下三個(gè)步驟:
1界轩、確定這個(gè)變量的數(shù)據(jù)類型
2、聲明變量
3衔瓮、定義變量
// 1.比如確定這個(gè)數(shù)據(jù)是 int 類型
int value 浊猾; // 2.聲明
value = 15 ; // 3.定義
// 當(dāng)然热鞍,也可以聲明的同時(shí)進(jìn)行定義
int value = 15 葫慎;
從代碼中認(rèn)識(shí)block
例如:
// 1.一個(gè)有參數(shù)有返回值的block類型:int (^) (int ,int)
int (^block1) (int ,int); // 2.block的聲明
block1 = ^int(int n1,int n2){ // 3.block的定義
int sum = n1 + n2;
return sum;
}
block1(10,20); // 4.block的調(diào)用
// 當(dāng)然,也可以聲明的同時(shí)進(jìn)行定義
void(^block2)(int) = ^(int a){
NSLog(@"你輸入的值是:%d",a );
};
block2(999);
// 我們可以看出block2的類型是:void(^)(int)
1.block的類型
格式:返回值類型(^)(block參數(shù)類型)
block的類型大致可以分為四種:
a: 無參數(shù)薇宠,無返回值
b: 無參數(shù)偷办,有返回值
c: 有參數(shù),無返回值
d: 有參數(shù)澄港,有返回值
2.block的聲明
格式:**返回值類型(^Block變量名)(block參數(shù)類型 參數(shù)變量名) **
int (^block11)(int a,int b);
int (^block22)(int,int); // 注意:參數(shù)變量名可以省略
3.block的定義
格式:^返回值類型(參數(shù)類型 參數(shù)變量名) { // 代碼塊 } ;
// 注意1:聲明的時(shí)候參數(shù)的變量名可以省略爽篷,當(dāng)定義是參數(shù)的變量名一定不能省略
void(^block111)(int) = ^void(int a) {
};
int(^block222)(int) = ^int(int a) {
return 2;
};
// 注意2:返回值的類型是可以省略的
void(^block111)(int a) = ^(int a) {
};
int(^block222)(int a) = ^(int a) {
return 2;
};
// 注意3: 當(dāng)沒有返回值,沒有參數(shù)時(shí),可以省略括號(hào)
void(^block333)() = ^{
};
4.block的調(diào)用
格式:block變量名(參數(shù));
5.如何聲明一個(gè)block屬性
方式一:
@interface ViewController ()
@property (nonatomic, strong) void(^block)(); // 方式一
@end
方式二:
typedef void(^BlockType)(); // BlockType:block類型別名
@interface ViewController ()
@property (nonatomic, assign) BlockType block1; // 方式二
@end
三、block的內(nèi)存管理
我們都知道內(nèi)存是可以劃分成堆,棧,方法區(qū),常量區(qū),全局區(qū)5個(gè)區(qū)慢睡。而block是可能被存放在以下三個(gè)區(qū)中的任意一個(gè)中逐工。
1.在全局靜態(tài)區(qū):_NSConcreteGlobalBlock
2.在棧中:_NSConcreteStackBlock
3.在堆中:_NSConcreteMallocBlock
MRC中:
1.如果block沒有訪問外部的局部變量或者局部變量被static修飾,block默認(rèn)存放在"全局區(qū)"
2.如果block訪問外部的局部變量,block存放在"棧"里面
MRC:不能使用retain聲明block,依然放在棧里面,會(huì)自動(dòng)銷毀.
MRC:使用copy聲明block,才會(huì)放在堆里面.
ARC如何管理Block:
1.如果block沒有訪問外部的局部變量或者局部變量被static修飾,block默認(rèn)存放在"全局區(qū)"
2.如果block訪問外部的局部變量,block存放在"堆"里面
ARC:使用strong聲明block,不要使用weak.
四、block的循環(huán)引用問題
------------“你強(qiáng)我就強(qiáng)漂辐,你弱我就弱”------------
在block的內(nèi)部訪問了一個(gè)對(duì)象:
1.如果這個(gè)對(duì)象本來就有強(qiáng)指針指著泪喊,那么這個(gè)block也會(huì)強(qiáng)引用這個(gè)對(duì)象;
2.如果這個(gè)對(duì)象只有弱指針引著髓涯,那么這個(gè)block就不會(huì)對(duì)這個(gè)對(duì)象進(jìn)行強(qiáng)引用袒啼。
Person類:
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property(nonatomic,strong)NSString *name; // 姓名
@property(nonatomic,assign)int age; // 年齡
@property(nonatomic,assign)int height; // 體重
// 聲明block
@property(nonatomic,strong) void(^block)();
- (void)eat;
@end
@implementation Person
- (void)eat
{
self.height += 1 ;
_block = ^{
// 在block的內(nèi)部訪問了自身這個(gè)對(duì)象。
NSLog(@"吃完飯后的體重是:%d",self.height);
};
_block();
}
@end
Person類外部使用:
// 這時(shí)候纬纪,p1這個(gè)強(qiáng)指針引著Person對(duì)象蚓再。
Person *p1 = [[Person alloc] init];
// eat內(nèi)部調(diào)用block時(shí),block就會(huì)強(qiáng)引用這person對(duì)象包各;
// 而本身person對(duì)象內(nèi)部就強(qiáng)引著block摘仅。
[p1 eat];
// person對(duì)象和block兩者互相強(qiáng)引著,誰也不能釋放问畅。
解決方案:
- (void)eat
{
self.height += 1 ;
//聲明weakSelf弱指針(解決方案)
__weak typeof(self) weakSelf = self;
_block = ^{
NSLog(@"吃完飯后的體重是:%d",weakSelf.height);
};
_block();
}
更為復(fù)雜的情況:?? 延遲操作或者異步任務(wù) ??
- (void)viewDidLoad
// 聲明weakSelf弱指針
__weak typeof(self) weakSelf = self;
_block = ^{
//因?yàn)檫@里如果不用強(qiáng)指針的話娃属,延時(shí)后,可能訪問的這個(gè)對(duì)象就會(huì)銷毀了护姆,那么這個(gè)block的內(nèi)部就沒辦法訪問這個(gè)對(duì)象了
//這樣用強(qiáng)指針引著矾端,就保證了只要block還在,那么這個(gè)對(duì)象就不會(huì)銷毀
__strong typeof(weakSelf) strongSelf = weakSelf;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",strongSelf);
});
};
_block();
}