block & 链式编程

0 第一性原理

  1. 函数引用
  2. 匿名函数,能自动捕获外部变量。
  3. 赋值操做,会把栈上的block copy到堆上。
  4. 可用于异步/同步,用于同步时,有些像模板模式。(本身的理解)
  5. 函数调用中,不要把,做为实参的block的block体,和本次函数调用,搞混了。

1 用法理解

1.1 做为属性,能够传递代码。

每每也是1.2用法中,函数接收方中的用法。java

1.2 做为函数入参,能够将业务逻辑写在一块儿,代码结构紧凑。

AFN中response的代码写在block中,能够把业务逻辑写在一块儿。objective-c

响应式编程,AFN中,发送请求具体实现是AFN作的,何时返回也不肯定。 这里用通知、delegate都会比block复杂。sql

1.3 做为返回值,可用于链式编程(函数式中常见、Masonry)

oc中链式的本质编程

  1. 借助了Getter的方式
  2. 返回block,()是执行block,至关于调用了一个函数。
//Human.h
- (Human *(^)(int))run;

//Human.m
- (Human *(^)(int))run {
    return ^Human *(int m) {
        NSLog(@"%d", m);
        return self;
    };
}

//invoke somewhere
Human *human = [Human new];
Human *(^block)(int) = human.run;
block(10);
//合起来
human.run(10);
复制代码

2 捕获变量的原理和__block

所谓捕获,就是指,把栈上变量的值copy到堆上。bash

  1. 局部/全局静态变量、全局变量,不在栈上,因此无需捕获。(这里局部指当前方法,全局指.m文件)
  2. 对于成员变量,会对self作强引用,也无需捕获。
  3. 对于局部变量。 基本类型变量,直接copy值到堆上。 对象类型变量,copy对象引用的值(地址)到堆上。 因此,这个值不能被修改。(但,对象属性的值,如human.age,能够修改)

4. __block则让 局部变量create在堆上,block捕获时直接强引用。

3 block内存分配

  1. 没有外部变量的时候,就是一个GlobalBlock
  2. 自动捕获外部变量的时候, 由于外部变量是在栈上的,因此block变成了栈上的block 若是进行了=赋值,会执行一个copy操做,变成了堆上的block

ARC下写copy仍是strong,都会copy,但最好仍是写copy提醒本身和别人。网络

//全局block
    NSLog(@"globalBlock2, %@", ^{
        NSLog(@"globalBlock");
    });
    
    //栈block, 使用了外部变量 -> 栈block
    int i = 2;
    NSLog(@"stackBlcok, %@", ^{
        NSLog(@"stackBlcok, %d", i);
    });
    
    //堆block, 栈block,进行了赋值操做 -> 堆block
    void (^heapBlock) (void) = ^{
        NSLog(@"heapBlock, %d", i);
    };
    NSLog(@"heapBlock, %@", heapBlock);
    heapBlock();
复制代码

3.1 深刻理解block 与 copy

//对于已经在堆上的block,赋值操做不会再进行copy,[block copy]后,仍然是原地址
    //也就是说,block的copy已经很是的自动化,显示的写 [block copy],已经彻底没必要要了
    void (^heapBlock) (void) = ^{
        NSLog(@"heapBlock, %d", i);
    };
    NSLog(@"heapBlock, %@", heapBlock);
    heapBlock();
    
    self.globalBlock = heapBlock;
    NSLog(@"self.globalBlock, %@", self.globalBlock);
    self.globalBlock = [heapBlock copy];
    NSLog(@"self.globalBlock, %@", self.globalBlock);
复制代码

4 声明和定义

三种方式中,保持一致的地方:入参的格式,返回类型都不带括号。 语法比较丑,多写几回才能练到手熟,也能够用下面的快捷方式。session

//Xcode中,输入inline,会有提示的block定义出来
复制代码

4.1 typed & 属性

//1. 返回类型不带括号
//2. block名、参数集带括号
//3. 参数集相似Java
typedef void (^TrueFalseCallback)(BOOL success);
typedef NSString * _Nonnull (^ABlock)(UIImage * _Nullable image);
复制代码
@property (nonatomic, copy) void (^block)(void);
复制代码

4.2 形参

//1. 形似typed声明的总体,放在一个括号内,做为类型
//2. block名右移做为形参
//FMDatabaseQueue.m
- (void)inDatabase:(void (^)(FMDatabase *db))block {
//block是参数,这里是函数体,不是block的定义。
        FMDatabase *db = [self database];
        block(db);
//...
}
复制代码

4.3 定义(也是容易看晕的地方)

  1. 等号右边,^返回类型(java形式的入参){block代码块}
  2. 把^和返回值换位置,同时去掉block名,并把^左移(以便编译器编译?)
  3. 定义时,不能用typedof定义的类型名,由于参数看不到。

/**
RAC中的一个定义
1. 参数是一个 返回值为RACSignal,参数为id的block
2. block内容没有,直接返回了一个RACSignal
3. RACSignal的参数是一个 返回值为RACDisposable,参数为id<RACSubscriber>的block
4. 这个block 是返回了一个nil
*/
[[RACCommand alloc]initWithSignalBlock:^RACSignal * _Nonnull(id _Nullable input) {
            return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
                [self shuwen:input subscriber:subscriber];
                return nil;
            }];
        }];
复制代码

5-1中,有两个定义的实例框架

5 糊涂过的地方

5-1 第三方框架中,部分block的入参是谁提供的?

简单说,异步

函数调用中,不要把,做为实参的block的block体,和本次函数调用,搞混了。async

  1. block是传过去的,对方想何时调用,就何时调用,调用的时候,他会按照block的参数设定去传值.

  2. 大多数状况下,能够经过函数名,看出来,对方大概会在何时调用这个block。

先看这个

//入参中的db,由databaseQueue提供
[[HHBaseSharedDBPersistence databaseQueue] inDatabase:^(FMDatabase *db) {
        FMResultSet *resultSet = [db executeQuery:self.sqlString];
        if ([resultSet next]) {
            dbVersion = [resultSet intForColumn:TABLE_NAME_META_COLUMN];
        }
        [resultSet close];
    }];
复制代码

再看看网络调用中:

AFURLSessionManager *session = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];

NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request
                                        progress:^(NSProgress * _Nonnull downloadProgress) {
                                               if (progress) {
                                                   dispatch_async(dispatch_get_main_queue(), ^{
                                                       progress(downloadProgress.fractionCompleted);
                                                   });
                                               }
                                           }
                                       destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {}  
                                       completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {}
];
复制代码

最后看Masonry中的使用,对照与1.3节中提到的链式编程。

- (MASConstraint * (^)(id))mas_equalTo {
    return ^id(id attribute) {
        return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
    };
}
复制代码

5.2 block与循环引用

www.jianshu.com/p/ce1f1ee52…的3.4节

6 注意事项

嵌套层次太深不要使用 -> 不利于代码调用、直观性较差。

相关文章
相关标签/搜索