版权声明:本文为延成原创文章,转载请标明出处
-
block是一种数据类型
-
block 快速生成模板代码的书写方式 inlineBlock
-
block的定义格式
返回值 (^block变量名)(参数) = ^(参数){};
-
block的作用
保存一段代码 -
block基本使用
//1.block声明 返回值(^block变量名)(参数)
void(^block)();
//给block起别名的声明方式
typedef void (^BlockOne)(void);
typedef void (^BlockTwo)(int value);
typedef NSString* _Nullable(^BlockThree)(void);
typedef NSString* _Nullable (^BlockFour)(int value);
@property(nonatomic,copy)BlockOne blockOne;
@property(nonatomic,copy)BlockTwo blockTow;
@property(nonatomic,copy)BlockThree blockThree;
@property(nonatomic,copy)BlockFour blockFour;
//2.block定义 方式一
void(^block1)() = ^(){
NSLog(@"%s",__FUNCTION__);
}
//block定义 方式二 如果没有参数,参数可以隐藏,如果有参数,定义的时候,必须要写参数,而且必须要有参数变量名
void(^block2)() = ^{
NSLog(@"%s",__FUNCTION__);
};
//block定义 方式三 block返回可以省略,不管有没有返回值,都可以省略
int(^block3)() = ^int{
NSLog(@"%s",__FUNCTION__);
return 3;
};
//3.block类型:int(^)(NSString *)
int(^block4)(NSString *) = ^(NSString *name){
return 2;
};
//4.block调用
block1();
// block快捷方式 inline
// <#returnType#>(^<#blockName#>)(<#parameterTypes#>) = ^(<#parameters#>) {
// <#statements#>
// };
- 开发使用场景
- 保存代码
@interface CellItem : NSObject
@property (nonatomic, strong) NSString *title;
// 保存每个cell做的事情
@property (nonatomic, strong) void(^block)();
+ (instancetype)itemWithTitle:(NSString *)title;
@end
// 创建模型
CellItem *item1 = [CellItem itemWithTitle:@"打电话"];
item1.block = ^{
NSLog(@"打电话");
};
CellItem *item2 = [CellItem itemWithTitle:@"发短信"];
item2.block = ^{
NSLog(@"发短信");
};
CellItem *item3 = [CellItem itemWithTitle:@"发邮件"];
item3.block = ^{
NSLog(@"发邮件");
};
// 点击cell就会调用
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// 把要做的事情(代码)保存到模型
CellItem *item = self.items[indexPath.row];
if (item.block) {
item.block();
}
}
- 代理传值
//ModalViewController.h文件
@interface ModalViewController : UIViewController
@property (nonatomic, strong) void(^block)(NSString *value);
@end
//ModalViewController.m文件
if (_block) {
_block(@"123");
}
//ViewController.m文件
ModalViewController *modalVc = [[ModalViewController alloc] init];
modalVc.block = ^(NSString *value) {
NSLog(@"%@",value);
};
// 跳转
[self presentViewController:modalVc animated:YES completion:nil];
-
内存管理MRC
总结:只要block没有引用外部局部变量,block放在全局区 只要block引用外部局部变量,block放在栈里面 block只要使用copy,不能使用retain,使用retain,block还是在栈里面 -
内存管理ARC
总结:只要block没有引用外部局部变量,block放在全局区 只要block引用外部局部变量,block放在堆里面 block使用strong,最好不要使用copy(使用copy会判断是浅copy还是深copy进行赋值,降低性能) -
循环引用
#import "ModalViewController.h"
@interface ModalViewController ()
@property (nonatomic, strong) void(^block)();
@end
@implementation ModalViewController
- (void)dealloc
{
NSLog(@"ModalViewController销毁");
}
- (void)viewDidLoad {
[super viewDidLoad];
// block造成循环利用:Block会对里面所有强指针变量都强引用一次
__weak typeof(self) weakSelf = self;
_block = ^{
__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();
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
// 如果控制器被dismiss就会被销毁
[self dismissViewControllerAnimated:YES completion:nil];
}
- 变量传递
__block int a = 3;
// 如果是局部变量,Block是值传递
// 如果是静态变量,全局变量,__block修饰的变量,block都是指针传递
void(^block)() = ^{
NSLog(@"%d",a);
};
a = 5;
block();
- 参数使用
#import <Foundation/Foundation.h>
@interface CacultorManager : NSObject
@property (nonatomic, assign) NSInteger result;
// 计算
- (void)cacultor:(NSInteger(^)(NSInteger result))cacultorBlock;
@end
#import "CacultorManager.h"
@implementation CacultorManager
- (void)cacultor:(NSInteger (^)(NSInteger))cacultorBlock
{
if (cacultorBlock) {
_result = cacultorBlock(_result);
}
}
@end
- (void)viewDidLoad {
[super viewDidLoad];
// 创建计算器管理者
CacultorManager *mgr = [[CacultorManager alloc] init];
[mgr cacultor:^(NSInteger result){
result += 5;
result += 6;
result *= 2;
return result;
}];
NSLog(@"%ld",mgr.result);
}
- 返回值
#import <Foundation/Foundation.h>
@interface CalculatorManager : NSObject
@property (nonatomic, assign) int result;
- (CalculatorManager *(^)(int))add;
@end
#import "CalculatorManager.h"
@implementation CalculatorManager
- (CalculatorManager *(^)(int))add
{
return ^(int value){
_result += value;
return self;
};
}
@end
- (void)viewDidLoad {
[super viewDidLoad];
CalculatorManager *mgr = [[CalculatorManager alloc] init];
mgr.add(5).add(5).add(5).add(5);
NSLog(@"%d",mgr.result);
self.test();
}
- (void(^)())test
{
return ^{
NSLog(@"调用了block");
};
}
注意:
- 利用typedef给block起别名,和指向函数的指针一样,block变量的名称就是别名
- block中可以定义和外界同名的变量,在block中访问的是block中的变量
- 默认情况下,不可以在block中修改外界变量的值
- 如果想在block中修改外界变量的值,必须在外界变量前加上__block,因为没加__block是值传递,加上__block是地址传递
- 如果在block中访问了外界的对象,一定要给对象加上__block,只要加上了__block,哪怕block在堆中,也不会对外界对象进行retain
1、block为什么用copy修饰
block在创建的时候它的内存是默认是分配栈(stack)上,而不是堆(heap)上的。所以它的作用域仅限创建时候的当前上下文(函数,方法…),当你在该作用域外使用block 时,程序会崩溃。
- 一般情况下你不需要自行调用copy或则retain一个block。只有当你需要在block定义域以外的地方使用时才需要copy,Copy将block从内存栈区移到堆区。
- 其实block使用copy是MRC留下来的也算是一个传统吧,在MRC下,如上述在方法中的block创建在栈区,使用copy就能把他放到堆区,这样在作用域外调用该block程序就不会崩溃。
- 但在ARC下,使用copy与strong其实都一样,因为block的retain就是用copy来实现的。
2、Copy将block从内存栈区移到堆区,这里移的是原来的block,还是新生成的block对象?
拷贝栈区的block所在的内存,然后在堆区重新开辟一块放新生成的block
3、__block什么时候用?
- 当需要在block中修改外部变量值时使用
- 外部变量避免被block强引用一次,避免循环引用
4、在block里面堆数组执行添加操作,这个数组需要声明为__block吗?
不需要,因为数组可以理解为指针,在block里面堆数组进行添加,删除操作,只是改变了指针指向的值,而没有修改外部数组的地址。
5、在block里面对NSInteger进行修改,需要对NSInteger声明为__block?
需要,NSInteger是基本数据数据类型,基本数据类型没有static等的保护下,需要__block。
本文深入解析Block在Objective-C中的定义、使用及内存管理,包括Block的基本语法、类型声明、调用方式,以及在不同场景下的应用,如保存代码块、代理传值、变量传递等。
478

被折叠的 条评论
为什么被折叠?



