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(); }]; }
posted on 2014-07-25 09:37 EileenLeung 阅读(342) 评论(0) 编辑 收藏 举报