iOS代码块block使用
代码块的本质是和其他的变量类似,不同的是,代码块存储的数据是一个函数体。使用代码块,你可以像调用其他标准函数一样的调用,可以传入参数,并得到返回值。
脱字符是代码块的语法标记。下图表示代码块的定义。
1.代码块的基本使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
//无参数无返回值 void (^myblock)() = ^() { NSLog (@ "Hello, World!" ); }; myblock(); //带参数无返回值 void (^myblock2)( NSString *string) = ^( NSString *string){ NSLog (@ "%@" ,string);}; myblock2(@ "Hello, World myblock2!" ); //无参数有返回值 int (^myblocksss)() = ^( int i){ return 12;}; int c = myblocksss(); NSLog (@ "%i" ,c); //有参数有返回值 int (^myblock3)( int ) = ^( int i){ return 12 * i; }; int i = myblock3(3); NSLog (@ "%i" ,i); |
2,利用typedef为Block进行重命名
使用typedef为block进行一次重命名,方法跟为函数指针进行重命名是一样的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
// Copyright © 2016年 liujun. All rights reserved. // #import <Foundation/Foundation.h> typedef int (^ MyBlock)( int a, int b); int main( int argc, const char * argv[]) { @autoreleasepool { // insert code here... __block int n = 100; MyBlock block = ^( int a, int b) { n = 20; //不过没有用__block 修饰 代码不会编译通过 return n + a + b; }; NSLog (@ "%i %i" , n ,block(3,4)); //输出结果 100 27 NSLog (@ "%i %i" , block(3,4) ,n); //输出结果 27 20 //以上输出。说明代码块是在调用的时候才会被执行 NSLog (@ "Hello, World!" ); } return 0; } |
3.Block在内存中的位置
根据Block在内存中的位置分为三种类型NSGlobalBlock,NSStackBlock, NSMallocBlock。
NSGlobalBlock:类似函数,位于text段;
NSStackBlock:位于栈内存,函数返回后Block将无效;
NSMallocBlock:位于堆内存。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
// Copyright © 2016年 liujun. All rights reserved. // #import <Foundation/Foundation.h> typedef long (^Sum)( int , int ); int main( int argc, const char * argv[]) { @autoreleasepool { // insert code here...v Sum sum1 = ^ long ( int a, int b) { return a + b ; }; NSLog (@ "sum1 = %@" , sum1); // 打印结果:sum1 = <__NSGlobalBlock__: 0x47d0> int base = 100; Sum sum2 = ^ long ( int a, int b) { return base + a + b; }; NSLog (@ "sum2 = %@" , sum2); // 打印结果:sum2 = <__NSMallocBlock__: 0xbfffddf8> Sum sum3 = [sum2 copy ]; NSLog (@ "sum3 = %@" , sum3); // 打印结果:sum3 = <__NSMallocBlock__: 0x902fda0> NSLog (@ "Hello, World!" ); } return 0; } |
NSGlobalBlock,我们只要实现一个没有对周围变量没有引用的Block,就会显示为是它。而如果其中加入了对定义环境变量的引用,就是NSStackBlock。那么NSMallocBlock又是哪来的呢?malloc一词其实大家都熟悉,就是在堆上分配动态内存时。没错,如果你对一个NSStackBlock对象使用了Block_copy()或者发送了copy消息,就会得到NSMallocBlock。这一段中的几项结论可从代码实验得出。
也就得到了下面对block的使用注意点。
对于Global的Block,我们无需多处理,不需retain和copy,因为即使你这样做了,似乎也不会有什么两样。对于Stack的Block,如果不做任何操作,就会向上面所说,随栈帧自生自灭。而如果想让它获得比stack frame更久,那就调用Block_copy(),让它搬家到堆内存上。而对于已经在堆上的block,也不要指望通过copy进行“真正的copy”,因为其引用到的变量仍然会是同一份,在这个意义上看,这里的copy和retain的作用已经非常类似。
4,外部参数在代码块的使用
blk1和blk2的区别在于:
blk1没有使用Block以外的任何外部变量,Block不需要建立局部变量值的快照,这使blk1与一般函数没有任何区别。
blk2与blk1唯一不同是的使用了局部变量base,在定义(注意是“定义”,不是“运行”)blk2时,局部变量base当前值被copy到栈上,作为常量供Block使用。执行下面代码,结果是203,而不是204。
1
2
3
4
5
6
7
|
int base = 100; base += 100; BlkSum sum = ^ long ( int a, int b) { return base + a + b; }; base++; printf( "%ld" ,sum(1,2)); |
在Block内变量base是只读的,如果想在Block内改变base的值,在定义base时要用 __block修饰:__block int base = 100;
1
2
3
4
5
6
7
8
9
|
__block int base = 100; base += 100; BlkSum sum = ^ long ( int a, int b) { base += 10; return base + a + b; }; base++; printf( "%ld\n" ,sum(1,2)); printf( "%d\n" ,base); |
输出将是214,211。Block中使用__block修饰的变量时,将取变量此刻运行时的值,而不是定义时的快照。这个例子中,执行sum(1,2)时,base将取base++之后的值,也就是201,再执行Blockbase+=10; base+a+b,运行结果是214。执行完Block时,base已经变成211了。
static变量、全局变量 :如果把上个例子的base改成全局的、或static。Block就可以对他进行读写了。因为全局变量或静态变量在内存中的地址是固定的,Block在读取该变量值的时候是直接从其所在内存读出,获取到的是最新值,而不是在定义时copy的常量。
1
2
3
4
5
6
7
8
9
|
static int base = 100; BlkSum sum = ^ long ( int a, int b) { base++; return base + a + b; }; base = 0; printf( "%d\n" , base); printf( "%ld\n" ,sum(1,2)); // 这里输出是3,而不是103 printf( "%d\n" , base); |
输出结果是0 4 1,表明Block外部对base的更新会影响Block中的base的取值,同样Block对base的更新也会影响Block外部的base值。
Block变量,被__block修饰的变量称作Block变量。 基本类型的Block变量等效于全局变量、或静态变量。
5,循环引用
retain cycle问题的根源在于Block和obj可能会互相强引用,互相retain对方,这样就导致了retain cycle,最后这个Block和obj就变成了孤岛,谁也释放不了谁。比如:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@implementation TsetBlock -( id )init{ if ( self = [superinit]) { self .testStr =@ "中国" ; self .block = ^( NSString *name, NSString *str){ NSLog (@ "arr:%@" , self .testStr); // 编译警告:Capturing 'self' strongly in this block is likely to lead to a retain cycle }; } returnself; } @end |
网上大部分帖子都表述为"block里面引用了self导致循环引用",但事实真的是如此吗?我表示怀疑,其实这种说法是不严谨的,不一定要显式地出现"self"字眼才会引起循环引用。我们改一下代码,不通过属性self.testStr去访问String变量,而是通过实例变量_testStr去访问,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@implementation TsetBlock -( id )init{ if ( self = [superinit]) { self .testStr =@ "中国" ; self .block = ^( NSString *name, NSString *str){ NSLog (@ "arr:%@" , _testStr); // 同样出现: Capturing 'self' strongly in this block is likely to lead to a retain cycle }; } returnself; } @end |
可以发现:
即使在你的block代码中没有显式地出现"self",也会出现循环引用!只要你在block里用到了self所拥有的东西!
要分两种环境去解决:在ARC下不用__block ,而是用 __weak 为了避免出现循环引用
1.ARC:用__week
__weaktypeof (self) weakSelf = self; 或者
__weak someClass *weakSelf = self;
2.MRC:用__block ,__block修饰的变量在Block copy时是不会retain的,所以,也可以做到破解循环引用。
__block someClass *blockSelf = self;
bloack的 retain、copy、release 操作
对Block不管是retain、copy、release都不会改变引用计数retainCount,retainCount始终是1;
NSGlobalBlock:retain、copy、release操作都无效;
NSStackBlock:retain、release操作无效,必须注意的是,NSStackBlock在函数返回后,Block内存将被回收。即使retain也没用。容易犯的错误是[[mutableAarry addObject:stackBlock],在函数出栈后,从mutableAarry中取到的stackBlock已经被回收,变成了野指针。正确的做法是先将stackBlock copy到堆上,然后加入数组:[mutableAarry addObject:[[stackBlock copy] autorelease]]。支持copy,copy之后生成新的NSMallocBlock类型对象。
NSMallocBlock支持retain、release,虽然retainCount始终是1,但内存管理器中仍然会增加、减少计数。copy之后不会生成新的对象,只是增加了一次引用,类似retain;
尽量不要对Block使用retain操作。
6.代码块的递归调用
代码块想要递归调用,代码块变量必须是全局变量或者是静态变量,这样在程序启动的时候代码块变量就初始化了,可以递归调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
static void (^ const myblock4)( int ) = ^( int i) { if (i > 0) { NSLog (@ "%i" ,i); myblock4(i - 1); } }; |
iOS代码块block使用的更多相关文章
- 一篇文章看懂iOS代码块Block
block.png iOS代码块Block 概述 代码块Block是苹果在iOS4开始引入的对C语言的扩展,用来实现匿名函数的特性,Block是一种特殊的数据类型,其可以正常定义变量.作为参数.作为返 ...
- [转]iOS代码块Block
代码块Block是苹果在iOS4开始引入的对C语言的扩展,用来实现匿名函数的特性,Block是一种特殊的数据类型,其可以正常定义变量.作为参数.作为返回值,特殊地,Block还可以保存一段代码,在需要 ...
- iOS学习之代码块(Block)
代码块(Block) (1)主要作用:将一段代码保存起来,在需要的地方调用即可. (2)全局变量在代码块中的使用: 全局变量可以在代码块中使用,同时也可以被改变,代码片段如下: ;//注意:全局变量 ...
- 代码块(block)的使用
Objective-C语法之代码块(block)的使用 代码块本质上是和其他变量类似.不同的是,代码块存储的数据是一个函数体.使用代码块是,你可以像调用其他标准函数一样,传入参数数,并得到返回值. 脱 ...
- 04OC之分类Category,协议Protocol,Copy,代码块block
一.Protocol协议 我们都知道,在C#有个规范称之为接口,就是规范一系列的行为,事物.在C#中是使用Interface关键字来声明一个接口的,但是在OC中interface是用来声明类,所以用了 ...
- 从C#到Objective-C,循序渐进学习苹果开发(4)--代码块(block)和错误异常处理的理解
本随笔系列主要介绍从一个Windows平台从事C#开发到Mac平台苹果开发的一系列感想和体验历程,本系列文章是在起步阶段逐步积累的,希望带给大家更好,更真实的转换历程体验.本文继续上一篇随笔<从 ...
- Objective-C-----协议protocol,代码块block,分类category
概述 ObjC的语法主要基于smalltalk进行设计的,除了提供常规的面向对象特性外,还增加了很多其他特性,本文将重点介绍objective-C中一些常用的语法特性. 当然这些内容虽然和其他高级语言 ...
- Objective-C 代码块(block)的使用
代码块本质上是和其他变量类似.不同的是,代码块存储的数据是一个函数体.使用代码块是,你可以像调用其他标准函数一样,传入参数数,并得到返回值. 脱字符(^)是块的语法标记.按照我们熟悉的参数语法规约所定 ...
- Objective-C语法之代码块(block)的使用
代码块本质上是和其它变量相似.不同的是,代码块存储的数据是一个函数体.使用代码块是,你能够像调用其它标准函数一样,传入參数数,并得到返回值. 脱字符(^)是块的语法标记.依照我们熟悉的參数语法规约所定 ...
随机推荐
- Python OS模块常用
python 读写.创建 文件 第二个:目录操作-增删改查 第三个:判断 第四个:PATH 第四个:os.mknod 创建文件(不是目录) import os os.chdir("/&quo ...
- Java 面向对象 知识点基础浅谈
1.类和对象的关系 类是一个抽象的模板,对象是根据模板制造出来的,只有类建立之后,对象才可以在类中实例化对象.举个例子讲:我要用黄金浇筑一块砖,我会在一个模型里进行,这样才能有砖的形状,那模型即是类, ...
- linux常用命令 命令管道符
多命令顺序执行 多命令顺序执行 多命令执行符 格式 作用 : 命令1:命令2 多个命令顺序执行,命令之间没有任何逻辑联系 && 命令1&&命令2 逻辑与 当命令1正确执 ...
- ClientDataSet应用
最近维护一个项目,里面用到ClientDataSet,由于之前接触ClientDataSet比较少,所以这个星期补了一下关于ClientDataSet的知识,并在此记录下我所了解到的并应用到实际项目中 ...
- eclipese的一些卡顿问题
一,在jsp页面上输入拼音的时候很卡顿怎么办? 二,我们在复制粘贴等用到ctry的时候会很卡顿,尤其是在jsp页面.干掉以下就好了.
- Oracle查询语句导致CPU使用率过高问题处理
解决此问题的关键在于如何找到造成CPU使用率过高的SQL语句.步骤如下: 1.使用Process Explorer工具查看到Oracle进程,双击Oracle进程,在弹出的属性窗口的Threads选项 ...
- springboot秒杀课程学习整理1-2
1)从数据库到前端,做了三层转换,最后统一返回给前端的格式 DO-> model: 放在service中,目的是为了组装来自于数据库的数据,有些字段来自于不同的表的取,这一层相当于真正的业务模型 ...
- 笨办法33while循环
改了之前的代码,加入while: #coding: utf-8 print("王小狗丑不丑?丑→1:不丑→2") answer = input(">") ...
- winfrom程序文本框第一次选中问题
想实现这样的功能: 就是在panel中的文本框,当第一次点击文本框时,全选文本框的内容:再次选择时,可以全选,也可以部分选中, 可是文本框总是从左全部选中,还不能从右边选择,在Enter或Down事件 ...
- vs2017 重新生成报错 MSB4057 BuildDependsOn DependsOnTargets ContainerPrepareForLaunch 解决办法
环境: win10 vs2017 .net core 删除引用的包: Microsoft.VisualStudio.Azure.Containers.Tools.Targets