ref1

一、__block 的使用

  说明:

    在 block 内只能读取在同一个作用域的变数而且没有办法修改在 block 外定义的任何变数,此时若我们想要这些变数能够在 block 中被修改,就必须在前面加上 __block 的修饰词,否则编辑时就会产生错误。

    在编译的时候,同一作用域的变量的值会被 copy 下来,即使在接下的语句中修改变量的值,也不会对 block 里面有影响。如果需要 block 运行的时候取的是变量当前的值,则在变量声明的时候加上 __block。

  1) 基本类型局部变量, 基本类型静态变量,基本类型全局变量在 block 中的使用

    gobalVariable = 50;
staticVariable = 100;
NSInteger localVariable1 = 10;
__block NSInteger localVariable2 = 20; void (^ABlock)(void) = ^(void) {
// 50
NSLog(@"src gobalVariable = %d", gobalVariable); // 100
NSLog(@"src staticVariable = %d", staticVariable); // 10
NSLog(@"src localVariable1 = %d", localVariable1); // 21
NSLog(@"src localVariable2 = %d", localVariable2); ++gobalVariable; // 可读可写
++staticVariable; // 可读可写 // ++localVariable1; // 只可读,所以这里尝试改变变量的值会发生编译错误
++localVariable2; // 可读可写 // 51
NSLog(@"des gobalVariable = %d", gobalVariable); // 101
NSLog(@"des staticVariable = %d", staticVariable); // 10
NSLog(@"des localVariable1 = %d", localVariable1); // 22
NSLog(@"des localVariable2 = %d", localVariable2);
};
++localVariable1; // 不会影响 block 中的值
++localVariable2; // 会影响 block 中的值 ABlock();

  2) Object类型局部变量, Object类型静态变量,Object类型全局变量, block类型变量在 block 中的使用

    a> MRC 下

xxx.h
#import <Foundation/Foundation.h> @interface BlockMemoryTestClass : NSObject {
@private
NSObject* instanceVariable;
} - (void)testMethod; @end xxx.m
#import "BlockMemoryTestClass.h" @implementation BlockMemoryTestClass static NSObject* staticVariable = nil;
NSObject* gobalVariable = nil; - (id)init {
self = [super init];
if (self) {
instanceVariable = [[NSObject alloc] init];
staticVariable = [[NSObject alloc] init];
gobalVariable = [[NSObject alloc] init];
}
return self;
} - (void)testMethod {
NSObject* localVariable = [[NSObject alloc] init];
__block NSObject* blockVariable = [[NSObject alloc] init]; typedef void (^ABlock)(void);
ABlock block = ^(void) {
NSLog(@"instanceVariable %@", instanceVariable);
NSLog(@"staticVariable %@", staticVariable);
NSLog(@"gobalVariable %@", gobalVariable);
NSLog(@"localVariable %@", localVariable);
NSLog(@"blockVariable %@", blockVariable);
}; // block = [[block copy] autorelease];
block(); NSLog(@"instanceVariable %d", [instanceVariable retainCount]);
NSLog(@"staticVariable %d", [staticVariable retainCount]);
NSLog(@"gobalVariable %d", [gobalVariable retainCount]);
NSLog(@"localVariable %d", [localVariable retainCount]);
NSLog(@"blockVariable %d", [blockVariable retainCount]);
NSLog(@"self %d", [self retainCount]);
} - (void)dealloc {
[super dealloc];
}

  输出结果:

/*
不含 block = [[block copy] autorelease];, 原因是不执行 [block copy],block 的类型是 NSStackBlock 存放在栈内存,执行了 [block copy] 变为 NSMallocBlock 存放在堆内存。
instanceVariable 1
staticVariable 1
gobalVariable 1
localVariable 1
blockVariable 1
self 1
*/ /*
含有 block = [[block copy] autorelease];
instanceVariable 1
staticVariable 1
gobalVariable 1
localVariable 2
blockVariable 1
self 2 (原因是 instanceVariable 的使用导致)
*/

  

    b> ARC 下

