iOS——Block回调

先跟着我实现最简单的 Block 回调传参的使用,如果你能举一反三,基本上可以满足了 OC 中的开发需求。已经实现的同学可以跳到下一节。

首先解释一下我们例子要实现什么功能(其实是烂大街又最形象的例子):

有两个视图控制器 A 和 B,现在点击 A 上的按钮跳转到视图 B ,并在 B 中的textfield 输入字符串,点击 B 中的跳转按钮跳转回 A ,并将之前输入的字符串

显示在 A 中的 label 上。也就是说 A 视图中需要回调 B 视图中的数据。
Pasted image 20230623170202.png

Pasted image 20230623170242.png

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AjEpWjSQ-1690945668053)(https://raw.githubusercontent.com/ArnoVD97/PhotoBed/master/photo202307281507212.png)]

block回调传值是一个从后面向前传递值的一个过程,这个即这个块应该定义在B页面内,重命名一个块,你会的到两个函数,一个是块函数,一个是回调函数,我们的网络请求用的就是block回调传值在网络请求中的manager就使用的是回调函数充当B页面,而需要调用数据的页面使用块,可以获得回调函数中的数据,

    BVC.callBackData(NSString  _Nonnull data#);
//回调函数(传值函数)用用于传入回调数据

    BVC.callBackData = ^(NSString  _Nonnull data)
    //普通的块,他会调用回调函数来获取里面的数据

//BViewController.h
#import <UIKit/UIKit.h>

typedef void(^CallBackBlcok) (NSString *text);//1

@interface BViewController : UIViewController

@property (nonatomic,copy)CallBackBlcok callBackData;//2
@end

在这里,代码 1 用 typedef 定义了 void(^) (NSString *text)的别名为 CallBackBlcok 。这样我们就可以在代码 2 中,使用这个别名定义一个 Block 类型的变量 callBackBlock

在定义了 callBackBlock 之后,我们可以在 B 中的点击事件中添加 callBackBlock 的传参操作:


//BViewController.m

- (IBAction)click:(id)sender {
 self.callBackBlock(_textField.text); //1
 [self.navigationController popToRootViewControllerAnimated:YES];
}

这样我们就可以在想要获取数据回调的地方,也就 A 的视图中调用 block:


// AViewController.m
- (IBAction)push:(id)sender {
 BViewController *bVC = [self.storyboard instantiateViewControllerWithIdentifier:@"BViewController"];

 bVC.callBackBlock = ^(NSString *text){ // 1

  NSLog(@"text is %@",text);

  self.label.text = text;

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

代码 1 中,通过对回调将 B 中的数据传递到代码块中,并赋值给 A中的 label,实现了整个回调过程。

上例是通过将 block 直接赋值给 block 属性,也可以通过方法参数的方式传递 block 块。

Block 的疑惑

到目前为止,一切看起来都很美好(如果你照着上面的例子做的话),功能正常, A 视图中也获取到数据了。但是某些人可能就要说了,你的代码有问题,你的思路有问题,你这是误人子弟。

是的,代码的确还有问题,第一个问题就是循环引用的问题,在 A 视图的block 代码块中:


bVC.callBackBlock = ^(NSString *text){
  NSLog(@"text is %@",text);  
  self.label.text = text;  
 };

代码 self.label.text = text; ,在 Block 中引用 self ,也就是 A ,而 A 创建并引用了 B ,而 B 引用 callBackBlock,此时就形成了一个循环引用,而编译器也不会报任何错误,我们需要非常小心这个问题(面试百分百问到我会乱说?)。此时我们通常的解决方法是使用弱引用来解除这个循环:


 __weak AViewController *weakSelf = self;
 bVC.callBackBlock = ^(NSString *text){ 
  NSLog(@"text is %@",text); 
//  self.label.text = text; 
  weakSelf.label.text = text;
 };

第二个问题是我自己对 Block 的理解不到位,我们都知道 Block 能截取自动变量,并且是不能在 Block 块中进行修改的(除非用__block修饰符),但是很明显 weakSelf.label.text的值被修改了,并且没有用__block修饰符, 这是为什么呢?因为 label 是个全局变量,而如果像如下的局部变量 a 是不能修改的,编译器也会报错:


局部变量

通过这个小例子发现的两个问题,也算是值得了。

为什么能实现回调

回顾一下我们在 B 中所实现的代码,不外乎定义了一个 Block 变量,并在适当的时候传入参数,那么为什么在调用了 self.callBackBlock(_textField.text) 之后,值就神奇传到了 A 中的 Block 块了呢?
通过简单的整理我们可以发现完整的回调流程应该是这样的:
Pasted image 20230623172613.png

当将一个 Block 代码块赋值给 bVC.callBackBlock 后,callBackBlock 的指针就指向了这个 Block 代码块。之后,可以通过调用 callBackBlock(NSString *text) 来执行这个 Block 代码块,并实现回调功能。

// 定义一个 Block 代码块
void (^myBlock)(NSString *text) = ^(NSString *text) {
    // 执行一些代码
    NSLog(@"%@", text);
};

// 将 Block 代码块赋值给 callBackBlock
bVC.callBackBlock = myBlock;

// 调用 callBackBlock,执行回调
if (bVC.callBackBlock) {
    bVC.callBackBlock(@"Hello, World!");
}

我按照我的理解说一下这个流程,如果没有回调函数那么这个block代码块不会被运行
只有运行到B页面的时候,使用了回调函数,才会执行block代码块里面的内容,回调函数的作用是调用这个匿名函数,并且传回一个值给我们使用的block代码块

// 声明一个带有回调参数的方法
- (void)performOperationWithCompletion:(void (^)(NSString *result))completion {
    // 执行一些操作
    NSString *result = @"Operation completed";
    
    // 调用回调Block并传递结果
    if (completion) {
        completion(result);
    }
}

// 调用带有回调参数的方法
[self performOperationWithCompletion:^(NSString *result) {
    NSLog(@"Result: %@", result);
}];

如果像这样使用方法来调用的话,在方法实现需要要使用回调函数,然后回跳转到调用该方法的地方,继续执行block块里面的的内容。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回答: 在iOS开发中,可以使用Block和delegate来实现一对多的回调。使用delegate时,首先需要定义一个delegate协议,其中包含回调方法。然后在需要进行回调的地方,设置delegate并调用回调方法。具体实现可以参考以下代码示例:\[2\] ``` // 定义delegate协议 protocol FirstCellDelegate: class { func firstCellBtnTap(_ cell: firstTableViewCell) } // 在需要进行回调的地方设置delegate并调用回调方法 weak var delegate: FirstCellDelegate? // 调用回调方法 if let delegate = self.delegate { delegate.firstCellBtnTap(self) } // 实现delegate回调方法 func firstCellBtnTap(_ cell: firstTableViewCell) { let cellIndexPath = tableView.indexPath(for: cell) print("delegate回调 section:\(cellIndexPath?.section ?? 0), row:\(cellIndexPath?.row ?? 0)") } ``` 另外,使用Block也可以实现一对多的回调Block可以作为函数表达式传递给API,可以选择性地存储,并且可以被多个线程使用。Block不仅包含了在回调时需要执行的代码,还包含了执行期间所需的数据。具体实现可以参考以下代码示例:\[3\] ``` // 定义Block回调 typealias CallbackBlock = () -> Void // 在需要进行回调的地方使用Block进行回调 var callback: CallbackBlock? // 调用回调 callback?() ``` 通过使用Block和delegate,可以实现一对多的回调,满足不同场景下的需求。 #### 引用[.reference_title] - *1* [Block实现iOS回调](https://blog.csdn.net/feelinghappy/article/details/119870367)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [iOS Block与Delegate的用法,各自优缺点及使用场景](https://blog.csdn.net/huangshanfeng/article/details/82106580)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值