Block的使用--页面传值方法及探究

上一篇讲了Block的简单实用,这一篇则是讲述Block的最经常也是比较简单的用法,个人认为这是block相对其他页面传值方式比较好的一个用处。也顺便总结下这两天对Block的理解的一些注意点。
对于页面传值这种情况,我们一般在程序里有几种处理的方法。
1、设置Delegate
2、使用NotificationCenter
3、使用Block
4、KVO页面传值
5、NSUserDefault等文件存储机制做中间介质传递(但比较浪费,用在页面传值上几率很低)
这一篇博客我只讲一下Block的一般页面传值的使用方法还有探究下其原理。话不多说直接上代码:

方法以及使用

///第一个页面中:
#import "ViewController.h"
#import "NextVC.h"

@interface ViewController ()<NextVCDelegate>{
    UILabel *_useBlockText;//显示文字
    UIButton *_toNextVC_Block;//进入下个页面
}
@end

///将按钮与显示label放到view上,这里省略。具体可到末尾看我Demo里的源码

/**
 *  @brief  按钮事件:进入下个页面,指定Block回调函数
 */
- (void)toNext_Block {

    NextVC *next = [[NextVC alloc]init];
    //  指定回调函数
    next.NextVCBlock = ^(NSString *text){
        _useBlockText.text = [NSString stringWithFormat:@"I‘m from block : %@", text];
    };

    [self.navigationController pushViewController:next animated:YES];
}
//第二个页面
//.h头文件
@interface NextVC : UIViewController
@property (nonatomic, copy) void(^NextVCBlock)(NSString *text);
@end

//.m实现文件
#import "NextVC.h"

@interface NextVC ()
{
    UIButton *_useBlock;
    UITextField *_text;
}

///将按钮与显示label放到view上,这里省略。具体可到末尾看我Demo里的源码

/**
 *  @brief  使用Block进行页面传值
 */
- (void)tranferUseBlock {
    //传值部分
    if (self.NextVCBlock)
        self.NextVCBlock(_text.text);

    [self.navigationController popViewControllerAnimated:YES];
}
@end

这里效果大致也就如下:

传递流程探究

在一个类中定义一个Block,在另一个类怎么得到消息改变的呢。

流程分析:通过打断点以及尝试总算明白了block在页面间传值的一个流程。
我们在第二个页面中声明了一个block类型的属性NextVCBlock:
@property (nonatomic, copy) void(^NextVCBlock)(NSString *text);

而我们在第一个页面实例化了NextVC的对象:
NextVC *next = [[NextVC alloc]init];
并且实现了这个NextVCBlock:
next.NextVCBlock = ^(NSString *text){
    _useBlockText.text = ...;
};

当我们在第二个页面中调用了该Block:
self.NextVCBlock(_text.text);
这会自动把Block复制到堆上,然后触发了next.NextVCBlock内部实现代码。

外部变量的使用权限问题

为了优化存储,Block变量与Block本身一开始都是存储于栈上的,跟局部变量类似。在函数体中定义的局部变量通常都是存储在栈内,全局变量/静态变量一般放在堆上。block在引用外部变量时可以是readonly。只能读不能改,如果你想修改局部变量,则应该使用__block对该变量进行修饰。比如 __block int sum = 10;

原因分析:
这是因为block事实上是指向一个结构体的指针,其内部的代码会被声称一个函数。
内部代码在使用外部的变量sum时,传递的只是一个值,而当使用了__block修饰后,传递过来的则是变脸sum的地址。

循环引用的问题(Memory recycle)

比如我们刚刚上面的- (void)toNext_Block 这个函数中,如果我们加入一句NSLog(@"%@",next.description);,也就是:

    NextVC *next = [[NextVC alloc]init];

    next.NextVCBlock = ^(NSString *text){
        _useBlockText.text = [NSString stringWithFormat:@"I‘m from block : %@", text];
        NSLog(@"%@",next.description);
    };

这种情况就会产生循环引用

原因分析:
因为当这个函数生命周期结束的时候,系统想释放next,但释放next首先要释放NextVCBlock这个变量.
而我们在NextVCBlock中又强引用了next这个对象。
主要原因是所有局部变量在默认情况下的修饰符都为__strong。

解决方法:
你以为这里只需要在NextVC前加__weak即可解决吗?事实却不是如此,因为如果我们使用:
__weak NextVC *next = [[NextVC alloc]init];
next一声明马上就被系统自动回收了,所以后面的使用是不执行的。最好的解决方法应该是:
NextVC *next = [[NextVC alloc]init];
__weak typeof(NextVC) *weakNext = next;
然后使用weakNext代替next:
weakNext.NextVCBlock = ^(NSString *text){
    _useBlockText.text = [NSString stringWithFormat:@"I‘m from block : %@", text];
    NSLog(@"I‘m weakNext == %@",weakNext.description);
};

在ARC环境下,以下几种情况Block会自动从栈复制到堆上:

(来源于网上一个帖子所说)

1、被执行copy方法

2、作为方法的返回值

3、将Block赋值给由__strong修饰符的id对象或者Block类型的成员变量

4、在方法名中含有usingBlock的Cocoa框架方法或者GDC的API中传递的时候(这一点我也不是很理解)

