objective-c 各种 block

本文详细介绍了Objective-C中Block的各种用法,包括局部函数、全局闭包、作为属性和函数参数的使用,以及在使用过程中需要注意的内存泄漏问题和如何捕获数据。特别强调了循环引用的产生及解决方案,提醒开发者在使用Block时要注意防止内存泄漏。
摘要由CSDN通过智能技术生成

Objective-C 各类 block

OC 里block 很常见,记录一下常见的用法。

原理 参考另外一篇blog

用法

常见形式


int (^firstBlock)(NSString *, int);
 
void (^showName)(NSString *);
 
NSDate *(^whatDayIsIt)(void);
 
void (^allVoid)(void);
 
NSString *(^composeName)(NSString *, NSString *);

返回值 (^名称)(参数)

示例

局部函数
    int(^sum)(int ) = ^(int a){
        NSLog(@"%d",a);
        return a;
    };
    
    sum(1);
    void(^logC)(void) = ^(){
        NSLog(@"没得参数");
    };
    logC();
全局闭包
#import <UIKit/UIKit.h>
typedef void(^GlobleBlock)(void); // 声明

@interface ViewController : UIViewController

@end
// 实现
GlobleBlock block = ^(){ 
        NSLog(@"全局的 block");
};
// 引用
block(); 
作为属性
#import <UIKit/UIKit.h>
typedef void(^GlobleBlock)(void);

@interface ViewController : UIViewController

@property (nonatomic, copy) GlobleBlock block;//属性

@end
self.block = ^{
   NSLog(@"这是个全局的 block");
};
self.block();
作为函数参数

这个也是我们常说的尾随闭包。一般在函数执行完毕后调用 block,完成函数执行完毕之后想要的操作。

常见如 网络请求之后的操作、 页面刷新之后通知等

- (void)functionWithBlock:(NSString *)name complete:(void(^)(NSString *sss))handle {
    
    NSLog(@"函数执行完毕");
    handle(name);
}

- (void)functionWithBlockNo:(NSString *)name complete:(void(^)(void))handle {
    
    handle();
}

    [self functionWithBlockNo:@"name" complete:^{
        NSLog(@"block 实现");
    }];
    
    [self functionWithBlock:@"str" complete:^(NSString *sss) {
        NSLog(@"函数传递的参数 %@",sss);
    }];

注意事项

该注意的就两个相互引用导致内存泄漏和闭包捕获数据

内存泄漏

强调一点,并不是所有的 block 都会造成循环引用

例1:
- (void)btnAction{

    GlobleBlock block = ^(){
        CCViewController *ssVC = [[CCViewController alloc]init];

        [self.navigationController pushViewController:ssVC animated:YES];
    };

    block();
}
BlockKinds[50750:1209368] <CCViewController: 0x7fea9740a950> dealloc
BlockKinds[50750:1209368] <SSViewController: 0x7fea975087a0> dealloc

点击按钮通过 block 跳转 VC,并不会造成循环引用

例2:
- (void)btnAction{
    [self functionWithBlock:@"block" complete:^(NSString *sss) {
        CCViewController *ssVC = [[CCViewController alloc]init];
        [self.navigationController pushViewController:ssVC animated:YES];
    }];
}
- (void)functionWithBlock:(NSString *)name complete:(void(^)(NSString *sss))handle {
    NSLog(@"函数执行完毕");
    handle(name);
}

<CCViewController: 0x7fd9e0e12650> dealloc
<SSViewController: 0x7fd9e3103ef0> dealloc

可见也没有发生内存泄漏

原理需要开一篇新的博客说明一下了, 关于 block 是不是导致内存泄漏可以根据 block 和 block 里面的对象有没有相互持有

上面的例子中 block 都是作为局部变量,当函数执行完毕之后 block 被释放掉了。 block 都没了,自然不会循环引用

例3:
typedef void(^GlobleBlock)(void);

@interface SSViewController : UIViewController
@property (nonatomic, copy) GlobleBlock block;

@end
- (void)viewDidLoad {
    //__weak typeof(self) weakself = self;
     
    self.block = ^{

        CCViewController *ssVC = [[CCViewController alloc]init];
        [self.navigationController pushViewController:ssVC animated:YES];
        //[weakself.navigationController pushViewController:ssVC animated:YES];
    };
}
- (void)btnAction{
         self.block();
}
<CCViewController: 0x7fd9e0e12650> dealloc

block作为 SSViewController 的属性被持有。然后 block 的实现里面又持有SSViewController。

典型的互相引用 造成内存泄漏,SSViewController无法释放。此时编译器也会给出提示

Capturing 'self' strongly in this block is likely to lead to a retain cycle

**解决办法

__weak typeof(self) weakself = self;

使用 weakself 替代 self,将互相持有的一方打断。既可避免内存泄漏

同理打断 block 的持有一样能解决循环引用问题

- (void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    self.block = nil;
}
<CCViewController: 0x7fbbef7177b0> dealloc
<SSViewController: 0x7fbbef701e80> dealloc

viewWillDisappear 中将 block 置为 nil,即可打断循环引用

捕获数据

block 使用外部局部变量是,Xcode 会给出提示 Variable is not assignable (missing __block type specifier)

    __block int num = 0;
    
        int(^sum)(int ) = ^(int a){
            num ++;
            return a +num;
        };

局部变量需要__block 修饰

属性变量虽然不需要修饰,但是使用时要留心属性在其他地方的变化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值