1. 首先,我们快速过一下,什么是Block?

Block是一段代码,它在OC中以^开头,可以有返回值,和参数列表,但就是没有名字。

所以,你可以把它认为是匿名函数。

事实上,它和Swift中的闭包(Closure)是一样的。

或者,学过.NET的童鞋知道委托吧,它和委托也差不多概念。

都是可以在一个方法中传入它,作为参数的方法。

无参无返回值的Block:

[MyObject myMethodParam1: xx param2: ^{

  ...

}];

有参有返回值的Block:

[MyObject myMethodParam1: xx param2: ^BOOL(id param1, id param2) {

  ...

}];

好了,说了那么多,我们来看个例子:

myDict enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
NSLog(@"Key - %@: Value - %@", key, value);
if ([@"END" isEqualToString:key]) {
*stop = YES;
}
}];

上面这段代码枚举一个字典的键值对,知道遇到END键值退出循环,否则枚举所有的键值对。

2. 在Block中我们可不可以使用Block范围以外声明的变量呢?

答案是可以的,但是它是只读的,你如果要修改这个变量,会编译错误。

我们还是来看上面那段代码的例子,在循环中,我们增加了一个外部变量,想要让Block提早结束

BOOL stopEarly = NO;
double stopValue = 100.2;
[myDict enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
NSLog(@"Key - %@: Value - %@", key, value);
if ([@"END" isEqualToString:key] || [value doubleValue] == stopValue) {
*stop = YES;
stopEarly = YES; // 这段代码编译会出错!!
}
}];

上面的代码stopValue变量的Block内读取没有任何问题,但是当我们企图在Block内修改stopEarly变量的值时,编译出错了!

那么如果,我们执意要修改Block外面的变量,是不是可以呢?

答案是可以的。

我们要使用__block关键字,原理上是通过使用该关键字,我们可以把block外的变量从栈中移到堆中,这样就可以在Block内部使用了。

当Block结束时,变量又回到栈中。

还是上面的代码,我们作下修改,如下:

__block BOOL stopEarly = NO;
double stopValue = 100.2;
[myDict enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
NSLog(@"Key - %@: Value - %@", key, value);
if ([@"END" isEqualToString:key] || [value doubleValue] == stopValue) {
*stop = YES;
stopEarly = YES;
}
}]; if (stopEarly)
NSLog(@"Block提前终止了!");

3. 当我们向Block内的对象发送消息时,系统会创建一个指向该对象的强指针

该强指针会一直保留到Block超出自己的范围,不存在。

4. Block数组

property (nonatomic, strong) NSMutableArray *blockArray;

...
- (void) someMethod {
[self.blockArray addObject:^{
...
}];
} // 获取数组中的block并调用
void (^doit)(void) = self.blockArray[];
doit();

5. 常见的Block中导致循环引用(Memory Cycle)的场景

前面我们曾经提到:

当我们向Block内的对象发送消息时,系统会创建一个指向该对象的强指针

该强指针会一直保留到Block超出自己的范围。

换句话说,Block内的所有对象,都会在堆上保留自己的内存,也就是说,Block会保留

指向这些对象的强指针(strong pointer)。

我们来看下面的代码:

@property (nonatomic, strong) NSMutableArray *blockArray;

...

// in the blockArray a strong pointer has been kept to the self object
// while self holds another strong pointer to blockArray
- (void) someMethod {
[self.blockArray addObject:^{
[self doSomething];
}];
}

上面的代码的问题是:blockArray保留了一个指向类对象的强引用指针,而类对象,也就是self也保留了

一个指向blockArray的强引用指针。

大家都有强指针指向对方, 它们都无法从栈中释放,这就导致了内存的循环引用。

解决方案:
为了打破循环引用,我们要使用一个弱类型的局部变量。
什么意思?
由于局部变量都是强指针类型,都保留指向堆中的强指针,所以我们要想办法创建一个弱指针类型的局部变量来
打破循环引用。
可以使用关键字:
__weak
比如:
__weak MyClass *weakSelf = self;
这样即使self本身是强指针类型的,而我们创建的weakSelf却变成了弱指针类型。

于是,上面有问题的代码我们可以进行如下的修改:

@property (nonatomic, strong) NSMutableArray *blockArray;

...

- (void) someMethod {
__weak MyClass *weakSelf = self; [self.blockArray addObject:^{
[weakSelf doSomething];
}];
}

这样block这边保留的对于对象的指针就变成了弱类型的指针,循环引用被打破了!
这是一个常用的解决方案,必须要记住!!

