iOS之Block详解:Block详解
ViewController.h(ARC)
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
// 属性声明的block都是全局的__NSGlobalBlock__
@property (nonatomic, copy) void (^copyBlock)();
@property (nonatomic, weak) void (^weakBlock)();
@end
ViewController.m(ARC)
#import "ViewController.h"
#import "Test.h"
typedef void (^blockSave)(void);
typedef void (^typedefBlock)(void);
void (^outFuncBlock)(void) = ^{
NSLog(@"someBlock");
};
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"block : %@", ^{NSLog(@"block");}); // __NSGlobalBlock__
NSString *str3 = @"1234";
NSLog(@"block is %@", ^{NSLog(@":%@", str3);}); // __NSStackBlock__
#pragma mark - 当全局block引用了外部变量,ARC机制优化会将Global的block,转为Malloc(堆)的block进行调用。
__block int age = 20;
int *ptr = &age;
// ARC下
blockSave x = ^{
NSLog(@"(++age):%d", ++age); // 变量前不加__block的情况下,会报错,变量的值只能获取,不能更改
};
blockSave y = [x copy];
y();
NSLog(@"x():%@, y():%@ , (*ptr):%d", x, y, *ptr);
// MRC下
Test *test = [[Test alloc] init];
[test test];
[test exampleB];
/**总结:
ARC下:(++age):21 (*ptr):20 // blockSave在堆中,*ptr在栈中
MRC下:(++age):21 (*ptr):21 // blockSave和*ptr都在栈中
*/
#pragma mark - copyBlock(未使用函数内变量) __NSGlobalBlock__
self.copyBlock = ^{
};
NSLog(@"1:%@", self.copyBlock);
#pragma mark - weakBlock(未使用函数内变量) __NSGlobalBlock__
self.weakBlock = ^{
};
NSLog(@"2:%@", self.weakBlock);
#pragma mark - copyBlock (使用函数内变量) __NSMallocBlock__
self.copyBlock = ^{
age = age+1-1;
};
NSLog(@"3:%@", self.copyBlock);
#pragma mark - weakBlock(使用函数内变量) __NSStackBlock__
self.weakBlock = ^{
age = age+1-1;
};
NSLog(@"4:%@", self.weakBlock);
#pragma mark - someBlock(定义在函数体外) __NSGlobalBlock__
NSLog(@"5:%@", outFuncBlock);
#pragma mark - typedefBlock(函数体外自定义的Block) __NSGlobalBlock__
typedefBlock b = ^{
};
NSLog(@"6:%@", b);
#pragma mark - 对栈中的block进行copy
// 不引用外部变量,定义在全局区、表达式没有使用到外部变量时,生成的block都是__NSGlobalBlock__类型
void (^testBlock1)() = ^(){
};
NSLog(@"testBlock1: %@", testBlock1);
// 引用外部变量 -- ARC下默认对block进行了copy操作,所以这里是__NSMallocBlock__类型
void (^testBlock2)() = ^(){
age = age+1-1;
};
NSLog(@"testBlock2: %@", testBlock2);
// Blocks提供了将Block和__block变量从栈上复制到堆上的方法来解决变量作用域结束时销毁的问题,堆上的Block会依然存在。
/*那么什么时候栈上的Block会复制到堆上呢?
1.调用Block的copy实例方法时
2.Block作为函数返回值返回时(作为参数则不会)
3.将Block赋值给附有__strong修饰符id类型的类或Block类型成员变量时
4.将方法名中含有usingBlock的Cocoa框架方法或GCD的API中传递Block时
在使用__block变量的Block从栈上复制到堆上时,__block变量也被从栈复制到堆上并被Block所持有。
*/
/*block里面使用self会造成循环引用吗?
1.很显然答案不都是,有些情况下是可以直接使用self的,比如调用系统的方法:
[UIView animateWithDuration:0.5 animations:^{
NSLog(@"%@", self);
}];
因为这个block存在于静态方法中,虽然block对self强引用着,但是self却不持有这个静态方法,所以完全可以在block内部使用self。
2.当block不是self的属性时,self并不持有这个block,所以也不存在循环引用
void(^block)(void) = ^() {
NSLog(@"%@", self);
};
block();
3.大部分GCD方法:
dispatch_async(dispatch_get_main_queue(), ^{
[self doSomething];
});
因为self并没有对GCD的block进行持有,没有形成循环引用。
4.……
只要我们抓住循环引用的本质,就不难理解这些东西。
*/
}
Test类在MRC条件下运行( -fno-objc-arc )
Test.h(MRC)
#import <Foundation/Foundation.h>
@interface Test : NSObject
- (void)test;
@end
Test.m(MRC)
#import "Test.h"
@implementation Test
- (void)test
{
__block int age = 20;
int *ptr = &age;
void (^textBlock)() = ^{
NSLog(@"(++age):%d", ++age);
};
textBlock();
// 对block进行retain、release、copy,retainCount都不会变化,都为1
// [textBlock retain];
// [textBlock copy];
// [textBlock release];
NSLog(@"Test: textBlock:%@, (*ptr):%d, %lu", textBlock, *ptr, (unsigned long)[textBlock retainCount]);
/**
MRC下:(++age):21 (*ptr):21
*/
#pragma mark - 对栈中的block进行copy
// 不引用外部变量
/* 这里打印的是__NSGlobalBlock__类型,但是通过clang改写的底层代码指向的是栈区:impl.isa = &_NSConcreteStackBlock
这里引用巧神的一段话:由于 clang 改写的具体实现方式和 LLVM 不太一样,并且这里没有开启 ARC。所以这里我们看到 isa 指向的还是__NSStackBlock__。但在 LLVM 的实现中,开启 ARC 时,block 应该是 __NSGlobalBlock__ 类型
*/
void (^testBlock1)() = ^(){
};
void (^testBlock2)() = [testBlock1 copy];
NSLog(@"Test: testBlock1: %@, testBlock2: %@", testBlock1, testBlock2);
[testBlock2 release];
// 引用外部变量,block为__NSStackBlock__类型
void (^testBlock3)() = ^(){
age = age+1-1;
};
void (^testBlock4)() = [testBlock3 copy];
NSLog(@"Test: testBlock3: %@, testBlock4: %@", testBlock3, testBlock4);
[testBlock4 release];
}
github地址:BlockTest