这里有个知识点可以扩展下:
    栈的优点是创建速度快、管理简单。有严格的生命周期。
    因为是先进后出队列,所以效率高也不会有碎片问题。由系统释放。

    堆是动态分配的,可由程序员自行控制释放。
    但是是根据指针指向一串不连续的内存,空间比较大但频繁的new/release肯定会造成内存空间的更加不连续,即内存碎片化。

效率上:
    堆:char *s1 = "Hellow Word";是在编译时就确定的;
    栈:char s1[] = "Hellow Word"; 是在运行时赋值的;
    再比如NSString与NSMutableString的对象,前者是放在栈中,后者是放在堆上。创建方法也不同,比如
    NSString *str = @"hello";
    NSMutableString *mutableStr = [[NSMutableString alloc] initWithString:@"hello"];
    前者是直接在栈上创建,后者是需要分配空间再进行初始化。

Demo地址:https://github.com/ChenNan-FRAM/TransferDataDemo

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-20 18:27:44

Block的使用--页面传值方法及探究的相关文章

iOS阶段学习第32天笔记(页面传值方法介绍)

iOS学习(UI)知识点整理 一.界面传值方法 1.方法一  Block传值  通过SubView视图的Block向View视图传值改变View视图的背景色 实例代码: 1)SubViewController.h 文件的代码实现 1 #import <UIKit/UIKit.h> 2 @interface SubViewController : UIViewController 3 @property (nonatomic,copy) void(^callback)(UIColor *colo

asp.net 几种传值方法的分析

本文转自:http://www.cnblogs.com/shengtianlong/archive/2010/08/11/1797608.html ASP.NET页面传值方法的优缺点及适用范围 1. Get(即使用QueryString显式传递)     方式:在url后面跟参数.     特点:简单.方便.     缺点:字符串长度最长为255个字符:数据泄漏在url中.     适用数据:简单.少量.关键的数据.     适用范围:传递给自己.传递给另一个目标页面:常用于2个页面间传递数据.

页面传值--Delegate/Block/KVO/Notification

上一篇讲了Block的页面传值.这里也就顺便把常用的几种页面传值放上来.这里我写了一个Demo:https://github.com/ChenNan-FRAM/TransferDataDemo .想下的可以上去下载来看看. 这里主要把上一篇提到的四种方式说一下(话不多说直接核心代码讲). 1.使用Delegate 2.使用Notification 3.使用Block 4.使用KVO 定义及使用 第二个界面.h 头文件 //定义一个委托协议 @protocol NextVCDelegate <NS

iOS block传值方法

iOS开发中的页面传值方式有很多种,最常见的是代理和属性传值方法,不过,block也可以传值,而且在一些特定的场合中,block传值会更简单,下面是一个我写的一个demo,大家可以参考一下. 在#import "AppDelegate.h"中,代码如下: 在第一个控制器中代码如下 在第二个控制器的.h文件中代码如下 在第二个控制器的.m文件中代码如下

webform 页面传值的方法总结

ASP.NET页面之间传递值的几种方式 页面传值是学习asp.net初期都会面临的一个问题,总的来说有页面传值.存储对象传值.ajax.类.model.表单等.但是一般来说,常用的较简单有QueryString,Session,Cookies,Application,Server.Transfer. 一.QueryString QueryString是一种非常简单的传值方式,他可以将传送的值显示在浏览器的地址栏中.如果是传递一个或多个安全性要求不高或是结构简单的数值时,可以使用这个方法.但是对于

JAVASCRIPT实现的WEB页面跳转以及页面间传值方法

在WEB页面中,我们实现页面跳转的方法通常是用LINK,BUTTON LINK ,IMG LINK等等,由用户点击某处,然后直接由浏览器帮我们跳转. 但有时候,需要当某事件触发时,我们先做一些操作,然后再跳转,这时,就要用JAVASCRIPT来实现这一跳转功能. 下面是具体的做法: 一:跳转到新页面,并且是在新窗口中打开时: 复制代码 代码如下: function gogogo() { //do someghing here... window.open("test2.html");

页面传值:属性,协议,Block传值

1.属性传值和协议传值 1 #import "RootViewController.h" 2 #import "SecondViewController.h" 3 #warning 第四步:签订协议 4 @interface RootViewController ()<SecondViewControllerDelegate> 5 @property(nonatomic,strong)UILabel *showTextLable; 6 @end 7 8

asp.net页面之间传值方法详解

asp.net中页面之间传值我们用得最多的就是get,post这两种了,其它的如session,appliction,cookie等这些相对来说少用也不是常用的,只是在特殊情况下在使用了. 1. Get(即使用QueryString显式传递) 方式:在url后面跟参数. 特点:简单.方便. 缺点:字符串长度最长为255个字符:数据泄漏在url中. 适用数据:简单.少量.关键的数据. 适用范围:传递给自己.传递给另一个目标页面:常用于2个页面间传递数据. 用法:例如:url后加?UserID=…,

C#:总结页面传值几种方法

小知识点: 1.  W7自带 .NetFrameWork 3.5, 兼容模式为 高版本号兼容低版本号: 2. WF和WPF都是基于XAML的,可是两者的用途不同. WF是一种开发框架,将工作流嵌入在.NET Framework应用程序中,所主要用于开发创建工作流应用程序.WF:http://msdn.microsoft.com/zh-cn/library/ms734696.aspx WPF是一种渲染UI的技术是一个用于Windows平台的全新的图形显示系统,它包括在.NET Framework中