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可以作为函数参数或者函数的返回值,而其本身又可以带输入 ...
随机推荐
- C++库(Google Breakpad)
Google Breakpad是什么? 一个开源的多平台崩溃报告系统. Google breakpad是一个非常实用的跨平台的崩溃转储和分析模块,它支持Windows,Linux和Mac和Solari ...
- Ionic常用命令行解释
原文链接 安装ionic npm install -g ionic 更新www/lib/ionic 目录的文件,如有项目中有bower,此命令会运行bower update ionic, 否则则会从C ...
- iOS-OC-基本控件之UIPageControl
UIPageControl(页面控制器,就是桌面的那些小点点,每个点代表一个界面) 父类是 UIControl. iOS开发中常用的基本控件,主要和UIScrollView一起使用,比较常用的就是有些 ...
- 注解:【有连接表的】Hibernate双向1->N关联 (N端、1端同时参与控制关联关系)
Person与Address关联:双向1->N,[有连接表的],N端.1端同时参与控制关联关系 Person.java package org.crazyit.app.domain; impor ...
- 微信扫描打开APP下载链接提示代码优化(转)
上一次我发了一篇文章叫“微信打开网址添加在浏览器中打开提示”,里面我发出来了三个代码,分别是纯JS.js+html.jQuery+HTML代码.今天来一个简化版带可以关闭的按钮操作.使用的是纯JS+H ...
- MSSQ调优所需用的语句
看一下各项指标是否正常,是否有阻塞,这个语句选取了前10个最耗CPU时间的会话语句 [session_id], [request_id], [start_time] AS '开始时间', [statu ...
- Linux第02天
Linux 第02天 1.Linux磁盘和文件系统 VFS————虚拟文件系统 df命令————查看已挂载的分区 df 分区名 du命令————查看文件夹大小 du 文件夹名 ln命令————符号链接 ...
- jsp 中登录验证 注销 的模版
用户名密码验证模版 <%@page import="com.jerehedu.bao.User"%> <%@ page language="ja ...
- 00 MySQL
1数据库 1.1名词解释 DB:数据库Database,用于存放数据仓库 DBMS:数据库管理系统 DataBase Management System,管理数据库 ta ...
- XML序列化及反序列化
//对象序列化xml OutModel outmodel = new OutModel(); XmlSerializer serializer = new XmlSerializer(typeof(O ...