iOS中block的使用详解

block的声明和简单使用

  • 苹果官方文档声明,block是objc对象。
block的定义方式
  • 无返回值无参数的block
void(^block1)() = ^{
    NSLog(@"调用了block1");
};
  • 无返回值有参数的block,参数的类型和传入参数的值不能省略,即int a不能省略
void(^block2)(int) = ^(int a){
       NSLog(@"调用了block2");     
};
  • 有返回值的block,返回值的类型可以省略,要是有参数,参数的类型和传入参数的值不能省略。
int(^block3)() = ^int{
    return 3;
};

block定义总结

总结:block的定义,不管block有没有返回值,block的实现部分都可以省略;若block有参数,则参数类型和传入参数的值不能省略。

  • 小tip:若初学者觉得block格式奇怪,记不住,可以输入inline就可以出现block,如图:
Paste_Image.png

block使用场景之----代理传值

在开发中控制器逆传传值最常用的就是用代理的方法,然而用block也可以实现逆传传值,而且更加简单方便。

  • 场景:假如在窗口的根控制器ViewController中点击屏幕的view,模态弹出一个窗口ModalViewController,再点击ModalViewController的view销毁ModalViewController,同时将想要传的值传给ViewController。

  • 在ViewController中的- touchesBegan方法中,进行到ModalViewController的跳转,同时给ModalViewController的block属性声明,注意!这里并没有调用

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    ModalViewController *modalVc = [[ModalViewController alloc] init];
    modalVc.view.backgroundColor = [UIColor brownColor];
    modalVc.block = ^(NSString *value) {
      
        NSLog(@"%@",value);
    };
    
    // 跳转
    [self presentViewController:modalVc animated:YES completion:nil];
}
  • 在ModalViewController的.h文件中声明一个block属性,这个block属性没有返回值,接收一个字符串类型的参数
@property (nonatomic, strong) void(^block)(NSString *value);
  • 在ModalViewController的.m文件中,点击控制器的view进行block的调用,此时就会将字符串中的值传给ViewController。注意if语句的判断,否则block不存在的时候,会报坏内存访问错误
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    if (_block) {
        _block(@"我是萌萌哒ModalViewController");
    }
    [self dismissViewControllerAnimated:YES completion:nil];
}
Paste_Image.png
block的内存管理
  • block在MRC环境和ARC环境的内存管理不同

MRC

  • 首先,在项目的building settings里面将环境改成MRC的环境
Paste_Image.png
  • block会根据其内部引用的变量的类型不同而在不同的存储空间
  • block可以存在全局区,栈区,堆区(__NSGlobalBlock____NSStackBlock__,__NSMallocBlock__)。
    • 若block中引用外部的局部变量,则打印结果是__NSStackBlock__,说明block存储在栈区
- (void)viewDidLoad {
    [super viewDidLoad];
    //局部变量a
    int a = 3;
    void(^block)() = ^{
        NSLog(@"调用block %d",a);
    };
    NSLog(@"%@",block);
}
Paste_Image.png
  • 若block中的变量的全局变量后者静态变量,则打印结果是__NSGlobalBlock__,说明block存储在全局区
//全局变量a
int a = 3;

- (void)viewDidLoad {
    [super viewDidLoad];
    void(^block)() = ^{
        NSLog(@"调用block %d",a);
    };
    NSLog(@"%@",block);
}

- (void)viewDidLoad {
    [super viewDidLoad];
    //静态变量a
    static int a = 3;
    void(^block)() = ^{
        NSLog(@"调用block %d",a);
    };
    NSLog(@"%@",block);
}
Paste_Image.png
  • 那么block什么时候保存在堆区呢?
  • 当block属性被copy修饰的时候放在堆区,打印结果为__NSMallocBlock __
//定义copy修饰的block属性
@property (nonatomic, copy) void(^block)();
- (void)viewDidLoad {
    [super viewDidLoad];
    int a = 3;
    void(^block)() = ^{
        NSLog(@"调用block%d",a);
    };
    self.block = block;
    NSLog(@"%@",self.block);
}
  • 假如我们尝试用retain修饰block,会报这样的警告,同时运行的结果是保存在栈区。
Paste_Image.png
Paste_Image.png
总结
只要block没有引用外部局部变量,block放在全局区
只要Block引用外部局部变量,block放在栈区.
block只能使用copy,不能使用retain,使用retain,block还是在栈区

