Objc Block
一、__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的更多相关文章
- 数据结构--用Objective-C简单实现的数据结构:栈
前言:最近在学习数据结构,这里用Objective-C简单实现了一下栈.用Objective-C确实好容易,因为我使用了Cocoa框架提供了NSMutableArray作为存储元素的集合,操作集合元素 ...
- iOS 逆向之ARM汇编
最近对iOS逆向工程很感兴趣. 目前iOS逆向的书籍有: <Hacking and Securing IOS Applications>, <iOS Hacker's Handboo ...
- @weakify, @strongify ObjC的Block中使用weakSelf/strongSelf @weakify/@strongify
首先要说说什么时候使用weakSelf和strongSelf. 下面引用一篇博客<到底什么时候才需要在ObjC的Block中使用weakSelf/strongSelf>的内容: Objec ...
- ObjC的Block中使用weakSelf/strongSelf @weakify/@strongify
首先要说说什么时候使用weakSelf和strongSelf. 下面引用一篇博客<到底什么时候才需要在ObjC的Block中使用weakSelf/strongSelf>的内容: Objec ...
- 到底什么时候才需要在ObjC的Block中使用weakSelf/strongSelf
转载,原文: http://blog.lessfun.com/blog/2014/11/22/when-should-use-weakself-and-strongself-in-objc-block ...
- objc反汇编分析,block函数块为何物?
上一篇向大家介绍了__block变量的反汇编和它的伪代码,本篇函数块block,通常定义成原型(^){},它在反汇编中是什么东西. 我们先定义将要反汇编的例子,为减少篇幅例子采用non-arc环境. ...
- Block解析(iOS)
1. 操作系统中的栈和堆 我们先来看看一个由C/C++/OBJC编译的程序占用内存分布的结构: 栈区(stack):由系统自动分配,一般存放函数参数值.局部变量的值等.由编译器自动创建与释放.其操作方 ...
- 「iOS造轮子」之UIButton 用Block响应事件
俗语说 一个不懒的程序员不是好程序员 造轮子,也只是为了以后更好的coding. coding,简易明了的代码更是所有程序员都希望看到的 无论是看自己的代码,还是接手别人的代码 都希望一看都知道这代码 ...
- iOS传值之block传值(一)
ios4.0系统已开始支持block,在编程过程中,blocks被Obj-C看成是对象,它封装了一段代码,这段代码可以在任何时候执行.Blocks可以作为函数参数或者函数的返回值,而其本身又可以带输入 ...
随机推荐
- sys/types.h fcntl.h unistd.h sys/stat.h
sys/types.h 是Unix/Linux系统的基本系统数据类型的头文件,含有size_t,time_t,pid_t等类型. 在应用程序源文件中包含 <sys/types.h> 以访问 ...
- C/C++中无条件花括号的妙用
C/C++中无条件花括号可以形成一个代码块,一个作用域.可以使括号内定义的变量就只在本域(就是这个大括号)内有效,而且不会影响其他域,即使名字相同. 在花括号内,如果变量前面带类型,则相当于新创建一个 ...
- MyEclipse Project Migration功能中文简单介绍
前端时间,我对myEclispe的project Migration产生了疑问,也不知道是干啥用的.然后百度之,翻译结果是项目迁移,再次百度其他人对这个的经验,没想到百度到的没多少,甚至都没有说明这个 ...
- npm 安装不了模块
npm install 不能安装模块 先卸载npm,然后再重新安装 sudo npm uninstall npm -g 如果上面卸载不了,则进入到npm的安装目录(which npm): sudo m ...
- 使用Flexible实现手淘H5页面的终端适配
拿到设计师给的设计图之后,剩下的事情是前端开发人员的事了.而手淘经过多年的摸索和实战,总结了一套移动端适配的方案--flexible方案. 这种方案具体在实际开发中如何使用,暂时先卖个关子,在继续详细 ...
- cmd中编译java
cmd定位到.java文件所在位置: 注意.java文件名应与类名相同. javac xxx.java:编译(生成.class文件): java xxx:运行(执行.class文件): 若类间相互调用 ...
- SecureCRT 使用 rz 和 sz 命令
1.下载软件 rzsz-3.34.tar.gz,登录linux,用命令 wget http://freeware.sgi.com/source/rzsz/rzsz-3.48.tar.gz下载. 2.解 ...
- Hadoop运维
简单记录几个hdfs的运维命令 //查看hdfs的状态,是否有missing block,corrupt block等,也可以看datanode的状态 hdfs dfsadmin -report // ...
- Codeforces525E Anya and Cubes(双向搜索)
题目 Source http://codeforces.com/contest/525/problem/E Description Anya loves to fold and stick. Toda ...
- Web设计师值得收藏的10个jQuery特效
jQuery已经不是什么新鲜的事儿,以前总把它认为是非常难的东西,也就没有认真去了解他了.直到学完CSS的大部分内容,才开始接触这种"write less, do more" 的J ...