前言 :Block 和GCD 是 iOS 多线程 编程的核心 ,它们是 一并引入的 ,Block 是一种可以在 C C++,Objective-C 当中使用的 语法闭包。开发者 可以将 代码像对象 一样传递。 令其在不同的 环境 下运行。还有一个很关键,就是 Block 在定义的范围内 可以访问到其中全部变量。 GCD 基于 dipatch queue .开发者将 线程排入队列中,由GCD 负责处理所有的事宜,其中包括 适时的创建,复用,销毁线程。 而开发者 不需要去处理。 这一点 不同于 Java 开发,开发者 需要自己去管理 线程。
一 : 简单介绍 Block
/**
简单的 block
*/
- (void)simpleBlock
{
// 语法结构 return_type (^block_name) (parameters)
// 块其实就是个值 而且自有其相关类型与 int float Objective-C 对象一样
// 也可以把块赋给变量 然后像使用其他变量那样使用它
void (^someBlock)()=^{
NSLog(@"this is a void return block");
};
int (^addBlock)(int a,int b)=^(int a,int b){
return a+b;
};
int add=addBlock(2,5);
NSLog(@"add is %d",add); // 7
// block 强大之处在与声明它的范围里,所有变量都可以为其捕获
int addtional = 5;
int (^newAddBlock)(int a,int b)=^(int a,int b){
return a+b+addtional;
};
int newAdd=newAddBlock(2,5);
NSLog(@"newAdd is %d",newAdd); // 12
// __block 修饰符 这样可以在块内部修改 这个变量
// 苹果的这个方法 给我们 演示了 “内联块” 的用法
NSArray *array=@[@0,@1,@2,@3,@4,@4];
__block NSInteger count=0;
[array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj compare:@2] == NSOrderedAscending){
count++;
}
}];
NSLog(@"count is %ld",(long)count);
// count =2
}
#pragma mark ---- 使用typedef block
// 实际上 这种定义Block 类型的使用block 是我们主要使用block 的方式
typedef int (^EOCSomeBlock)(BOOL flag,int value);
- (void)typedefBlock
{
// 使用typedef 的block 有两个好处 一 可以在使用块的时候更易用 更易懂
// 二 如果 哪天要修改 那么 我们只需要修改 EOCSomeBlock 那么在所有之前调用过
// EOCSomeBlock 的地方 编译器都会给我们报错, 而如果我们写成 (int(^)(BOOL flag,int value))completion 那么很 有可能会忘记具体的地方。
}
// 对比下面这两个方法 哪个更好呢?
- (void)noTypedefBlock:(int(^)(BOOL flag,int value))completion
{
}
- (void)typedefBlock:(EOCSomeBlock )completion
{
}
#pragma mark ---- 全局块 栈块 以及 堆块
/**
栈 block
定义 block 的时候 其所占的内存区域是分配在栈中的。也就是说 block 只在定义它的那个范围内有效
下面这个段代码就有危险 ,因为可以通过编译 但是运行时而错误 时而正常 ,若编译器没有覆写待执行的代码
则正常 反之 崩溃。 因为我们并不知道 stack 上面的内存啥时候会回收覆写
*/
- (void)testStackBlock
{
void (^block)();
int i=arc4random()%2;
if (i==0) {
block=^{
NSLog(@" Block A i is %d",i);
};
}else{
block=^{
NSLog(@"Block B i is %d",i);
};
}
block();
}
/**
堆 Block
给block 对象发送 copy 消息后 会把 block 拷贝到 堆上面去了
对一个block 后续的copy 不会真的执行复制 只是递增引用计数 在ARC 环境下会自动释放
这样 代码就真正安全了
*/
- (void)testCopyBlock
{
void (^block)();
int i=arc4random()%2;
if (i==0) {
block=[^{
NSLog(@" Block A i is %d",i);
} copy];
}else{
block=[^{
NSLog(@"Block B i is %d",i);
} copy];
}
block();
}
// 这是一个全局 block
// 全局block 声明在全局内存里 ,不需要每次用到的时候在栈中创建
// 不会捕捉任何的外围的变量 对全局块的拷贝操作是个空操作。 实际相当于一个单例
void (^block)()=^{
NSLog(@"this is a block ");
};