xxx.h
#import <Foundation/Foundation.h> @interface BlockMemoryTestClass : NSObject {
@private
NSString* instanceString;
} - (void)testInstanceVariable;
- (void)testGobalVariable;
- (void)testStaticVariable;
- (void)testLocalVariable;
- (void)testBlockVariable;
- (void)testWeakVariable; @end xxx.m
#import "BlockMemoryTestClass.h" @implementation BlockMemoryTestClass NSString* gobalString = nil; - (void)testInstanceVariable {
instanceString = @"123"; NSLog(@"instanceString is %@", instanceString);
NSLog(@"instanceString address %p", &instanceString); void(^ABlock)(void) = ^(void) {
NSLog(@"instanceString is %@", instanceString);
NSLog(@"instanceString address %p", &instanceString);
}; instanceString = nil;
ABlock();
} - (void)testGobalVariable {
gobalString = @"123"; NSLog(@"gobalString is :%@", gobalString);
NSLog(@"gobalString address %p", &gobalString); void(^ABlock)(void) = ^(void) {
NSLog(@"gobalString is :%@", gobalString);
NSLog(@"gobalString address %p", &gobalString);
}; gobalString = nil;
ABlock();
} - (void)testStaticVariable {
static NSString* staticString = nil;
staticString = @"123"; NSLog(@"staticString is %@", staticString);
NSLog(@"staticString address %p", &staticString); void(^ABlock)(void) = ^(void) {
NSLog(@"staticString is %@", staticString);
NSLog(@"staticString address %p", &staticString);
}; staticString = nil;
ABlock();
} - (void)testLocalVariable {
NSString* localString = @"123"; NSLog(@"localString is %@", localString);
NSLog(@"localString address %p", &localString); void(^ABlock)(void) = ^(void) {
NSLog(@"localString is %@", localString);
NSLog(@"localString address %p", &localString);
}; localString = nil;
ABlock();
} - (void)testBlockVariable {
__block NSString* blockString = @"123"; NSLog(@"blockString is %@", blockString);
NSLog(@"blockString address %p", &blockString); void(^ABlock)(void) = ^(void) {
NSLog(@"blockString is %@", blockString);
NSLog(@"blockString address %p", &blockString);
}; blockString = nil;
ABlock();
} - (void)testWeakVariable {
NSString* localString = @"123";
__weak NSString* weakString = localString; NSLog(@"weakString is %@", weakString);
NSLog(@"weakString address %p", &weakString);
NSLog(@"weakString str address %p", weakString); // 注意这里的 weak 存放的是 localString 的地址 void(^ABlock)(void) = ^(void) {
NSLog(@"weakString is %@", weakString);
NSLog(@"weakString address %p", &weakString);
NSLog(@"weakString str address %p", weakString);
}; localString = nil;
ABlock();
} @end

  运行结果:


二、block 的声明

  1) 声明一个 block 函数   “返回值类型 (^block名字)(传入参数)”

// 无参数无返回的 block 声明
void (^ FirstBlock)(void); // 有参数又反回的 block 声明
int (^ SecondBlock)(int, char); // 含有 10 个 block 的阵列 声明
void (^ BlockArray[10])(int);

  2) 声明一个参数是 block 类型的方法  “返回类型 (^)(传入参数))block形参名

- (void)methodWithBlock:(void (^)(void))block {

}

  3) 声明一个带 block 体的 block 变量  "返回值类型 (^block名字)(传入参数)= ^(传入参数) { };"

void (^BlockVariable)(NSInteger, NSInteger) = ^(NSInteger a, NSInteger b) {
NSLog(@"a = %d", a);
NSLog(@"b = %d", b);
};

  

 


三、arc下的 block 的内存管理问题。

  1) 根据 block 在内存的位置分为 3 类:

    a> NSGobalBlock: block 实现体里面没有引用外部变量

    void (^BlockVariable)(NSInteger, NSInteger) = ^(NSInteger a, NSInteger b) {
NSLog(@"a = %d", a);
NSLog(@"b = %d", b);
}; NSLog(@"%@", BlockVariable); // <__NSGlobalBlock__: 0x4060>

  

    b> NSStackBlock: 位于栈内存,当函数返回时 block 无效

    NSArray* localVariable = @[@(1), @(2), @(3)];

    NSLog(@"Block is %@", ^(void){
NSLog(@"localVariable = %@", localVariable);
});

  

    c> NSMallocBlock: 位于堆内存

    void (^BlockVariable)(void) = ^(void) {
NSLog(@"localVariable = %@", localVariable);
}; NSLog(@"%@", BlockVariable); // <__NSMallocBlock__: 0x8c55e20>

  

  2) block 的 copy,retain, release 操作

    a> NSStackBlock 进行 copy 操作即可变为 NSMallocBlock

    b> copy,retain, release 对 block 的 reatain count 没有影响,block 的 retain count 始终是 1.

    c> 对 NSGobalBlock 进行 copy, reatain, release 无效

    d> 对NSStackBlock 进行 retain, release 无效,

       EX:MRC 方法中如果返回 mutableArray,操作[mutableArray addObject:stackBlock]; 是错误的,原因是方法结束 stackBlock 会被释放,应该改为 [mutableArray addObject:[stackBlock copy]]; 先将 block 从栈内存复制到堆内存上, 这会 retain 其引用的外部变量。所以 block 中如果引用了他的宿主对象,那很可能引起循环引用。

          ARC 方法中如果返回 mutableArray,操作[mutableArray addObject:block]; 是没有问题的,因为 arc 下实例化的 block 是 NSMallocBlock 存放于堆内存。

    e> 对 NSMallocBlock 进行 retain,copy, release 是有效的。reetain count 始终是 1,但是 reference count 会改变。copy 不会生成新 block 对象。

    f> 尽量不要对 block 进行 retain 操作。

  3) 循环引用的问题 (arc 环境下)

    EX_1

