`

(转)[IOS]iOS 简单易懂的 Block 回调使用和解析

    博客分类:
  • IOS
阅读更多

转自:https://www.jianshu.com/p/7d32ed28292f

 

 

前言

老实说在早前我已经学会了如何使用 Block 来做一些方法回调,传递参数的功能,并且用 Block 简单封装了第三方的网络库(AFNetworking)。虽说对 Block 的应用说不上得心应手,但是却是极其地喜欢使用这种设计模式,并且在项目中也大量地使用了。

但是,最近一位即将参加面试的学弟问我,什么是 Block 呢?我蒙圈了,但是毕竟是学长,我假装淡定地反问道:你所理解的 Block 是什么呢?学弟说:是一段封装的代码块,并可以放在任意位置使用,还可以传递数据。我心里暗喜,这孩子还是图样了,于是语重心长地说:Block 的本质是可以截取自动变量的匿名函数。但是说出这句话我就后悔了,这句话他喵的到底是个什么意思?看着学弟满意地走了之后,我就疯狂地上网查资料,万一下次这个熊孩子深究起来可不就破坏了我英明神武的形象了,但是并没有很满意的答案,大多是照文档描述了 Block 的定义以及基本用法,不然就是高深地去探讨 Block 底层的实现机制,显然这些都不适合让一个初学者既能学会使用又能够没有疑惑地使用。

本文主要讲的是 Block 回调的使用,以及 Block 是如何实现这种神奇的回调两部分来讲的。

Block 回调实现

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

首先解释一下我们例子要实现什么功能(其实是烂大街又最形象的例子):
有两个视图控制器 A 和 B,现在点击 A 上的按钮跳转到视图 B ,并在 B 中的textfield 输入字符串,点击 B 中的跳转按钮跳转回 A ,并将之前输入的字符串
显示在 A 中的 label 上。也就是说 A 视图中需要回调 B 视图中的数据。

想不明白的同学可以看一看最终实现的效果图:

 
block example

这里不再对 Block 的语法做说明了,不了解的同学可以点传送门

首先,我们需要定义两个试图控制器 AViewController 和 BViewController,现在我们需要思考一下,Block 应该在哪里定义呢?

我们可以简单地这样思考,需要回调数据的是 A 视图,那么 Block 就应该在 B 中定义,用于获取传入回调数据。

因此我们在 BViewController.h 中定义如下:

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

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

@interface BViewController : UIViewController

@property (nonatomic,copy)CallBackBlcok callBackBlock;//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 块。

完整的示例代码放在 git.oschina.net 上,代码地址:BlockMagic

关于 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 是不能修改的,编译器也会报错:

 
局部变量

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

Block 为什么能实现神奇的回调

在这里我不会说什么实现原理,仅仅是个人对 Block 能实现神奇回调的理解,有错误的地方请大家指出。

在先前使用 Block 的过程中,虽然会使用,但是总是有一个疑惑,简单说来就是:

为什么在 A 中的 block 块能调用到 B 中的数据?

回顾一下我们在 B 中所实现的代码,不外乎定义了一个 Block 变量,并在适当的时候传入参数,那么为什么在调用了 self.callBackBlock(_textField.text) 之后,值就神奇传到了 A 中的 Block 块了呢?

通过整理使用的过程,我发现是我们的思维陷入了误区(可能是我个人),我们认为在 B 中传入 _textField.text 参数之后, A 中的 Block 块就可以获取到值。虽然思路是对的,但其实是不完整,导致我们形成了回调的数据是通过某种底层实现传递过去的错觉,这就使得我们认为这不需要深究。

事实是,通过简单的整理我们可以发现完整的回调流程应该是这样的:

 
回调流程
  1. block 代码块赋值给 bVC.callBackBlock,此时 callBackBlock 的指针就指向这个代码块。
  2. 调用 callBackBlock(NSString *text)
  3. 由于 callBackBlock 的指针是指向 A 中的 block 代码块,因此执行代码块的代码,实现回调。

很显然之前我忽略了代码块赋值给 callBackBlock 的这个操作(羞愧)。

现在再通过一段代码可以更清晰地理解这个原理:

 bVC.callBackBlock = ^(NSString *text){ //1
        NSLog(@"text is %@",text);
    };
  bVC.callBackBlock = ^(NSString *text){ //2
        NSLog(@"text b is %@",text);
    }; 

上述代码中,我们对 callBackBlock进行了两次赋值,结果会怎么样呢?

 
two block

可以看出来,Block 的回调只对代码 2 生效,因为callBackBlock的指针最后指向了代码 2 的代码块。所以并没有什么神奇的魔法,也没什么隐藏的底层机制(这里指的是方便理解的底层)让你可以带着疑惑去使用它。

总结

我这个人学习方法,总结起来就是看到新技术,先在自己的代码里跑一遍,能跑通,并且使用起来没有什么难度,就基本不会深究了(如果遇到某个熊孩子就坑了)。但是自我反思过,这样的学习方法是很不对的,写代码不能不求甚解,如果想要有所突破,不想局限于码农,一定要深入探究一下实现的机制,最起码要保证不带着疑惑去使用。




 
分享到:
评论

相关推荐

    IOS block回调代码实例Demo

    总之,"IOS block回调代码实例Demo"是一个很好的学习资源,它展示了如何在iOS应用中使用Block进行回调,这对于理解和掌握iOS开发中的异步编程至关重要。通过深入研究这个Demo,开发者可以更好地运用Block来优化代码...

    iOS的block回调

    Block的使用尤其在处理回调、异步操作和事件响应时非常常见。下面我们将深入探讨iOS中的Block回调及其应用。 首先,我们来理解什么是Block。Block本质上是Objective-C的对象,它可以捕获和存储其所在上下文的局部...

    ios-block 回调.zip

    在iOS开发中,Block是一种强大的代码组织和回调机制,它允许我们把代码块作为一个对象来传递,这在处理异步操作、事件响应或者简化复杂的逻辑时特别有用。"ios-block 回调.zip"中的"CallbackDemo"很可能是展示了一个...

    ios-block回调 仅仅是回调.zip

    在iOS开发中,Block是一...总之,"ios-block回调 仅仅是回调.zip"这个资源包可能涵盖了Block在各种回调场景下的实践,通过学习BlockReturnTest的实现,你可以更深入地理解和掌握Block的使用技巧,提升你的iOS开发能力。

    iOS中Block的回调使用和解析详解

    本篇文章将深入讲解如何在iOS中使用Block进行回调,并通过一个具体的例子来阐述其工作原理和常见问题。 在iOS开发中,我们经常会遇到这样一个场景:从一个界面(如AViewController)跳转到另一个界面(如...

    ios-Scrollview 滚动选择菜单block回调.zip

    本示例"ios-Scrollview 滚动选择菜单block回调.zip"着重展示了如何利用`UIScrollView`来创建一个滚动选择菜单,并且实现了一个基于Block的回调机制。这种回调机制在用户完成选择后,可以将选中的值传递到其他部分的...

    block回调测试

    在iOS开发中,Block是一种强大的、灵活的代码封装机制,常用于回调函数或者闭包的实现。本测试项目“BlockTest”着重关注了Block作为回调时的参数传递问题。下面我们将详细探讨Block的基础知识,Block作为回调的使用...

    Delegate&Block作回调

    例如,可能有一个ViewController,它既可以使用Delegate来与另一个对象通信,也可以使用Block来执行一些简单的回调操作。通过这种方式,学习者可以更深入地理解这两种回调机制,并在实际项目中做出明智的选择。 ...

    iOS块回调代替代理模式演示

    总的来说,"iOS块回调代替代理模式演示"这个主题展示了如何利用块回调简化对象间的通信,提高代码可读性和维护性。通过学习和实践这种模式,开发者能够更灵活地设计和实现iOS应用。在`blockCallbackReplaceDelegate`...

    iOS Block使用教程

    在iOS开发中,Block是一种强大的编程工具,它允许我们在代码中定义匿名函数或者闭包,使得函数能够作为参数传递,也可以直接在其他函数内部定义和使用。熟练掌握Block的使用对于提升iOS应用的代码质量、可读性和效率...

    iOS block使用总结

    Blocks是Objective-C的语法扩展,后来也被引入到Swift中,对于处理异步操作、事件回调、简化代码结构等方面具有重要作用。下面将对iOS Block的使用进行详细讲解。 1. **Block的定义** Block可以看作是一种匿名函数...

    ios-自定义实用Alert提示框(Block回调).zip

    - 在需要显示Alert的地方,简单调用自定义类的方法,传入必要的参数和回调Block,即可实现Alert的展示和事件处理。 这种自定义 Alert 提示框的方式不仅提高了代码的可维护性,还使得在项目中添加、修改或删除Alert...

    ios自定义回调方法Demo

    本文将深入探讨如何在iOS中创建和使用自定义回调方法,以及它们在实际项目中的应用。 首先,让我们了解回调的基本概念。回调是一种设计模式,其中一个函数或方法作为参数传递给另一个函数,以便在某个事件发生或...

    ios-block回调的地图.zip

    总的来说,“ios-block回调的地图.zip”项目提供了一个方便的工具,帮助iOS开发者快速集成和管理地图定位功能,通过Block回调简化了事件处理,提高了开发效率。对于那些不熟悉CoreLocation框架或者想要优化地图定位...

    iOS Block 的使用, 包括传值, 基本用法

    Block不仅可以简化复杂的代码结构,还可以实现回调机制,使得异步操作变得简单易懂。本篇文章将深入探讨iOS Block的基本用法以及如何传递值。 ### 1. Block的基本结构 Block 是一个对象,它可以捕获并存储在其定义...

    Java对接iOS内购,回调地址验证.zip

    主要是Java对接iOS内购,回调验证相关的方法, 真机测试的时候,一定要退出原来的账号,才能用沙盒测试账号,二次验证,请注意区分宏, 测试用沙盒验证,App Store审核的时候也使用的是沙盒购买,所以验证购买凭证的...

    ios-一句代码实现键盘弹出消失监控,含block回调结果.zip

    本资源“ios-一句代码实现键盘弹出消失监控,含block回调结果.zip”提供了一个简洁的方法来监控键盘的显示和隐藏状态,并通过Block回调进行相应的处理逻辑。这个功能在很多场景下都非常实用,比如在聊天应用中需要...

    ios Block和代理的对比

    在iOS开发中,Block和代理是两种常用的回调机制,它们都可以用来实现对象间的通信,但具体用法和特性有所差异。下面将详细讲解Block和代理的对比,以及它们各自的应用场景。 首先,Block是一种内联函数,它可以捕获...

    ios-把block当成属性放在全局字典中的用例.zip

    在这个“ios-把block当成属性放在全局字典中的用例.zip”示例中,开发者创建了一个单例字典,将Block作为其中的一个属性,以便于在应用程序的不同部分之间共享和调用,从而简化了跨界面的数据传递和回调机制。...

Global site tag (gtag.js) - Google Analytics