Block
一、基本概念:
Block是一种C语言的数据类型,指向结构体的指针,平常我们将Block当作一个代码段使用,相当于函数,Block相比于函数的优点是 Block可以当做一个参数传递而函数/方法不能.。我们可以用Block写一些函数语句,可以使用多线程。Block也可以作为一个回调,不仅包含回调期间的代码,也包含了执行期间需要的数据。类似于"闭包",闭包的定义是可以c从函数外部访问函数内部的变量。
二、基本用法:
1)无参无返回值(最简单形式)
定义格式:void (^block名)() = ^{代码块;};
使用格式:block名();
2)无参有返回值(以返回值为int为例)
定义格式:int (^block名)() = ^{代码块;};
使用格式:int result = block名();
3)有参无返回值(以参数为int为例)
定义格式:void (^block名)(int) = ^(int a){代码块;};
使用格式: block名(6);
4)有参有返回值(以参数、返回值为int为例;可以有多个参数)
定义格式:int (^block名)(int) = ^(int a){代码块;};
使用格式:int result = block名(6);
5)利用typedef定义block类型(和指向函数的指针很像)
定义格式:typedef 返回值类型(^新别名)(参数类型列表);
例:typedef int (^myBlock)(int,int);
三、Block访问外部变量
1)在block内部可以访问block外部的变量,但是,这是一个新的内存空间变量;
block内部也可以定义和block外部同名的变量,此时这个局部变量会屏蔽外部变量的作用域。
2)如果想要在block内部修改外部的局部变量,需要给该变量加上__block关键字;原因是加__block,block会引用常量变量的地址,则可以任意修改该变量指向的内容。如果不加__block直接在block内部修改变量,会编译报错,block内部变量是只读的。
四、Block使用技巧和注意事项
1)block的快速提示,输入inlineBlock 会有代码提示;
2)block变量用作方法的参数时,最好把参数类型列表部分加上具体的参数名
3)利用block遍历取值
例: [dict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL *_Nonnull stop) {
}];
5) block修饰:block的发展也经过了MRC和ARC两个阶段;
MRC情况下:用copy修饰后,如果要在block内部使用对象,则需要进行__block typeof(Target) blockTarget = Target处理,在block里面用blockTarget 操作。
ARC情况下:如果用copy修饰,该block就会存储在堆空间以避免在使用时发生释放。但会对block的内部对象进行强引用,导致循环引用,内存无法释放。 所以block使用self ,要使用 self 弱引用写法. 即__weaktypeof(self) weakSelf =self;如果用weak修饰block,该block就会存放在栈空间,虽不会出现循环引用,但容易造成需要使用对象时,对象已经不存在的问题,原因是存储在栈空间的各种数据是系统自动处理的,并不需要程序猿手动处理,所以我们也不知道对象什么时候会被释放。
6)循环引用
分析循环(VC代表viewController控制器,op代表一个操作)
已知 : op --> self(VC) 强引用
如果 self 强引用的对象,对 op 也有强用.那么就会有循环引用
self(VC) --> queue/OPCache --> op --> self(VC)
所以block使用 self ,要使用 self 弱引用写法. 即__weak typeof(self) weakSelf = self;
GCD中的block(任务)出现self,会造成循环引用吗? ----- GCD中的block可以直接使用self
block循环引用的条件: block------->强应用(self) self ------->强引用(block属性)
NSOperation 中的blcok中是否可以出现 self ?
一般情况下 NSOperationQueue 需要作为一个属性. 将操作添加到操作队列中!
注意: 1.确实会出现循环引用!
但是: 操作一旦执行完毕之后,就会被自动销毁! 所以 NSOperation 中可以出现 self.
7)block可以在两个对象之间进行传值,类似于代理和通知;
以POST请求为例,假如我在A对象中封装了POST请求的方法,该方法请求回来的数据想传递给B对象,就可以利用block实现:
代码示例:
"A对象中声明方法
//MARK:- 一句话封装POST请求
- (void)POSTRequestWithURLString:(NSString *)urlString andDict:(NSDictionary *)pramarts andSuccessBlock:(successBlock)successblock andFalseBlock:(falseBlock)falseblock ;
"实现方法
- (void)POSTRequestWithURLString:(NSString *)urlString andDict:(NSDictionary *)pramarts andSuccessBlock:(successBlock)successblock andFalseBlock:(falseBlock)falseblock {
//MARK:- 创建请求
NSURL *url = [NSURL URLWithString:urlString];
#warning - POST请求要创建可变请求
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:0 timeoutInterval:15];
//MARK:- 设置请求体、请求方法等参数
request.HTTPMethod = @"POST";
NSMutableString *strM = [NSMutableString string];
[pramarts enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL *_Nonnull stop) {
NSString *pramKey = key;
NSString *pramValue = obj;
[strM appendFormat:@"%@=%@&",pramKey,pramValue];
}];
NSString *pramart = [strM substringToIndex:strM.length-1];
request.HTTPBody = [pramart dataUsingEncoding:NSUTF8StringEncoding];
//MARK:- 发送请求
[[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *_Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (data&&!error) {
if (successblock!=nil) {//一定要先判断block是否为空
successblock( obj,response);//回调
}
}else{
if (falseblock!=nil) {//同上
falseblock(error);
}
}
}]resume];
}
"B对象调用方法
[[YQPOSTRequest sharedPOSTRequest] POSTRequestWithURLString:urlStr andDict:pram andSuccessBlock:^(NSData *data, NSURLResponse *response) {
NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
} andFalseBlock:^(NSError *error) {
NSLog(@"%@",error);
}];