Block

本文深入解析Block在Objective-C中的定义、使用及内存管理,包括Block的基本语法、类型声明、调用方式,以及在不同场景下的应用,如保存代码块、代理传值、变量传递等。

版权声明:本文为延成原创文章,转载请标明出处

  • 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 时,程序会崩溃。

  1. 一般情况下你不需要自行调用copy或则retain一个block。只有当你需要在block定义域以外的地方使用时才需要copy,Copy将block从内存栈区移到堆区。
  2. 其实block使用copy是MRC留下来的也算是一个传统吧,在MRC下,如上述在方法中的block创建在栈区,使用copy就能把他放到堆区,这样在作用域外调用该block程序就不会崩溃。
  3. 但在ARC下,使用copy与strong其实都一样,因为block的retain就是用copy来实现的。
2、Copy将block从内存栈区移到堆区,这里移的是原来的block,还是新生成的block对象?

拷贝栈区的block所在的内存,然后在堆区重新开辟一块放新生成的block

3、__block什么时候用?
  1. 当需要在block中修改外部变量值时使用
  2. 外部变量避免被block强引用一次,避免循环引用
4、在block里面堆数组执行添加操作,这个数组需要声明为__block吗?

不需要,因为数组可以理解为指针,在block里面堆数组进行添加,删除操作,只是改变了指针指向的值,而没有修改外部数组的地址。

5、在block里面对NSInteger进行修改,需要对NSInteger声明为__block?

需要,NSInteger是基本数据数据类型,基本数据类型没有static等的保护下,需要__block。

### Block在编程中的含义和用法 #### 区块链上下文中的Block 在区块链技术领域,`block`通常指代构成区块链的基本单元。每个区块包含了若干交易记录和其他元数据。具体来说,在智能合约环境中获取当前区块信息可以通过定义一个名为 `get_block_info()` 的函数实现: ```solidity contract BlockInfo { function getBlockInfo() public view returns (uint, uint, uint, address) { return ( block.number, block.timestamp, block.difficulty, block.coinbase ); } } ``` 上述代码展示了如何在一个 Solidity 合约中创建一个可以查询当前区块详情的方法[^1]。 #### NumPy 中的block 方法 对于 Python 编程语言而言,特别是在处理数值运算时经常使用的库——NumPy 提供了一个叫做 `block` 的方法。此方法主要用于构建由其他数组组成的大型数组结构。下面是一个简单的例子来说明其基本用途: ```python import numpy as np arr = np.block([ [np.array([1, 2]), np.array([3])], [np.eye(2), [[4]]] ]) print(arr) ``` 这段脚本会输出如下矩阵: ``` [[1. 2. 3.] [0. 1. 4.]] ``` 这里利用了 `block` 函数将几个不同的子阵列拼接在一起形成一个新的二维数组[^2]。 #### Linux 网络编程中的非阻塞连接尝试 当涉及到操作系统层面的操作时,“block”也可能指的是程序执行过程中是否会暂停等待某个事件的发生。例如在网络通信场景下建立TCP连接的过程中可以选择采用非阻塞模式来进行快速失败检测: ```c int connect_nonblock(const char *lpIP, int nPort){ int err = -1; socklen_t len = sizeof(err); // ...省略部分初始化套接字并设置为非阻塞模式的代码... if(connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr))<0){ if(errno==EINPROGRESS){ printf("Non-blocking connection attempt started.\n"); return 0; /* 连接正在尝试 */ }else{ perror("connect error"); close(sockfd); return -1; } } // 成功立即返回或进一步处理已完成的连接... } ``` 在这个 C 语言片段里,通过调用 `connect()` 并配合特定错误码判断实现了异步发起网络请求的功能[^3]。 综上所述,尽管都涉及到了“block”,但在不同语境和技术栈之间其实有着截然不同的意义与应用场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值