内容字号:默认大号超大号

段落设置:段首缩进取消段首缩进

字体设置:切换到微软雅黑切换到宋体

Blocks官方文档笔记:Blocks Programming Topics

2017-08-12 19:09 出处:清屏网 人气: 评论(0

block.png

前言

最近,我的前同事问了我一个问题,你知道 (global、malloc、stack)block 吗,什么是 (global、malloc、stack)block ,相信你在面试中或多或少遇到过一些Blocks的题目,一些简单,一些偏门,但是我认为脱离实际开发的面试题对于面试应聘者是完全无意义的,或许你知道 Blocks 的内存结构,你也知道 Blocksisa 在不同情况下指向的类,但这些仅适合拿来茶余饭后交谈的话题( 装B )罢了,因为你在半年一年之后自己都不清楚它是什么,只是再翻出来能很快顿悟,所以我不建议面试官将这些放在台面上讨论。

那么这篇文章写的将是实际开发所需要用到的,同时也是 Apple官方 给出的 Blocks 文档

关于 Block 对象的内存结构我不打算在本文讲解,你可以在 Block 实现的 源码 中去了解,但是从源码中可以论证一点 Blockisa 指针指向的类包含了一下几种:

_NSConcreteStackBlock 
_NSConcreteMallocBlock
_NSConcreteAutoBlock
_NSConcreteFinalizingBlock 
_NSConcreteGlobalBlock
_NSConcreteWeakBlockVariable

并不是很多博客上的文章及面试官口中所说的这三种:

_NSConcreteStackBlock 
_NSConcreteMallocBlock
_NSConcreteGlobalBlock

本文目录

一.Blocks概念、概述
二.Blocks申明、创建、使用
三.Blocks的变量类型(循环引用关系)、__block储存类型(本文重点)

一.Blocks概念、概述

闭包是一个函数(或指向函数的指针),再加上该函数执行的外部的上下文变量(有时候也称作自由变量)。 Block 实际上就是 Objective-C 语言对于闭包的实现。通常来说, Block 都是一些简短代码片段的封装,适用作工作单元,通常用来做并发任务、遍历、以及回调

Block 代表通常是小的,独立的代码片段。因此,它们特别有用,作为可以并发执行的工作单元或者集合中的项目封装的工具,或者当另一个操作完成时作为回调。

Block 是传统回调函数的有用替代,主要有两个原因:

  • 它们允许您在方法实现的上下文中执行的调用点编写代码,因此, Block 通常是框架方法的参数。
  • 它们允许访问局部变量,而不是使用需要一个体现您执行操作所需的所有上下文信息的数据结构的回调函数,您只需直接访问局部变量即可。

对于官方文档上的这两点有点绕口,理解为,可以在方法的实现中调用 Block ,同时可以访问局部变量,将这些信息回调到外部结构中去。

二.Blocks申明、创建、使用

2.1声明、创建

int multiplier = 7;
int (^ myBlock)(int) = ^(int num){
   return num * multiplier;
};

下例说明了该示例:

blocks.jpg

注意:

int (^ myBlock)(int) 表示申明了一个名为 myBlock 的Block指针来保存 ^(int num){return num * multiplier;} 对象,如果你对这句话不了解,你可以去看看我的 iOS 程序(APP)运行中的内存分配 这遍文章。

此处你可理解为 UIView *view = [UIView new]; 中的 UIView *view 为指向UIView类型的指针,而 [UIView new] 是生成的一个UIView对象。

此处将代码分成两部分:

  • int (^ myBlock)(int) Block的声明(可比着 UIView *view 看)
  • ^(int num){return num * multiplier;} Block的实现(可比着 [UIView new] 看)

2.2 Block使用

显而易见,有了上面的基础,我们就能够大胆的猜测Block的用法了。

  • 1.声明全局的Block。
  • 2.作为局部变量使用。
  • 3.用作属性或者成员变量。
  • 4.作为函数的参数传递。

1.声明全局的Block:

#import "ViewController.h"

/** 全局初始化Block */
void(^kGlobalBlock)(id , id ) = ^(id mayActionType, id parameters){
    NSLog(@"%@,%@",mayActionType,parameters);
};

/** 全局未初始化Block */
void(^kGlobalBlockWaitingInitinalize)(id , id );

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    [self golbalBlock];
}
#pragma mark - - 全局类型的 Block
- (void)golbalBlock{
    kGlobalBlock(@1,@"Bob");
    kGlobalBlockWaitingInitinalize = ^(id parameterOne, id parameterTwo){
        NSLog(@"parameterOne:%@ \n parameterOne:%@",parameterOne,parameterTwo);
    };
    
    kGlobalBlockWaitingInitinalize(@"Alice",@"Lucy");
    
}
@end

注意,调用Block时必须保证Block已经被初始化,因为此处为Block都在内部实现,Block都是已经初始化。 如果调用了一个未被初始化的Block ,程序将会崩溃。

两种验证Block是否初始化的方法

if (kGlobalBlockWaitingInitinalize) {
    kGlobalBlockWaitingInitinalize(@"Alice",@"Lucy");
}
!kGlobalBlockWaitingInitinalize ? : kGlobalBlockWaitingInitinalize(@"Alice",@"Lucy");