ARC

  • 若block中的变量的全局变量后者静态变量,则打印结果仍然是__NSGlobalBlock__,block仍然存储在全局区
  • 若block中引用外部的局部变量,则打印结果是__NSMallocBlock__,说明block存储在堆区,和MRC情况不同。
  • 对于ARC下的block属性用strong和copy修饰都是一样的,但是copy的底层会让setter方法多一层对可变不可变属性的判断,浪费资源,所以在ARC的情况下用strong修饰block,同理NSString属性修饰也用strong。
block的内存管理总结
 总结:block里面引用全局变量或者静态变量,放在全局区__NSGlobalBlock__
 
 *  MRC情况:
 *  block里面引用局部变量,存放在栈区__NSStackBlock__
 *  MRC中修饰block只能用copy,不能用retain,因为retain修饰block还在栈中,若block是局部变量,不能包住block的命,再次访问会造成坏内存访问。
 
 *  ARC情况:
 *  block里面引用局部变量,存放在堆区__NSMallocBlock__
 *  block在ARC情况下用strong修饰
 *  对于NSString和block在ARC尽量用strong,因为copy底层的setter方法会对新对象做一次copy操作,还要判断是不是不可变对象,浪费性能
 */
block的循环引用
  • block会对内部的所有的强指针变量都强引用一次,这就会造成block的循环引用,造成内存泄露。下面看例子:

在ViewController中以modal的方式展示ModalViewController,在ModalViewController中调用dismiss方法销毁控制器。则在dealloc方法中会打印ModalViewController被销毁。

然而若定义一个block属性,并在block的实现中做如下操作:

@property (nonatomic, strong) void(^block)();
_block = ^{
    NSLog(@"%@",self);
};

此时当调用ModalViewController的dismiss方法的时候不会调用dealloc方法中的打印语句,说明ModalViewController没有被真正的销毁。因为ModalViewController强引用一个block属性,block会对内部的强指针self进行一次强引用。所以造成循环引用

  • 改进方法是将self改成弱指针,这样就不会造成循环引用
__weak typeof(self) weakSelf = self;
_block = ^{
    NSLog(@"%@",weakSelf);   
};

block的变量传递

  • 请看这样一段代码,大家猜一猜打印的结果是3还是5?
/***************  1  **************/
- (void)viewDidLoad {
    [super viewDidLoad];    
    //普通局部变量a
    int a = 3;
    void(^block)() = ^{
        NSLog(@"%d",a);  
    };
    a = 5;
    block();
}

如果这样呢?

/***************  2  **************/
- (void)viewDidLoad {
    [super viewDidLoad];    
    //静态变量a
    static int a = 3;
    void(^block)() = ^{
        NSLog(@"%d",a);  
    };
    a = 5;
    block();
}

这样呢?

/***************  3  **************/
//全局变量a
int a = 3;
- (void)viewDidLoad {
    [super viewDidLoad];    
    void(^block)() = ^{
        NSLog(@"%d",a);  
    };
    a = 5;
    block();
}

这样呢?

/***************  4  **************/
- (void)viewDidLoad {
    [super viewDidLoad];    
    //用__block修饰的局部变量a
    __block int a = 3;
    void(^block)() = ^{
        NSLog(@"%d",a);  
    };
    a = 5;
    block();
}

实际编译可以得出结果,除了1的打印结果是3,剩下的都是5。那么可以总结出结论如下:

* 如果block中是普通的局部变量,是值传递,即在局部变量声明的那一刻就把局部变量的值放到block中了,之后在修改不会造成影响
* 如果是静态变量,全局变量,__block修饰的变量,block都是指针传递。指针传递的值可以修改。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • 《Objective-C高级编程》这本书就讲了三个东西:自动引用计数、block、GCD,偏向于从原理上对这些内容...
    WeiHing阅读 9,732评论 10 69
  • 前言 Blocks是C语言的扩充功能,而Apple 在OS X Snow Leopard 和 iOS 4中引入了这...
    小人不才阅读 3,729评论 0 23
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,036评论 29 470
  • 目录 Block概述 Block定义方式 Block保存代码 Block传值 Block对外部变量的传递 Bloc...
    子斌阅读 1,360评论 2 7
  • __block和__weak修饰符的区别其实是挺明显的:1.__block不管是ARC还是MRC模式下都可以使用,...
    LZM轮回阅读 3,204评论 0 6