本文略為全面的介紹block的使用:
block定義方式,
block傳值,
block循環(huán)引用,
block內(nèi)存管理,
block做參數(shù),
block做返回值(實(shí)現(xiàn)鏈?zhǔn)骄幊?等等,
此篇在手,block我有!
Action!
-
block的三種定義方式以及block類型
1.沒(méi)有返回值,沒(méi)有參數(shù)的定義方式
//返回值類型(^block的名字)(參數(shù)類型) = ^(參數(shù)類型和參數(shù)名) {};
void(^block)() = ^(){
NSLog(@"調(diào)用了block");
};
//當(dāng)然,沒(méi)有參數(shù)的時(shí)候可以把括號(hào)省去
void(^block)() = ^{
NSLog(@"調(diào)用了block");
};
2.有返回值,有參數(shù)的定義方式
//返回值類型(^block的名字)(參數(shù)類型) = ^(參數(shù)類型和參數(shù)名) {};
//如果有參數(shù),定義的時(shí)候,必須要寫參數(shù),而且必須要有參數(shù)變量名
int(^block)(int) = ^(int a){
return 1;
};
3.定義時(shí)帶有返回類型的,(不常用)
int(^block)() = ^int{//這里的int就是這個(gè)block的返回值類型
return 1;
};
4.系統(tǒng)提供了一個(gè)定義block的宏
// block快捷方式 輸入:inline
// returnType(^blockName)(parameterTypes) = ^(parameters) {
// statements
// };
5.block的調(diào)用
//定義block
void(^block)() = ^{
NSLog(@"調(diào)用了block");
};
//調(diào)用block
block();
6.block的類型
//block有自己的類型,就想@"string"是NSString類型一樣
//格式就是 返回值(^)(參數(shù)類型)
//比如這個(gè)block的類型就是: int(^)(int)
int(^block)(int) = ^(int a){
return 1;
};
//這個(gè)block的類型就是void(^)()
void(^block)() = ^{
NSLog(@"調(diào)用了block");
};
//在ARC中把block定義成屬性要用strong類型,定義方式如下:
@property (nonatomic, strong) void(^block)();//這樣在類中可以拿到self.block
//當(dāng)然也可以取別名:
typedef void(^BlockType)();//BlockType不是變量名,而是這種類型的block的別名
//然后就可以這樣
@property (nonatomic, strong) BlockType block;
-
使用block保存代碼
這種方式我在app的"用戶設(shè)置"界面中使用到,需求大約是這樣:
用戶設(shè)置界面每一行cell會(huì)做不同的事,有的是跳轉(zhuǎn)界面,有的是switch開關(guān),有的是需要顯示一下AlertView.
這樣的話,我們可以把需要執(zhí)行的代碼包裝成block,把block放在cell的模型里面,當(dāng)點(diǎn)擊cell的時(shí)候,拿出模型中的block來(lái)執(zhí)行,感覺(jué)還不錯(cuò).
//這是cell的模型
typedef void(^optionBlock)();
@interface SettingItem : NSObject
@property(nonatomic,copy)NSString *icon;//圖標(biāo)
@property(nonatomic,copy)NSString *title;//文字
@property(nonatomic,assign) Class destVc;//需要跳轉(zhuǎn)的界面
@property(nonatomic,copy) optionBlock option;//需要執(zhí)行的代碼
@end
然后我們可以在didSelectRowAtIndexPath里面這么做:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
if (item.option != nil) {//判斷block是否為空 不為空就執(zhí)行
item.option();
}else if ([item isKindOfClass:[SettingArrowItem class]]) {//block為空 執(zhí)行其他操作(比如界面跳轉(zhuǎn)):
SettingArrowItem *newItem = (SettingArrowItem *)item;
UIViewController *vc = [[newItem.destVc alloc]init];
vc.title = item.title;
[self.navigationController pushViewController:vc animated:YES];
}
}
-
使用block進(jìn)行傳值
在學(xué)習(xí)block之前,一直在使用代理傳值,但是用了block以后,再也不想用代理了.
下面介紹block傳值(逆?zhèn)?簡(jiǎn)單使用
需求如下:
在ViewControllerOne跳到ViewControllerTwo
ViewControllerTwo拿到數(shù)據(jù)后dismiss
在ViewControllerOne打印數(shù)據(jù)
//ViewControllerOne.m:
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{//點(diǎn)擊控制器View的時(shí)候調(diào)用
ModalViewController *modalVc = [[ModalViewController alloc] init];
modalVc.block = ^(NSString *value) {
NSLog(@"%@",value);
};//給他block賦值
[self presentViewController:modalVc animated:YES completion:nil];
}
//ViewControllerTwo.h:
@interface ModalViewController : UIViewController
@property (nonatomic, strong) void(^block)(NSString *value);
@end
//ViewControllerTwo.m:
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
// 傳值給ViewControllerOne
if (_block) {
_block(@"123");
}
[self dismissViewControllerAnimated:YES completion:nil];
}
-
block變量的傳遞
一個(gè)很簡(jiǎn)單的問(wèn)題:block使用外部變量,是值傳遞,還是指針傳遞
1.值傳遞
//block為值傳遞只有一種情況:
int a = 3;
void(^block)() = ^{
NSLog(@"%d",a);
};
a = 5;
block();//這里調(diào)用block打印出的是3,是值傳遞
結(jié)論:// 如果block訪問(wèn)的變量是局部變量,那么變量是值傳遞
2.指針傳遞
static int a = 3;
void(^block)() = ^{
NSLog(@"%d",a);
};
a = 5;
block();//這里調(diào)用block打印出的是5,是指針傳遞
//另外全局變量,靜態(tài)變量都是指針傳遞
結(jié)論:如果是靜態(tài)變量,那么變量是指針傳遞
3.經(jīng)過(guò)其他測(cè)試總結(jié):
(1)如果是局部變量,Block是值傳遞
(2)如果是靜態(tài)變量,全局變量,__block修飾的變量,block都是指針傳遞
-
block做參數(shù)
第一次見到block當(dāng)做參數(shù)是在AFN框架中,AFN幫你拿到數(shù)據(jù)以后,執(zhí)行你傳給他的block:
//這一個(gè)個(gè)對(duì)AFN進(jìn)行簡(jiǎn)單封裝的方法:
+(void)requestWihtMethod:(RequestMethodType)methodType
success:(void (^)(id response))success//是一個(gè)block
failure:(void (^)(NSError* err))failure//是一個(gè)block
{
AFHTTPSessionManager *manage = [AFHTTPSessionManager manager];
[manage GET:url parameters:params progress:^(NSProgress * _Nonnull downloadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
if (success) {
success(responseObject);//拿到數(shù)據(jù)后執(zhí)行你傳入的block
}
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
failure(error);//拿到數(shù)據(jù)后執(zhí)行你傳入的block
}];
}
}
接下來(lái)我們自定義一個(gè)計(jì)算器,在block里面自定義計(jì)算方式,將block傳入計(jì)算器來(lái)進(jìn)行計(jì)算:
//CalculatorManager.h:
@interface CacultorManager : NSObject
@property (nonatomic, assign) NSInteger result;//要計(jì)算的數(shù)據(jù)
-(void)calculatorWithMethod:(NSInteger(^)(NSInteger // 計(jì)算方法parameterresult))methodBlock;
@end
//CalculatorManager.m:
-(void)cacultor:(NSInteger (^)())cacultorBlock
{
if (cacultorBlock) {//判斷block是否為空
_result = cacultorBlock(_result);//執(zhí)行block中的計(jì)算方法
}
}
//ViewController.m:使用計(jì)算器
-(void)viewDidLoad {
[super viewDidLoad];
// 創(chuàng)建計(jì)算器管理者
CalculatorManager *mgr = [[CalculatorManager alloc] init];
[mgr calculator:^(NSInteger result){//自定義計(jì)算方法block,作為參數(shù)傳進(jìn)去
result += 5;
result += 6;
result *= 2;
return result;
}];
NSLog(@"%ld",mgr.result);
-
block做方法返回值(鏈?zhǔn)骄幊?
我們平時(shí)寫一些工具類的方法的時(shí)候(比如計(jì)算器)
//注:result是CalculatorManager的屬性,用來(lái)保存計(jì)算結(jié)果
//如果計(jì)算方法這么寫
-(int)add:(int)value{
_result += value;
return result;
}
//就要這么調(diào)用:
CalculatorManager *mgr = [[CalculatorManager alloc] init];
[mgr add:[mgr add:[mgr add:[mgr add:[mgr add:[mgr add:5]]]]]];//返回值是數(shù)字,要繼續(xù)用mar調(diào)用add來(lái)執(zhí)行操作
//如果計(jì)算方法這么寫:
-(CalculatorManager *)add:(int)value
{
_result += value;
return self;
}
//就要這么調(diào)用:
CalculatorManager *mgr = [[CalculatorManager alloc] init];
[[[[[mgr add:5] add:5] add:5] add:6] add:7];//返回值是計(jì)算器本身,可以繼續(xù)調(diào)用add方法
現(xiàn)在我們要用返回值是block的方法來(lái)實(shí)現(xiàn)鏈?zhǔn)骄幊?
//計(jì)算方法這么寫,返回值是 返回值為CalculatorManager的block.
-(CalculatorManager *(^)(int))add//相當(dāng)于一個(gè)get方法
{
return ^(int value){
_result += value;
return self;
};
}
//就可以這么調(diào)用
CalculatorManager *mgr = [[CalculatorManager alloc] init];
mgr.add(5).add(5).add(5).add(5);
下面簡(jiǎn)單介紹一下調(diào)用原理:
mgr.add相當(dāng)于get方法的調(diào)用: [mgr add];
mgr.add返回的是一個(gè)block,所以你可以給他一個(gè)參數(shù)5,于是寫成這樣:mgr.add(5)
然后block返回的又是CalculatorManager,所以繼續(xù)調(diào)用add
如此循環(huán)下去.......
點(diǎn)這里是我用 block 做返回值寫的一個(gè)屏幕適配小工具
-
block內(nèi)存管理
首先,在oc中block是一個(gè)對(duì)象,只有對(duì)象才涉及到內(nèi)存管理
block的內(nèi)存管理在MRC和ARC中有不同的地方,接下來(lái)將分別介紹
1.MRC:
block存放位置:
int a = 3;//這是一個(gè)局部變量
void(^block)() = ^{
NSLog(@"調(diào)用block%d",a);
};
NSLog(@"%@",block);
//打印結(jié)果:<__NSStackBlock__: 0x7fc498746000>
//此時(shí)引用了外部局部變量,block放在棧里面
static int b = 2;
-(void)viewDidLoad {
[super viewDidLoad];
void(^block)() = ^{
NSLog(@"調(diào)用block%d",b);
};
NSLog(@"%@",block);
}
//打印結(jié)果:<__NSGloBalBlock__: 0x7fc498746000>
//此時(shí)引用了全局變量,block放在全局區(qū)
MRC:管理block總結(jié):
只要block沒(méi)有引用外部局部變量,block放在全局區(qū)
只要Block引用外部局部變量,block放在棧里面.
定義屬性時(shí):
@property (nonatomic, copy) void(^block)();
block只能使用copy,不能使用retain,
因?yàn)槭褂胷etain,block還是在棧里面 代碼塊過(guò)了方block就銷毀了,再次訪問(wèn)self.block會(huì)出現(xiàn)壞內(nèi)存訪問(wèn)
使用copy是放在堆里面,代碼塊過(guò)了不會(huì)銷毀
2.ARC:
block存放位置:
int a = 3;//這還是一個(gè)局部變量
void(^block)() = ^{
NSLog(@"調(diào)用block%d",a);
};
NSLog(@"%@",block);
//打印結(jié)果:<__NSMallocBlock__: 0x7fc498746000>
//此時(shí)引用了外部局部變量,block放在堆里面
static int b = 2;
-(void)viewDidLoad {
[super viewDidLoad];
void(^block)() = ^{
NSLog(@"調(diào)用block%d",b);
};
NSLog(@"%@",block);
}
//打印結(jié)果:<__NSGloBalBlock__: 0x7fc498746000>
//此時(shí)引用了全局變量,block放在全局區(qū)
ARC:管理block總結(jié):
只要block沒(méi)有引用外部局部變量,block放在全局區(qū)
只要block引用外部局部變量,block放在堆里面.
定義屬性時(shí):
@property (nonatomic, strong) void(^block)();
block只能使用strong,不要使用copy
因?yàn)楫?dāng)使用copy的時(shí)候,set方法是調(diào)用了copy幫你深拷貝一次,沒(méi)有這個(gè)必要.
就像NSString一樣,他一般都是@"a"這種常量,沒(méi)必要再去深拷貝一次,所以NSString常量也用strong不用copy.
-
block循環(huán)引用
1. block的循環(huán)引用問(wèn)題
1.為什么會(huì)產(chǎn)生循環(huán)引用:
因?yàn)閎lock會(huì)給內(nèi)部的強(qiáng)指針對(duì)象進(jìn)行一次強(qiáng)引用,比如常見的傳入block中的self進(jìn)行強(qiáng)引用
并且在self中,block又是strong的,self對(duì)block是強(qiáng)引用
所以,你強(qiáng)引用我,我強(qiáng)引用你,誰(shuí)也不會(huì)被釋放,就造成了循環(huán)引用
所以,為了避免循環(huán)引用,我們要在block使用self之前,進(jìn)行這一步操作:
__weak typeof(self) weakSelf = self;
在block中使用weakSelf,就不會(huì)產(chǎn)生循環(huán)引用問(wèn)題了.
使用了__weak修飾符的對(duì)象,作用等同于定義為weak的property。自然不會(huì)導(dǎo)致循環(huán)引用問(wèn)題.
2.接下來(lái)是雙層block的循環(huán)引用問(wèn)題:
先來(lái)看這樣一個(gè)例子:
__weak typeof(self) weakSelf = self;
block = ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{//延時(shí)2秒執(zhí)行下面的代碼
NSLog(@"%@",weakSelf);
});
};
block();
這樣執(zhí)行下去,如果在延時(shí)時(shí)間2秒還沒(méi)到,控制器就dismiss或者pop(總之是銷毀)了,打印出的weakSelf是null,
為什么呢?
因?yàn)橹挥衟ush self或者present self的控制器對(duì)self強(qiáng)引用,當(dāng)self dismiss了或者pop了,就沒(méi)有人對(duì)self強(qiáng)引用了(block對(duì)self沒(méi)有強(qiáng)引用),根據(jù)ARC的內(nèi)存管理原則,當(dāng)沒(méi)有人對(duì)一個(gè)對(duì)象強(qiáng)引用的時(shí)候,該對(duì)象就會(huì)銷毀.
所以,當(dāng)self dismiss了或者pop了,self就銷毀了,2秒后block再訪問(wèn)self的時(shí)候,self已經(jīng)不再了.
這時(shí),我們要做如下處理:
__weak typeof(self) weakSelf = self;
block = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;//這個(gè)是局部變量 棧內(nèi)的強(qiáng)指針 當(dāng)這個(gè)block執(zhí)行完畢 這個(gè)指針就會(huì)釋放
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{//延時(shí)2秒執(zhí)行下面的代碼
NSLog(@"%@",strongSelf);
});
};
block();
加上這一句:__strong typeof(weakSelf) strongSelf = weakSelf;
__strong 就相當(dāng)于定義為strong的property
那么當(dāng)self銷毀的之前(pop或者dismiss之前),有兩個(gè)人對(duì)self強(qiáng)引用(一個(gè)是push self或者present self的控制器,一個(gè)是這個(gè)block中定義的strongSelf).
當(dāng)控制器銷毀的時(shí)候(pop或者dismiss時(shí)),push self或者present self的控制器不在強(qiáng)引用self,self失去一個(gè)強(qiáng)引用,但是self不會(huì)銷毀,因?yàn)閎lock中定義的strongSelf還在對(duì)self強(qiáng)引用.
但是你會(huì)問(wèn),那這么不會(huì)造成循環(huán)引用嗎?不著急,繼續(xù)往下看:
當(dāng)延時(shí)2秒到了,block可以訪問(wèn)到strongSelf
當(dāng)延時(shí)block代碼塊過(guò)了,strongSelf就會(huì)指向nil了(因?yàn)閟trongSelf是局部變量,存在棧內(nèi)的強(qiáng)指針 當(dāng)這個(gè)block執(zhí)行完畢,這個(gè)指針就會(huì)釋放)
此時(shí)就沒(méi)有人對(duì)self進(jìn)行強(qiáng)引用了,self也會(huì)銷毀,
至此,大家都銷毀了..
感謝閱讀
你的支持是我寫作的唯一動(dòng)力