Objective-C编程 - 关于Block的要点的更多相关文章

  1. OC高级编程——深入block,如何捕获变量,如何存储在堆上

    OC高级编程——深入block,如何捕获变量,如何存储在堆上   首先先看几道block相关的题目 这是一篇比较长的  博文 ,前部分是block的测试题目,中间是block的语法.特性,block讲 ...

  2. iOS开发技巧系列---使用链式编程和Block来实现UIAlertView

    UIAlertView是iOS开发过程中最常用的控件之一,是提醒用户做出选择最主要的工具.在iOS8及后来的系统中,苹果更推荐使用UIAlertController来代替UIAlertView.所以本 ...

  3. ios block常见的错误(三)——并发编程的block引用

    在一些技术型的企业里面,有关block面试笔试题,将会问得很深,如下例子: 请问DemoObj的对象能否正确释放,为什么? //DemoObj.m @interface DemoObj() @prop ...

  4. Objective-C( block的使用)

    block block用来保存一段代码 block的标志:^ block跟函数很像:可以保存代码.有返回值.有形参.调用方式跟调用方法一样 block内部可以访问外面的变量 默认情况下,block内部 ...

  5. 【转载】学习C++和编程的几个要点

    1.把C++当成一门新的语言学习(和C没啥关系!真的.):2.看<ThinkingIn C++>,不要看<C++变成死相>:3.看<The C++ Programming ...

  6. Block使用要点

    Block简介 Block其实包含两个部分内容 Block执行的代码,这是在编译的时候已经生成好的: 一个包含Block执行时需要的所有外部变量值的数据结构. Block将使用到的.作用域附近到的变量 ...

  7. Python并行编程的几个要点

    一.基于线程的并行编程 如何使用Python的线程模块 如何定义一个线程 如何探测一个线程 如何在一个子类中使用线程 Lock和RLock实现线程同步 信号实现线程同步 条件(condition)实现 ...

  8. Delphi编程中动态菜单要点归纳

      一.创建菜单并添加项目 在设计程序时,有时需要动态创建菜单, 通常使用以下的语句: PopupMenu1 := TPopupMenu.Create(Self);  Item := TMenuIte ...

  9. PHP编程20大效率要点

    1.如果能将类的方法定义成static,就尽量定义成static,它的速度会提升将近4倍. 2.$row[’id’] 的速度是$row[id]的7倍. 3.echo 比 print 快,并且使用ech ...

随机推荐

  1. .NET Out Of Memory Exception - Used 1.3GB but have 16GB installed

    I am getting an Out Of Memory exception in my c# application when the memory usage for the applicati ...

  2. 让DELPHI自带的richedit控件显示图片

    让DELPHI自带的richedit控件显示图片 unit RichEx; { 2005-03-04 LiChengbin Added: Insert bitmap or gif into RichE ...

  3. iReport数据库连接找不到驱动

  4. ASP.NET MVC:模块化/插件式架构实现(转载)

    I’ve recently spent quite a lot of time researching and prototyping different ways to create a plugi ...

  5. EditText中文文档

    感谢农民伯伯的翻译文:http://www.cnblogs.com/over140/archive/2010/09/02/1815439.html 属性名称 描述 android:autoLink 设 ...

  6. Eclipse with ADT的安装和配置

    我们从安卓官方网站(https://developer.android.com/sdk/index.html#download)下载下来的eclipse是捆绑好了ADT的,所以不用自己安装插件,十分方 ...

  7. IP组播

    1  IP组播基础 IP组播技术有效地解决了单点发送.多点接收的问题.组播源只发送一份数据,被传递的信息在距组播源尽可能远的网络节点才开始被复制和分发,并且只发送给需要该信息的接收者.  说明: 本章 ...

  8. 再看copy_on_write缩小临界区的例子

    本例子是模拟的读者写者问题,采用shared_ptr+写时拷贝实现,其中我觉得一个比较值得注意的地方是考虑到对象可能在临界区析构而将析构移除临界区,这对于多线程来说要多看多思. #include< ...

  9. 创想三维:5款最好用的免费3D建模软件【转】

    虽然网上有需要现成的免费三维模型,但对于许多人而言,3D打印机最吸引他们之处是可以设计创造完全属于自己的模型.问题是,现代专业级CAD软件大多价格高昂,例如Solidworks或Zbrush这样的程序 ...

  10. webAR涉及的技术【转】

    1.技术体系 1.1技术体系整理 其中绿色底色的代表Demo中表现出的能力比较成熟,可以直接应用. 脑图地址:http://naotu.baidu.com/file/3392a895a90397252 ...