再议Block

什么是Block?

Block objects are a C-level syntactic and runtime feature. They are similar to standard C functions, but in addition to executable code they may also contain variable bindings to automatic (stack) or managed (heap) memory. A block can therefore maintain a set of state (data) that it can use to impact behavior when executed.

在ios,blocks是对象,它封装了一段代码,这段代码可以在任何时候执行。Blocks可以作为函数参数或者函数的返回值,而其本身又可以带输入参数或返回值。

一个block本质上就是一个函数指针,即那个代码快的内存地址。block常用作传值,实际上就是把block的地址传到要调用block的地方。

block是带有局部变量的匿名函数(即没有名称的函数),就是OC中的闭包(closure),又名匿名函数,块函数,块。

什么是闭包(closure)? 闭包是可以包含自由(未绑定到特定对象)变量的代码块。

block的用途

block都是一些简短代码片段的封装,适用作工作单元,通常用来做并发任务、遍历、以及回调。

block的用法?

1,block Pointer定义如下:

回传值 (^名字)(参数列表);

2,block Pointer 具体使用:

// 声明一个名字为myBlock的block 指针,该指针指向的Block有一个int输入和一个int 输出

int (^myBlock) (int a);

//将Block的实体指定给myBlock指针

myBlock = ^(int a) {

       return a*a;

};

//调用blcok实体

int result = myBlock(4);

3,使用typedef给复杂变量block定义类型别名

//使用定义的新类型myBlock来声明对象,等价于int (^myBlock) (int a);

typedef int (^myBlock) (int a);

声明block属性变量的时候,property中需设置成copy

4,用block来存取变量

存取静态变量

//result的值是8,因为outA是static类型的变量

static int outA = 8;

int (^myPtr)(int) = ^(int a){return outA + a;};

outA = 5;

int result = myPtr(3);    

存取自由变量