2.作为局部变量使用:

局部变量即在方法、函数中的Block

- (void)viewDidLoad {
    [super viewDidLoad];
    int multiplier = 7;
    int (^ myBlock)(int) = ^(int num){
       return num * multiplier;
    };
    myBlock(3);
}

3.用作属性或者成员变量:

#import "ViewController.h"

typedef NSString * (^BlocksTypeParameter)(id , id);
typedef NSString * (^BlocksTypeNonParameter)();
typedef void (^BlocksTypeNonReture)(id parameterOne, id parameterTwo);

@interface ViewController ()
/* 无参数类型 */
@property (nonatomic, copy) NSString *(^blockProperty)();
/* 有参数类型 */
@property (nonatomic, copy) NSString *(^blockPropertyWithParameters)(NSString *);
/* 无返回值类型 */
@property (nonatomic, copy) void(^blockPropertyWithNonRetern)();
@end


@implementation ViewController{
    /* 有参数类型 */
    BlocksTypeParameter _parameterBlock;
    /* 无参数类型 */
    BlocksTypeNonParameter _nonParemeterBlock;
    /* 无返回值类型 */
    BlocksTypeNonReture _nonRetureBlock ;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    [self propertyBlock];
}

#pragma mark - - 属性、成员变量类型的 Block
- (void)propertyBlock{
    NSString *name = @"Bob";
    // 无参数类型
    _blockProperty = ^{
        NSLog(@"no parameters");
        return name;
    };
    
    // 有参数类型
    _blockPropertyWithParameters = ^(NSString *parameter){
        NSLog(@"has parameters");
        return parameter;
    };
    
    // 无返回值类型
    _blockPropertyWithNonRetern = ^{
        NSLog(@"non retern");
    };
}

4.作为函数的参数传递

- (void)viewDidLoad {
    [super viewDidLoad];
    [self postNetWorkWithParameters:@"parameters" success:^(id response) {
        NSLog(@"%@",response);
    } failure:^(id error) {
        NSLog(@"%@",error);
    }];
}
- (void)postNetWorkWithParameters:(id)parameters
                          success:(void (^)(id response))finishBlock
                          failure:(void (^)(id error))failure{
    finishBlock(@"success");
    failure(@"failure");
}

三.Blocks的变量类型(循环引用关系)、__block储存类型(本文重点)

如果你还在为使用Block照成循环引用而感到苦恼,那么看完这里将会解开你的疑惑

首先来说说官方文档上关于Block可使用的变量归为5类

1.Global variables are accessible, including static variables that exist within the enclosing lexical scope.

2.Parameters passed to the block are accessible (just like parameters to a function).

3.Stack (non-static) variables local to the enclosing lexical scope are captured as const variables. Their values are taken at the point of the block expression within the program. In nested blocks, the value is captured from the nearest enclosing scope.

4.Variables local to the enclosing lexical scope declared with the __block storage modifier are provided by reference and so are mutable. Any changes are reflected in the enclosing lexical scope, including any other blocks defined within the same enclosing lexical scope. These are discussed in more detail in The __block Storage Type .

5.Local variables declared within the lexical scope of the block, which behave exactly like local variables in a function. Each invocation of the block provides a new copy of that variable. These variables can in turn be used as const or by-reference variables in blocks enclosed within the block.

即:

1.全局变量

2.传递给Block的参数

3.方法中局部变量非__block 捕获变量

4.方法中__block 语法修饰的变量

5.Block内声明的变量

3.1 关于__block存储类型:

您可以通过应用__block存储类型修饰符来指定导入的变量是可变的,即读写。

__block变量存储在变量的作用域范围中,以及在变量的作用域中声明或创建的所有 BlockBlock Copy 之间共享。因此,如果在框架中声明的Block的 Copies of the blocks 都能在框架的末尾(例如,通过在某个地方排队等待执行),那么存储将会在堆栈的销毁中存活下来。在给定作用域范围内的多个Block可以同时使用一个共享变量。

作为优化,Block存储从堆栈开始,就像Block本身一样。如果这个Block是用 block copy 复制的,变量会被复制到堆中。因此, __block 变量的地址可以随时间变化。

对于 __block 变量还有两个进一步的限制:它们不能是 variable length arrays ,也不能是包含 C99 variable length arrays 的结构。

3.2 关于循环引用

如果您在方法的实现中使用了一个Block,当这个Block 被复制时,它会对Block中使用的对象的进行强引用:

那么关于循环引用的造成的原因必然是,在 Block Copy 到堆中时Block强引用了这个对象,然后对象又存在强引用Block的情况下就会造成强引用。以下是解决方案:

__weak typeof(self) weakSelf = self;
    self.block = ^{
        __strong typeof(self) strongSelf = weakSelf;
    };

那么对于方法、函数中的Block,只存在Block对 对象进行强引用,不存在对象对Block强引用,此时的Block作为参数先是保存在栈中,然后 被Copy到堆中。

分享给小伙伴们:
本文标签: BlocksProgrammingTopics

相关文章

发表评论愿您的每句评论,都能给大家的生活添色彩,带来共鸣,带来思索,带来快乐。

CopyRight © 2015-2016 QingPingShan.com , All Rights Reserved.

清屏网 版权所有 豫ICP备15026204号