一、__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();
}];
}