xxx.h
#import <Foundation/Foundation.h> typedef void (^ABlcok)(void); @interface BlockCycleTestClass : NSObject @property(nonatomic, strong) ABlcok aBlock; @end xxx.m
#import "BlockCycleTestClass.h" @implementation BlockCycleTestClass - (id)init {
self = [super init];
if (self) { // 循环引用: 前提是 self 必须是 strong 引用 block 否则不会造成 retain cycle, 如果 block 的修饰变为 assign 不会引起 retain cycle。
self.aBlock = ^(void) {
[self doSomething];
}; // 循环引用: 前提是 self 必须是 strong 引用 block 否则不会造成 retain cycle, 如果 block 的修饰变为 assign 不会引起 retain cycle。
__block BlockCycleTestClass* blockSelf = self;
self.aBlock = ^(void) {
[blockSelf doSomething];
}; // 不会循环引用
__weak BlockCycleTestClass* weakSelf = self;
self.aBlock = ^(void) {
[weakSelf doSomething];
}; // 不会循环引用
ABlcok anotherBlock = ^(void) {
[self doSomething];
};
anotherBlock(); }
return self;
} - (void)doSomething {
NSLog(@"do something");
} - (void)dealloc {
NSLog(@"no cycle retain...");
} @end

    EX_2:

#import <UIKit/UIKit.h>

typedef void (^AnotherBlock)(void);

@interface AppDelegate : UIResponder <UIApplicationDelegate> {
@private
AnotherBlock instanceBlock;
} @property (strong, nonatomic) UIWindow *window; @end #import "AppDelegate.h" @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor]; NSLog(@"Self retain count is: %ld", CFGetRetainCount((__bridge CFTypeRef)self));
[self test];
NSLog(@"Self retain count is: %ld", CFGetRetainCount((__bridge CFTypeRef)self)); [self.window makeKeyAndVisible];
return YES;
} -(void)test {
// 输出结果: 1, 3, 2,3 的原因: self自身,stack block一份,malloc block一份。(循环引用)
instanceBlock = ^(void) {
NSLog(@"%p", self);
};
NSLog(@"Self retain count is: %ld", CFGetRetainCount((__bridge CFTypeRef)self)); // 输出结果: 1, 1, 1
__weak AppDelegate* weakSelf = self;
instanceBlock = ^(void) {
NSLog(@"%p", weakSelf);
}; // 输出结果: 1, 3, 1
AnotherBlock block = ^(void) {
NSLog(@"%p", self);
};
}

  


四、MRC 下使用 block 需要注意的事项:

  1) 曾经遇到这种情况, 这里必须要设置 weakSelf = nil; 否则内存溢出。

- (void)method1 {
__block __unsafe_unretained MyClassName* weakSelf = self;
[self method2:^{
NSLog(@"++++++++ %@ %d", weakSelf, weakSelf.retainCount);
weakSelf = nil;
}]; } - (void)method2:(void(^)(void))completedBlock {
ASIHTTPRequest* reqeust = ...;
[request setCompletionBlock:^{
if (completedBlock) completedBlock();
}];
}

  

  

