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可以作为函数参数或者函数的返回值,而其本身又可以带输入 ...
随机推荐
- 我常用的Vi命令
Vi对于linux的重要性和受欢迎的程度在此一律不表.此刻互联网上不少介绍vi的文章和博客,相信写得比我好的也不在少数.然而为什么我依然写这样一篇文章呢?我对linux知识和了解也都来自于互联网,很难 ...
- System Error Codes
很明显,以下的文字来自微软MSDN 链接http://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx M ...
- day2
三级菜单: ))))))))))] last_levels.pop() ]]]]]]]]:] information = : ch = msvcrt.getch() ][][: : password= ...
- LeetCode 414 Third Maximum Number
Problem: Given a non-empty array of integers, return the third maximum number in this array. If it d ...
- iOS创建自定义的xib视图,不带控制器调用
1 我们平常使用视图都是在控制器中加载各种视图,但是有时候一个单独的视图,弄一个控制器未免有些显得太沉重了,所以我们现在来创建一个带xib加载视图的自定义视图 2 创建一个视图类集成uiview 3 ...
- 自动换行 lable换行 div换行 span换行 label换行
label word-break: break-all; white-space: normal;
- 卸载 ibus 使Ubuntu16.04任务栏与启动器消失 问题解决
经查证是unity误卸载了,我使用了命令: sudo apt-get remove --purge ibus 解决方法是: 使用以下命令:重置compiz: dconf reset -f /org/c ...
- Jetty使用教程(四:21-22)—Jetty开发指南
二十一.嵌入式开发 21.1 Jetty嵌入式开发HelloWorld 本章节将提供一些教程,通过Jetty API快速开发嵌入式代码 21.1.1 下载Jetty的jar包 Jetty目前已经把所有 ...
- 前台处理json字符串的几种方法(转)
原文地址http://www.css88.com/archives/3919 比如我有两个变量,我要将a转换成字符串,将b转换成JSON对象: var a={"name":&quo ...
- jQuery插件(多级菜单)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...