使用block的时候什么情况下使用redis需要

& & & &Block是一种数据类型,C语言中的匿名函数,可以被当做参数和返回值传递,它是提前准备好的能工作的代码单元,可以在任何需要的时候被执行。& & & &特意小结了下对 block 的一些理解,如下:& & & &一、默认情况下,我们不允许在 block 代码块内部修改外部变量的数值。如果要修改外部定义的变量,我们需要使用__block 关键字进行修饰。& & & &二、使用Block传值时要防止循环引用。& & & & & & &例如:在一个控制器中,self强指针指向一个对象,这个对象又强指针指向一个 block,在这个 block 的代码块中有一个 self. ,那么就会造成循环引用,从而导致内存无法释放,造成内存泄露。& & & & & & 解决方法:只要打断其中一条强引用。& & & & & & & & & 一般在ARC情况下,通常用 __weak 修饰 block 中的 self. ,从而打断强引用。例如:__weak typeof(self)weakSelf = self ,用 weakSelf 来指向 self 的内存地址,使得 self 在 block 中使用时变成一个弱指针,从而打断循环引用。& & & & & & & & &在MRC情况下,用 __unsafe__unretained 来打断强引用,但是 __unsafe__unretained 是 “assign” 形式,如果指向的对象被释放,其指针地址还是存在的保存不变,如果继续使用该指针,就会出现“野指针”现象。& & & & & &所以当Block内引用局部变量时,在MRC环境下我们使用 __block,ARC环境下要用 __weak 修饰此变量来解除循环引用。& & & 三、block 的内存管理问题。& & & & & & &当 block 是一个变量时,在ARC运行环境下,有以下几点特性:& & & & & & &&& 1.block没有使用外部变量时,保存在全局区;& & & & & & & & 2.使用了外部变量,会保存在堆区。& & & & & & MRC运行环境下的特性:& & & & & & & &1.定义block:保存在栈区;& & & & & & & &2.若需要使用 Block_copy 操作时,会把block复制到堆区;& & & & & & & &3.block使用完成之后,需要使用 Block_release 释放& & & & & & & &4.如果没有使用外部变量(这种情况极少),同样保存在全局区。关于栈区、堆区的特点:& & & &栈区:主要存放局部变量,定义的参数等,在函数结束时,系统会自动回收它的内存空间。 特点:方便快捷。& & & &堆区:是由程序员自行管理的内存地址,程序结束时,如果内存地址没有释放完全,则由系统回收。特点:灵活。
最新教程周点击榜
微信扫一扫有没有这样一个需求场景,block 会产生循环引用,但是业务又需要你不能使用 weak self? 如果有,请举一个例子并且解释这种情况下如何解决循环引用问题。
需要不使用 weak self 的场景是:你需要构造一个循环引用,以便保证引用双方都存在。比如你有一个后台的任务,希望任务执行完后,通知另外一个实例。在我们开源的 YTKNetwork 网络库的源码中,就有这样的场景。
在 YTKNetwork 库中,我们的每一个网络请求 API 会持有回调的 block,回调的 block 会持有 self,而如果 self 也持有网络请求 API 的话,我们就构造了一个循环引用。虽然我们构造出了循环引用,但是因为在网络请求结束时,网络请求 API 会主动释放对 block 的持有,因此,整个循环链条被解开,循环引用就被打破了,所以不会有内存泄漏问题。代码其实很简单,如下所示:
YTKBaseRequest.m
- (void)clearCompletionBlock {
// nil out to break the retain cycle.
self.successCompletionBlock =
self.failureCompletionBlock =
总结来说,解决循环引用问题主要有两个办法:
第一个办法是「事前避免」,我们在会产生循环引用的地方使用 weak 弱引用,以避免产生循环引用。
第二个办法是「事后补救」,我们明确知道会存在循环引用,但是我们在合理的位置主动断开环中的一个引用,使得对象得以回收。
下期的问题是:weak 变量在引用计数为 0 时,会被自动设置成 nil,这个特性是如何实现的?
阅读(...) 评论()block一 : block的基本语法和使用
block一 : block的基本语法和使用
发布时间: 1:43:25
编辑:www.fx114.net
本篇文章主要介绍了"block一 : block的基本语法和使用",主要涉及到block一 : block的基本语法和使用方面的内容,对于block一 : block的基本语法和使用感兴趣的同学可以参考一下。
1.block的语法:
block定义的标准方法为:
^返回值类型&(参数列表)&{表达式}&
^&int&(void)&{&&&&&&&&&&return&1;&&&&&&};&&
其中int是返回值类型。void是参数列表,表示不传参数。{}中的是表达式。
但是当一些情况下,有的参数是可以省略的。下面就说一下这些省略的情况
①. 当返回值类型是void的时候,void可以省略
^&void&(void)&{&&&&&&&&&&NSLog(@&这是block&);&&&&&&};&&
^&(void)&{&&&&&&&&&&NSLog(@&这是block&);&&&&&&};&&
这两个表达式是一样的。
②.当参数是void的时候,参数也是可以省略的。例如:
^&(void)&{&&&&&&&&&&NSLog(@&这是block&);&&&&&&};&&
^{&&&&&&&&&&NSLog(@&这是block&);&&&&&&};&&
这两个表达式也是一样的。
2. block的申明
有的时候当block定义之前需要申明。下面我们来看看block申明的方法:
返回值类型 (^block的名字) (参数列表)
int&(^block)(int)&=&&^&int&(int&a){&&&&&&&&&&return&a+1;&&&&&&};&&
等号前面的就是block的申明。申明的block必须和赋值的block一致,否则会出现编译错误
3. block的使用
①block当成变量使用
block其实也是一种变量,可以把他当成一个变量使用,最常用的方法是在文件头先定义一个block。用申明的方法定义。例如:
typedef&int&(^block)&(int);&&
定义过之后就可以和使用变量一样,用block的名字使用定义的这个block了,例:
block&selfBlock&=&^&int&(int&a){&&&&&&&&&&return&a+1;&&&&&&};&&
int&(^block)(int)&=&&^&int&(int&a){&&&&&&&&&&return&a+1;&&&&&&};&&
这两段代码是一样的。
前面一个是直接像使用变量一样使用block,类似int a =... 这里就用 block a =...。
②block截取自动变量和改变外部变量值
block具有截取自动变量的功能。例:
{&&&&&&&&int&m=0;&&&&&&int&(^block)(void)&=&&^&int&{&&&&&&&&&&NSLog(@&in&block&m=%d&,m);&&&&&&&&&&return&1;&&&&&&};&&&&&&m&=&3;&&&&&&block();&&}&&
输出:in&block&m=0&&
截取自动变量就是在block定义的时候,将外部变量截取,在block中这个变量值不再改变。如例所示,在定义block的时候已经将变量截取了。所以在block中,m=0。不会因为外部m改变而改变。所以输出的是0,不是3。
实际他的具体实现是因为创建block的时候有一个赋值操作,将外部变量进行了复制。
{&&&&&&int&m=0;&&&&&&int&(^block)(void)&=&&^&int&{&&&&&&&&&&int&inblock&=&m;&&&&&&&&&&NSLog(@&in&block&m=%d&,inblock);&&&&&&&&&&return&1;&&&&&&};&&&&&&m&=&3;&&&&&&block();&&&&}&&
block的具体实现类似上面这段代码,block会自带一个变量inblock。在block创建的时候会用这个变量保存外部变量m,block内部使用到m的地方,都会用inblock代替。所以当外部的m改变时,和block内部m的使用已经完全没关系了。
普通情况下是不能在block中改变外部变量的值的,如上面所说,因为block内部的外部变量都是赋值来的,所以是无法改变的,如果直接在内部写上改变值的代码,在编译的时候,会报编译错误。如图所示。
如果真的想改变变量的值,可以在定义变量的时候使用__block。
{&&&&&&__block&int&m=0;&&&&&&int&(^block)(void)&=&&^&int&{&&&&&&&&&&m&=&1;&&&&&&&&&&NSLog(@&in&block&m=%d&,m);&&&&&&&&&&return&1;&&&&&&};&&&&&&m&=&3;&&&&&&block();&&}&&
这样就不会报编译错误了。但是注意了,加上__block的变量将不会拥有截取自动变量的功能。原理上来说,加上__block之后,block内部的inblock将不会赋值,而是直接使用外部变量。不会具有截取自动变量功能。我们来用例子看看。
{&&&&&&__block&int&m=0;&&&&&&int&(^block)(void)&=&&^&int&{&&&&&&&&&&NSLog(@&in&block&m=%d&,m);&&&&&&&&&&return&1;&&&&&&};&&&&&&m&=&3;&&&&&&block();&&}&&
输出:in&block&m=3&&
使用上面截取自动变量的例子。在变量定义时候加上__block。输出完全变了。block将不再拥有截取自动变量的功能
4.Block使用copy
每个block都会有自己的作用域,在超出作用域的时候,有时不使用copy的话,会导致崩溃,特别是非ARC的情况下,下面我们来看看什么时候不需要copy,什么时候需要copy
①在未使用外部变量的情况下,block属于全局变量,不需要copy
bbk&=&^{&&&&&&NSLog(@&abcde&);&&};&&
②.其他情况下,除了使用GCD之外,都最好使用copy,否则就会有崩溃的风险
一、不得利用本站危害国家安全、泄露国家秘密,不得侵犯国家社会集体的和公民的合法权益,不得利用本站制作、复制和传播不法有害信息!
二、互相尊重,对自己的言论和行为负责。
本文标题:
本页链接:2015年4月 移动开发大版内专家分月排行榜第二
2015年5月 移动开发大版内专家分月排行榜第三2015年3月 移动开发大版内专家分月排行榜第三2014年10月 移动开发大版内专家分月排行榜第三
2015年5月 移动开发大版内专家分月排行榜第一2015年4月 移动开发大版内专家分月排行榜第一2014年9月 移动开发大版内专家分月排行榜第一
2015年3月 移动开发大版内专家分月排行榜第二2014年8月 移动开发大版内专家分月排行榜第二
2015年5月 移动开发大版内专家分月排行榜第一2015年4月 移动开发大版内专家分月排行榜第一2014年9月 移动开发大版内专家分月排行榜第一
2015年3月 移动开发大版内专家分月排行榜第二2014年8月 移动开发大版内专家分月排行榜第二
2015年4月 移动开发大版内专家分月排行榜第二
2015年5月 移动开发大版内专家分月排行榜第三2015年3月 移动开发大版内专家分月排行榜第三2014年10月 移动开发大版内专家分月排行榜第三
匿名用户不能发表回复!|
每天回帖即可获得10分可用分!小技巧:
你还可以输入10000个字符
(Ctrl+Enter)
请遵守CSDN,不得违反国家法律法规。
转载文章请注明出自“CSDN(www.csdn.net)”。如是商业用途请联系原作者。全面解析Objective-C中的block代码块的使用
作者:suiling
字体:[ ] 类型:转载 时间:
这篇文章主要介绍了Objective-C中的block代码块的使用,包括闭包等重要特性的讲解,需要的朋友可以参考下
1.相关概念
在这篇笔记开始之前,我们需要对以下概念有所了解。
1.1 操作系统中的栈和堆
注:这里所说的堆和栈与数据结构中的堆和栈不是一回事。
我们先来看看一个由C/C++/OBJC编译的程序占用内存分布的结构:
栈区(stack):由系统自动分配,一般存放函数参数值、局部变量的值等。由编译器自动创建与释放。其操作方式类似于数据结构中的栈,即后进先出、先进后出的原则。
例如:在函数中申明一个局部变量系统自动在栈中为b开辟空间。
堆区(heap):一般由程序员申请并指明大小,最终也由程序员释放。如果程序员不释放,程序结束时可能会由OS回收。对于堆区的管理是采用链表式管理的,操作系统有一个记录空闲内存地址的链表,当接收到程序分配内存的申请时,操作系统就会遍历该链表,遍历到一个记录的内存地址大于申请内存的链表节点,并将该节点从该链表中删除,然后将该节点记录的内存地址分配给程序。
例如:在C中malloc函数
p1 = (char )malloc(10);
但是p1本身是在栈中的。
链表:是一种常见的基础数据结构,一般分为单向链表、双向链表、循环链表。以下为单向链表的结构图:
单向链表是链表中最简单的一种,它包含两个区域,一个信息域和一个指针域。信息域保存或显示关于节点的信息,指针域储存下一个节点的地址。
上述的空闲内存地址链表的信息域保存的就是空闲内存的地址。
全局区/静态区:顾名思义,全局变量和静态变量存储在这个区域。只不过初始化的全局变量和静态变量存储在一块,未初始化的全局变量和静态变量存储在一块。程序结束后由系统释放。
文字常量区:这个区域主要存储字符串常量。程序结束后由系统释放。
程序代码区:这个区域主要存放函数体的二进制代码。
下面举一个前辈写的例子:
//main.cpp
int a = 0; // 全局初始化区
char *p1; // 全局未初始化区
&&& char s[] = "abc"; // 栈
&&& char *p2; // 栈
&&& char *p3 = "123456"; // 在常量区,p3在栈上
&&& static int c =0; // 全局静态初始化区
&&& p1 = (char *)malloc(10);
&&& p2 = (char *)malloc(20); // 分配得来的10和20字节的区域就在堆区
&&& strcpy(p1, "123456"); // 在常量区,这个函数的作用是将"123456" 这串字符串复制一份放在p1申请的10个字节的堆区域中。
&&& // p3指向的"123456"与这里的"123456"可能会被编译器优化成一个地址。
strcpy函数
原型声明:extern char *strcpy(char* dest, const char *src);
功能:把从src地址开始且含有NULL结束符的字符串复制到以dest开始的地址空间。
1.2 结构体(Struct)
在C语言中,结构体(struct)指的是一种数据结构。结构体可以被声明为变量、指针或数组等,用以实现较复杂的数据结构。结构体同时也是一些元素的集合,这些元素称为结构体的成员(member),且这些成员可以为不同的类型,成员一般用名字访问。
我们来看看结构体的定义:
struct tag { member-list } variable-
struct:结构体关键字。
tag:结构体标签。
member-list:结构体成员列表。
variable-list:为结构体声明的变量列表。
在一般情况下,tag,member-list,variable-list这三部分至少要出现两个。以下为示例:
// 该结构体拥有3个成员,整型的a,字符型的b,双精度型的c
// 并且为该结构体声明了一个变量s1
// 该结构体没有标明其标签
// 该结构体拥有同样的三个成员
// 并且该结构体标明了标签EXAMPLE
// 该结构体没有声明变量
struct EXAMPLE{
//用EXAMPLE标签的结构体,另外声明了变量t1、t2、t3
struct EXAMPLE t1, t2[20], *t3;
以上就是简单结构体的代码示例。结构体的成员可以包含其他结构体,也可以包含指向自己结构体类型的指针。结构体的变量也可以是指针。
下面我们来看看结构体成员的访问。结构体成员依据结构体变量类型的不同,一般有2种访问方式,一种为直接访问,一种为间接访问。直接访问应用于普通的结构体变量,间接访问应用于指向结构体变量的指针。直接访问使用结构体变量名.成员名,间接访问使用(*结构体指针名).成员名或者使用结构体指针名-&成员名。相同的成员名称依靠不同的变量前缀区分。
struct EXAMPLE{
//声明结构体变量s1和指向结构体变量的指针s2
struct EXAMPLE s1, *s2;
//给变量s1和s2的成员赋值,注意s1.a和s2-&a并不是同一成员
s2-&a = 3;
s2-&b = 4;
最后我们来看看结构体成员存储。在内存中,编译器按照成员列表顺序分别为每个结构体成员分配内存。如果想确认结构体占多少存储空间,则使用关键字sizeof,如果想得知结构体的某个特定成员在结构体的位置,则使用offsetof宏(定义于stddef.h)。
struct EXAMPLE{
//获得EXAMPLE类型结构体所占内存大小
int size_example = sizeof( struct EXAMPLE );
//获得成员b相对于EXAMPLE储存地址的偏移量
int offset_b = offsetof( struct EXAMPLE, b );
1.3 闭包(Closure)
闭包就是一个函数,或者一个指向函数的指针,加上这个函数执行的非局部变量。
说的通俗一点,就是闭包允许一个函数访问声明该函数运行上下文中的变量,甚至可以访问不同运行上文中的变量。
我们用脚本语言来看一下:
function funA(callback){
&&& alert(callback());
function funB(){
&&& var str = "Hello World"; // 函数funB的局部变量,函数funA的非局部变量
&&& funA(
&&&&&&& function(){
&&&&&&&&&&&
通过上面的代码我们可以看出,按常规思维来说,变量str是函数funB的局部变量,作用域只在函数funB中,函数funA是无法访问到str的。但是上述代码示例中函数funA中的callback可以访问到str,这是为什么呢,因为闭包性。
2.blcok基础知识
block实际上就是Objective-C语言对闭包的实现。
2.1 block的原型及定义
我们来看看block的原型:
NSString * ( ^ myBlock )( int );
上面的代码声明了一个block(^)原型,名字叫做myBlock,包含一个int型的参数,返回值为NSString类型的指针。
下面来看看block的定义:
myBlock = ^( int paramA )
&&& return [ NSString stringWithFormat: @"Passed number: %i", paramA ];
上面的代码中,将一个函数体赋值给了myBlock变量,其接收一个名为paramA的参数,返回一个NSString对象。
注意:一定不要忘记block后面的分号。
定义好block后,就可以像使用标准函数一样使用它了:
myBlock(7);
由于block数据类型的语法会降低整个代码的阅读性,所以常使用typedef来定义block类型。例如,下面的代码创建了GetPersonEducationInfo和GetPersonFamilyInfo两个新类型,这样我们就可以在下面的方法中使用更加有语义的数据类型。
// Person.h
#import // Define a new type for the block
typedef NSString * (^GetPersonEducationInfo)(NSString *);
typedef NSString * (^GetPersonFamilyInfo)(NSString *);
@interface Person : NSObject
- (NSString *)getPersonInfoWithEducation:(GetPersonEducationInfo)educationInfo
&&& andFamily:(GetPersonFamilyInfo)familyI
我们用一张大师文章里的图来总结一下block的结构:
2.2 将block作为参数传递
-(void) testBlock:( NSString * ( ^ )( int ) )myB
-(void) testBlock:( NSString * ( ^ )( int ) )myBlock
&&& NSLog(@"Block returned: %@", myBlock(7) );
由于Objective-C是强制类型语言,所以作为函数参数的block也必须要指定返回值的类型,以及相关参数类型。
2.3 闭包性
上文说过,block实际是Objc对闭包的实现。
我们来看看下面代码:
#import void logBlock( int ( ^ theBlock )( void ) )
&&& NSLog( @"Closure var X: %i", theBlock() );
int main( void )
&&& NSAutoreleasePool *
&&& int ( ^ myBlock )( void );
&&& pool = [ [ NSAutoreleasePool alloc ] init ];
&&& x = 42;
&&& myBlock = ^( void )
&&& logBlock( myBlock );
&&& [ pool release ];
&&& return EXIT_SUCCESS;
上面的代码在main函数中声明了一个整型,并赋值42,另外还声明了一个block,该block会将42返回。然后将block传递给logBlock函数,该函数会显示出返回的值42。即使是在函数logBlock中执行block,而block又声明在main函数中,但是block仍然可以访问到x变量,并将这个值返回。
注意:block同样可以访问全局变量,即使是static。
2.4 block中变量的复制与修改
对于block外的变量引用,block默认是将其复制到其数据结构中来实现访问的,如下图:
通过block进行闭包的变量是const的。也就是说不能在block中直接修改这些变量。来看看当block试着增加x的值时,会发生什么:
myBlock = ^( void )
编译器会报错,表明在block中变量x是只读的。
有时候确实需要在block中处理变量,怎么办?别着急,我们可以用__block关键字来声明变量,这样就可以在block中修改变量了。
基于之前的代码,给x变量添加__block关键字,如下:
对于用__block修饰的外部变量引用,block是复制其引用地址来实现访问的,如下图:
3.编译器中的block
3.1 block的数据结构定义
我们通过大师文章中的一张图来说明:
上图这个结构是在栈中的结构,我们来看看对应的结构体定义:
struct Block_descriptor {
&&& unsign
&&& void (*copy)(void *dst, void *src);
&&& void (*dispose)(void *);
struct Block_layout {
&&& void *
&&& void (*invoke)(void *, ...);
&&& struct Block_descriptor *
&&& /* Imported variables. */
从上面代码看出,Block_layout就是对block结构体的定义:
isa指针:指向表明该block类型的类。
flags:按bit位表示一些block的附加信息,比如判断block类型、判断block引用计数、判断block是否需要执行辅助函数等。
reserved:保留变量,我的理解是表示block内部的变量数。
invoke:函数指针,指向具体的block实现的函数调用地址。
descriptor:block的附加描述信息,比如保留变量数、block的大小、进行copy或dispose的辅助函数指针。
variables:因为block有闭包性,所以可以访问block外部的局部变量。这些variables就是复制到结构体中的外部局部变量或变量的地址。
3.2 block的类型
block有几种不同的类型,每种类型都有对应的类,上述中isa指针就是指向这个类。这里列出常见的三种类型:
_NSConcreteGlobalBlock:全局的静态block,不会访问任何外部变量,不会涉及到任何拷贝,比如一个空的block。例如:
#include int main()
&&& ^{ printf("Hello, World!\n"); } ();
&&& return 0;
_NSConcreteStackBlock:保存在栈中的block,当函数返回时被销毁。例如:
#include int main()
&&& char a = 'A';
&&& ^{ printf("%c\n",a); } ();
&&& return 0;
_NSConcreteMallocBlock:保存在堆中的block,当引用计数为0时被销毁。该类型的block都是由_NSConcreteStackBlock类型的block从栈中复制到堆中形成的。例如下面代码中,在exampleB_addBlockToArray方法中的block还是_NSConcreteStackBlock类型的,在exampleB方法中就被复制到了堆中,成为_NSConcreteMallocBlock类型的block:
void exampleB_addBlockToArray(NSMutableArray *array) {
&&& char b = 'B';
&&& [array addObject:^{
&&&&&&&&&&& printf("%c\n", b);
void exampleB() {
&&& NSMutableArray *array = [NSMutableArray array];
&&& exampleB_addBlockToArray(array);
&&& void (^block)() = [array objectAtIndex:0];
&&& block();
总结一下:
_NSConcreteGlobalBlock类型的block要么是空block,要么是不访问任何外部变量的block。它既不在栈中,也不在堆中,我理解为它可能在内存的全局区。
_NSConcreteStackBlock类型的block有闭包行为,也就是有访问外部变量,并且该block只且只有有一次执行,因为栈中的空间是可重复使用的,所以当栈中的block执行一次之后就被清除出栈了,所以无法多次使用。
_NSConcreteMallocBlock类型的block有闭包行为,并且该block需要被多次执行。当需要多次执行时,就会把该block从栈中复制到堆中,供以多次执行。
3.3 编译器如何编译
我们通过一个简单的示例来说明:
#import typedef void(^BlockA)(void);
__attribute__((noinline))
void runBlockA(BlockA block) {
&&& block();
void doBlockA() {
&&& BlockA block = ^{
&&&&&&& // Empty block
&&& runBlockA(block);
上面的代码定义了一个名为BlockA的block类型,该block在函数doBlockA中实现,并将其作为函数runBlockA的参数,最后在函数doBlockA中调用函数runBloackA。
注意:如果block的创建和调用都在一个函数里面,那么优化器(optimiser)可能会对代码做优化处理,从而导致我们看不到编译器中的一些操作,所以用__attribute__((noinline))给函数runBlockA添加noinline,这样优化器就不会在doBlockA函数中对runBlockA的调用做内联优化处理。
我们来看看编译器做的工作内容:
#import __attribute__((noinline))
void runBlockA(struct Block_layout *block) {
&&& block-&invoke();
void block_invoke(struct Block_layout *block) {
&&& // Empty block function
void doBlockA() {
&&& struct Block_d
&&& descriptor-&reserved = 0;
&&& descriptor-&size = 20;
&&& descriptor-&copy = NULL;
&&& descriptor-&dispose = NULL;
&&& struct Block_
&&& block-&isa = _NSConcreteGlobalB
&&& block-&flags = ;
&&& block-&reserved = 0;
&&& block-&invoke = block_
&&& block-&descriptor =
&&& runBlockA(&block);
上面的代码结合block的数据结构定义,我们能很容易得理解编译器内部对block的工作内容。
3.4 copy()和dispose()
上文中提到,如果我们想要在以后继续使用某个block,就必须要对该block进行拷贝操作,即从栈空间复制到堆空间。所以拷贝操作就需要调用Block_copy()函数,block的descriptor中有一个copy()辅助函数,该函数在Block_copy()中执行,用于当block需要拷贝对象的时候,拷贝辅助函数会retain住已经拷贝的对象。
既然有有copy那么就应该有release,与Block_copy()对应的函数是Block_release(),它的作用不言而喻,就是释放我们不需要再使用的block,block的descriptor中有一个dispose()辅助函数,该函数在Block_release()中执行,负责做和copy()辅助函数相反的操作,例如释放掉所有在block中拷贝的变量等。
4.下面来看几个具体的运行示例:
4.1参数是NSString*的代码块
&&&&&&& void (^printBlock)(NSString *x);
&&&&&&& printBlock = ^(NSString* str)
&&&&&&&&&&& NSLog(@"print:%@", str);
&&&&&&& };
&&&&&&& printBlock(@"hello world!");
运行结果是:print:hello world!
4.2代码用在字符串数组排序
&&&&&&& NSArray *stringArray = [NSArray arrayWithObjects:@"abc 1", @"abc 21", @"abc 12",@"abc 13",@"abc 05",nil];
&&&&&&& NSComparator sortBlock = ^(id string1, id string2)
&&&&&&&&&&& return [string1 compare:string2];
&&&&&&& };
&&&&&&& NSArray *sortArray = [stringArray sortedArrayUsingComparator:sortBlock];
&&&&&&& NSLog(@"sortArray:%@", sortArray);
运行结果:
sortArray:(
4.3代码块的递归调用
代码块想要递归调用,代码块变量必须是全局变量或者是静态变量,这样在程序启动的时候代码块变量就初始化了,可以递归调用
&&&&&&& static void (^ const blocks)(int) = ^(int i)
&&&&&&&&&&& if (i & 0) {
&&&&&&&&&&&&&&& NSLog(@"num:%d", i);
&&&&&&&&&&&&&&& blocks(i - 1);
&&&&&&&&&&& }
&&&&&&& };
&&&&&&& blocks(3);
运行打印结果:
4.4在代码块中使用局部变量和全局变量
在代码块中可以使用和改变全局变量
int global = 1000;
int main(int argc, const char * argv[])
&&& @autoreleasepool {
&&&&&&& void(^block)(void) = ^(void)
&&&&&&&&&&& global++;
&&&&&&&&&&& NSLog(@"global:%d", global);
&&&&&&& };
&&&&&&& block();
&&&&&&& NSLog(@"global:%d", global);
&&& return 0;
运行打印结果:
global:1001
global:1001
而局部变量可以使用,但是不能改变。
&&&&&&& int local = 500;
&&&&&&& void(^block)(void) = ^(void)
//&&&&&&&&&&& local++;
&&&&&&&&&&& NSLog(@"local:%d", local);
&&&&&&& };
&&&&&&& block();
&&&&&&& NSLog(@"local:%d", local);
在代码块中改变局部变量编译不通过。怎么在代码块中改变局部变量呢?在局部变量前面加上关键字:__block
&&&&&&& __block int local = 500;
&&&&&&& void(^block)(void) = ^(void)
&&&&&&&&&&& local++;
&&&&&&&&&&& NSLog(@"local:%d", local);
&&&&&&& };
&&&&&&& block();
&&&&&&& NSLog(@"local:%d", local);
运行结果:
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具

我要回帖

更多关于 solr在什么情况下使用 的文章

 

随机推荐