Objc Block的更多相关文章

  1. 数据结构--用Objective-C简单实现的数据结构:栈

    前言:最近在学习数据结构,这里用Objective-C简单实现了一下栈.用Objective-C确实好容易,因为我使用了Cocoa框架提供了NSMutableArray作为存储元素的集合,操作集合元素 ...

  2. iOS 逆向之ARM汇编

    最近对iOS逆向工程很感兴趣. 目前iOS逆向的书籍有: <Hacking and Securing IOS Applications>, <iOS Hacker's Handboo ...

  3. @weakify, @strongify ObjC的Block中使用weakSelf/strongSelf @weakify/@strongify

    首先要说说什么时候使用weakSelf和strongSelf. 下面引用一篇博客<到底什么时候才需要在ObjC的Block中使用weakSelf/strongSelf>的内容: Objec ...

  4. ObjC的Block中使用weakSelf/strongSelf @weakify/@strongify

    首先要说说什么时候使用weakSelf和strongSelf. 下面引用一篇博客<到底什么时候才需要在ObjC的Block中使用weakSelf/strongSelf>的内容: Objec ...

  5. 到底什么时候才需要在ObjC的Block中使用weakSelf/strongSelf

    转载,原文: http://blog.lessfun.com/blog/2014/11/22/when-should-use-weakself-and-strongself-in-objc-block ...

  6. objc反汇编分析,block函数块为何物?

    上一篇向大家介绍了__block变量的反汇编和它的伪代码,本篇函数块block,通常定义成原型(^){},它在反汇编中是什么东西. 我们先定义将要反汇编的例子,为减少篇幅例子采用non-arc环境. ...

  7. Block解析(iOS)

    1. 操作系统中的栈和堆 我们先来看看一个由C/C++/OBJC编译的程序占用内存分布的结构: 栈区(stack):由系统自动分配,一般存放函数参数值.局部变量的值等.由编译器自动创建与释放.其操作方 ...

  8. 「iOS造轮子」之UIButton 用Block响应事件

    俗语说 一个不懒的程序员不是好程序员 造轮子,也只是为了以后更好的coding. coding,简易明了的代码更是所有程序员都希望看到的 无论是看自己的代码,还是接手别人的代码 都希望一看都知道这代码 ...

  9. iOS传值之block传值(一)

    ios4.0系统已开始支持block,在编程过程中,blocks被Obj-C看成是对象,它封装了一段代码,这段代码可以在任何时候执行.Blocks可以作为函数参数或者函数的返回值,而其本身又可以带输入 ...

随机推荐

  1. Python 开发轻量级爬虫08

    Python 开发轻量级爬虫 (imooc总结08--爬虫实例--分析目标) 怎么开发一个爬虫?开发一个爬虫包含哪些步骤呢? 1.确定要抓取得目标,即抓取哪些网站的哪些网页的哪部分数据. 本实例确定抓 ...

  2. 提额 APP

    提额 APP 开始参与这个APP也是巧合,一个月之前,我还在忙于电信运营商的工作,上级就过来问我在之前公司有没有用过 html css js这些.在维库的时候,可是从前台到后台都是要全包的呀,因为项目 ...

  3. PHP 基础(赋值及函数)

    开端<?php>结尾</php> 弱类型语言  定义变量的时候 不需要 声明   但是 每一个变量前   都必须  加$ 符号 储存文件按  统一放到 安装文件夹下面的  WA ...

  4. 1.UI初认识

    前节:app是什么? app英文全称:application 应用程序,简称应用.也就是手机应用的简写 出处:http://www.cnblogs.com/mcj-coding/p/5098254.h ...

  5. 16. 3Sum Closest

    题目: Given an array S of n integers, find three integers in S such that the sum is closest to a given ...

  6. (转)CDN——到底用还是不用?

    用CDN的七个理由 浏览器从服务器上下载css.js和图片等文件时都要和服务器连接,而大部分浏览器对同一个域名用于下载文件的并发连接数限制在4个,这意味着如果要下载第五个文件就必须等前四个文件中有一个 ...

  7. HTML登录注册界面怎么制作?

    在没有学习CSS样式的前提下,是如何做一个简单的注册界面的. 一.表单标签(form) 首先我们先写一个<form></form>的标签,form标签属于表单标签,通常我们的登 ...

  8. 计算机网络中的帧封装(C实现)

    这段时间开始复习计算机网络,看到帧封装这一节,结合以前的课程设计,就用C写了个帧封装的程序,说实话C学的确实不怎么样,实现的时候对于文件操作那部分查了好多资料,下面说说帧封装是啥情况. 学过计算机网络 ...

  9. 【C#】MVC项目中搭建WebSocket服务器

    前言 因为项目需要,前端页面中需要不断向后台请求获取一个及一个以上的状态值.最初的方案是为每个状态值请求都建立一个定时器循环定时发起Ajax请求,结果显而 易见.在HTTP1.1协议中,同一客户端浏览 ...

  10. 图文介绍如何在Eclipse统计代码行数

    使用Eclipse可以方便的统计工程或文件的代码行数,方法如下: 1.点击要统计的项目或许文件夹,在菜单栏点击Search,然后点击File... 2.选中正则表达式(Regular expressi ...