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 注意事項

嵌套層次太深不要使用 -> 不利於代碼調用、直觀性較差。

相關文章
相關標籤/搜索