{ __block int num = 5; int (^myPtr)(int) = ^(int a){return num++;}; int (^myPtr2)(int) = ^(int a){return num++;};

//result的值为5,num的值为6  int result = myPtr(0); 

 //result的值为6,num的值为7  result = myPtr2(0);   NSLog(@"result=%d", result); }

5, block作为类的属性

typedef int (^MyBlock) (int a); @property(nonatomic,copy) int (^block)(int a); //使用c的方式, 不能使用OC函数形参的写法. @property (nonatomic,copy) MyBlock blockName;

6,block作为函数参数

作为函数的参数,blocks某种意义上替代了回调函数或者delegate。当函数调用了,假设某个事件触发,这时blocks里的内容就会运行。这样有利于代码的整合和阅读,你不需要到处去实现委托方法了。

- (void)beginTaskWithCallbackBlock:(void (^)(void))callbackBlock { ...      callbackBlock(); } - (void)beginFetchWithCallbackBlock:(MyBlock)callbackBlock { ...     callbackBlock(3); }

7,以block作为函数返回值

-(int (^)(int))blockBack{     return ^(int cout) {

        return cout;

     }; }

8,Blocks可以访问局部变量,但是不能修改。 int num = 0;     //使用block     int (^myBlock) (int a,int b) = ^(int a,int b){         num = a+b;         return num;     };

如果要修改就要加关键字:__block (注意,是两个下划线"_")

__block int num = 0;     //使用block     int (^myBlock) (int a,int b) = ^(int a,int b){         num = a+b;         return num;     };

block在使用中遇到的问题?

1.    修改局部变量需要在局部变量前面加__block修饰符,将变量的存储范围扩展为该函数以及该函数内定义的block的行为主体内。

2.    在属性定义一个block的时候需要使用copy,因为块是在栈上分配的,一旦离开作用域, 就会释放, 因此如果你要把块用在别的地方, 必须要复制一份

3.    在ARC下, 以下几种情况, Block会自动被从栈复制到堆被执行copy方法

·  作为方法返回值

·  将Block赋值给附有__strong修饰符的id类型的类或者Blcok类型成员变量时

·  在方法名中含有usingBlock的Cocoa框架方法或者GCD的API中传递的时候.

4. 循环引用的问题

A和B两个对象,A持有B,B同时也持有A,A只有B释放之后才有可能释放,同样B只有A释放后才可能释放,当双方都在等待对方释放的时候, retain cycle就形成了,结果是,两个对象都永远不会被释放,最终内存泄露。

循环引用(retain cycle)的解决方法:

1,尽量保持子对象引用父对象的时候使用弱引用,也就是assign,比如:

@property (nonatomic,assign) NSObject *parent;

2,及时地将造成retain cycle中的一个变量设置为nil,将环break掉.

3,block中的retain cycle

@interface ABCBlockKeeper : NSObject

@property (copy) void (^block)(void);

@implementation ABCBlockKeeper

·

- (void)configureBlock {

self.block = ^{

[self doSomething]; // capturing a strong reference to self

// creates a reference cycle

};

}

...

@end

·  4,block中retain cycle 的解决

·  方法一 将引用的一方变成weak,从而避免循环引用

·  - (void)configureBlock {

XYZBlockKeeper * __weak weakSelf = self;

·

self.block = ^{

[weakSelf doSomething]; // capture the weak reference

// to avoid the reference cycle

}

}

·

或者

- (void)configureBlock {

__weak typeof(self) weakSelf = self;

·

self.block = ^{

//如果想防止 weakSelf 被释放,可以再次强引用

typeof(weakSelf) strongSelf = weakSelf;

[weakSelf doSomething]; // capture the weak reference cycle

}

}

·  方法二.使用完某对象没有必要在保留该对象的时候,在block里面将对象释放即可打破保留环

·  - (void)downloadData {

NSURL *url = [NSURL URLWithString:@"http://www.baidu.com"];

_networkFetcher = [[EOCNetworkFetcher alloc] initWithURL:url];

[_networkFetcher startWithCompletionHandler:^(NSData *data) {

_fetchedData = data;

_networkFetcher = nil;//加上此行,此处是为了打破循环引用

}];

}

·  方法三. 在调用完block之后,将该block设置为nil(block为某类的属性的时候,这么使用)

·  - (void)p_requestCompleted {

if(_completionHandler) {

_completionHandler(_downloadData);

}

self.completionHandler = nil;//加上此行,此处是为了打破循环引用

}

如何使用xcode检测循环引用

1.    Xcode 的Instruments工具集可以很方便地检测循环引用,但是检测不出block产生的循环引用,示例如下

1.    - (void)viewDidLoad {

[super viewDidLoad];

//firstArray 持有secondArray, secondArray 持有 firstArray,形成retain cycle

NSMutableArray *firstArray = [NSMutableArray array];

NSMutableArray *secondArray = [NSMutableArray array];

[firstArray addObject:secondArray];

[secondArray addObject:firstArray];

}

1.    ·  在Xcode 的菜单栏,选择“Product”--->“Profile”,在调出的界面中选择"Leaks"--->"choose",调出Instruments界面。

Instruments会用红色的X表示一次内存泄露的产生,Instruments中可以通过切换到“Leaks”,单击“Cycles&Roots”,就可以看到以图形方式显示出来的循环引用,这样,我们就可以很方便的看到产生循环引用的对象了。

1.    具体使用步骤如下:

部分内容摘自:www.dullgrass.com

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Jimoer

在Java的反射中,Class.forName和ClassLoader的区别

最近在面试过程中有被问到,在Java反射中Class.forName()加载类和使用ClassLoader加载类的区别。当时没有想出来后来自己研究了一下就写下来...

1572
来自专栏用户2442861的专栏

《Java虚拟机原理图解》 1.2.2、Class文件中的常量池详解(上)

知道了常量池的位置后,然后让我们来揭秘常量池里究竟有什么东西吧~     

1311
来自专栏程序员互动联盟

【编程基础】聊聊C语言-兵马未动粮草先行(1)

上一篇我们讲的聊聊C语言-我的地盘我做主,相信大家对变量的存储类型和变量的作用域有了一定的了解。现在我们马上公布上期的答案如下: #include<stdio....

3358
来自专栏Python小屋

Python函数中单独一个星号或斜线作为形参的含义

在函数定义时,位于*parameter或单独一个星号*之后的所有参数都只能以关键参数的形式进行传值,不接收其他任何形式的传值。 >>> def demo(a, ...

3806
来自专栏哲学驱动设计

重构一个繁琐的数据结构

    在GIX4项目的开发过程中,遇到一个比较复杂的数据结构。复杂,是因为它有许多限制条件。我的工作是在现有系统中,添加新的功能,并在过程中重构部分旧代码。 ...

24310
来自专栏CDA数据分析师

Python3 大作战之 encode 与 decode 讲解

原文链接http://blog.csdn.net/qq_29053519/article/details/79170519 大家好,很久没更新了,也是年底了最近...

3015
来自专栏恰童鞋骚年

.NET基础拾遗(1)类型语法基础和内存管理基础

在.NET中所有的内建类型都继承自System.Object类型。在C#中,不需要显示地定义类型继承自System.Object,编译器将自动地自动地为类型添...

1142
来自专栏绿巨人专栏

TypeScript中的怪语法

1223
来自专栏GreenLeaves

C# Encoding

之前做公司项目的时候,对于C#编码这块总是一知半解,所以打算通过这篇笔记对C#编码(Encoding)进行彻底的扫盲,关于编码和字符集的基础知识,请参考字符集和...

2797
来自专栏听雨堂

获得定长字符串

        C#中的字符串是Unicode编码,length是Unicode的Char的个数。所以,假如一个字符串中中英文混杂,又想获得一个固定宽度的字符串...

2476

扫码关注云+社区

领取腾讯云代金券