由簡入深,洞悉Block 小小擴展:關於值的類型

關於Blcok,我們經常使用,在Swift中,閉包更是無處不在。那麼,關於Block,我們到底瞭解多少呢?這篇文章旨在對Block做一次全面的總結,從簡單應用,到案例講解再到易錯點,以及原理和內存機制,爭取一文寫清楚Block。
拒絕搬磚,手寫代碼,全部測試。

知識點:

小小擴展:關於值的類型

1.Block的幾種用法
(1)作爲局部變量;
(2)作爲全局變量;
(3)Block作爲函數參數;
(4)Block作爲函數返回值;
(5)Block中使用局部變量和全局變量;

2.Block在項目中常用案例
(1)頁面傳值;
(2)代替代理使用;
(3)作爲參數傳值;

3.Block的循環引用問題
(1)問題產生原因;
(2)如何解決;

4.Block內存機制

5.Block原理

小小擴展:關於值的類型

我們知道,無論是變量、常量還是參數,都會有它的類型,我們一開始接觸一門語言的時候,都會詳細的介紹這門語言的各種類型。比如C的int, float, double, char, const char, char *, *, 等等類型,比如OC的NSString, NSArray, NSSet, NSValue等等類型。我們也知道,這些類型用來標誌一個值在內存中的存儲方式。把某個類型放在值的前面,用於聲明這個值的類型。比如:
int a;
char b;
NSString * name;
那麼,Block作爲匿名函數,它也是有類型的,不過它的類型比較複雜一些,是由簡單類型組合起來的類型。比如:
void (^block) (void)
類型是: void (^) (void)
NSString * (^block1) (void)
類型是:NSString * (^) (void)
NSInteger (^block3) (NSInteger, NSInteger)
類型是: NSInteger (^) (NSInteger, NSInteger)
明白了Block的類型,我們就可以把Block跟其他簡單類型一樣作爲參數,作爲變量進行使用了。

1.Block的幾種用法
(1)作爲局部變量;
//block作爲局部變量
-(void)test1 {
   /**
    返回值類型 (^Block名稱)(參數列表) = ^ 返回值類型 (參數列表){
       實現代碼
    }
     */
    void (^block) (void) = ^ {
        NSLog(@"返回值爲空,參數爲空的block");
    };
    //使用block
    block();
    
    NSString * (^block1) (void) = ^{
        NSString * temp = @"返回值爲字符串,參數爲空的block";
        return temp;
    };
    //使用
    NSLog(@"%@", block1());
    
    NSInteger (^block2) (NSInteger) = ^ NSInteger (NSInteger a){
        NSInteger t = a * 10;
        return t;
    };
    NSInteger test2 = block2(3);
    NSLog(@"test2: %ld", (long)test2);
    
    //求兩個數的和
    NSInteger (^block3) (NSInteger, NSInteger) = ^ NSInteger (NSInteger a, NSInteger b) {
        return a + b;
    };
    NSInteger test3 = block3(2, 7);
    NSLog(@"test3: %ld", test3);
    
    //求兩個數的乘積
    double (^block4) (double, double) = ^double (double a, double b) {
        return a * b;
    };
    NSLog(@"test4: %.2f", block4(2.3, 4.7));
    
    //求兩個數的加減乘除運算
    double (^block5) (double, double, NSString*) = ^ double (double a, double b, NSString* mark){
        
        if ([mark isEqualToString:@"+"]) {
            return a + b;
        } else if ([mark isEqualToString:@"-"]) {
            return a - b;
        } else if ([mark isEqualToString:@"*"]) {
            return a * b;
        } else if ([mark isEqualToString:@"/"]) {
            if (b == 0) {
                return -1;
            }
            return a / b;
        } else {
            return -1;
        }
    };
    
    NSLog(@"test5: %f", block5(4.0, 6.5, @"+"));
    NSLog(@"test6: %f", block5(7.8, 6.5, @"-"));
    NSLog(@"test7: %f", block5(2.4, 6.5, @"*"));
    NSLog(@"test8: %f", block5(4.8, 2.4, @"/"));
    
    //block賦值
    double (^block6) (double, double) = block4;
    //調用
    NSLog(@"test9: %.2f", block6(2.1, 3.8));
}

打印結果:


(2)作爲全局變量;
//給block起別名
typedef NSInteger (^SumBlock) (NSInteger, NSInteger);
@interface BlockTestViewController ()

@property(nonatomic, copy)SumBlock block7;

@end
//block作爲全局變量
-(void)test2 {
    self.block7 = ^ NSInteger (NSInteger a, NSInteger b) {
        NSInteger sum = 0;
        for (NSInteger i = a; i < b + 1; i++) {
            sum = sum + I;
        }
        NSLog(@"從 %ld 到 %ld 的和爲 %ld", a, b, sum);
        return sum;
    };
    
    if (self.block7) {
        //調用
        self.block7(0, 100);
        self.block7(0, 50);
    }
}

打印結果:


(3)Block作爲函數參數;
//請求成功
typedef void (^SuccessBlock)(NSDictionary* json, int code);
//請求失敗
typedef void (^FailBlock)(NSString* msg);

//調用test3系列:block作爲參數
-(void)test4 {
    //有參數,有返回值的block作爲參數
    [self test3:^NSString *(NSString *name) {
        NSLog(@"name = %@", name);
        return @"哇哈哈哈哈";
    }];
    //有參數,無返回值的block作爲參數
    [self test3_1:^(NSString *name) {
        NSLog(@"我是block的參數,name = %@", name);
    }];
    //無參數,有返回值的block作爲參數
    [self test3_2:^int{
        return 10;
    }];
    //函數也有返回值,block也有返回值
    NSArray * arr = [self test3_3:^NSArray *(NSArray *array) {
        //過濾重元素
        NSMutableArray * tempArray = [NSMutableArray array];
        BOOL flag = NO;
        for (NSNumber * num in array) {
            for (NSNumber * temp in tempArray) {
                if (num == temp) {
                    flag = YES;
                }
            }
            if (flag == NO) {
                [tempArray addObject:num];
            }
            flag = NO;
        }
        return tempArray;
    }];
    NSLog(@"測試的array: %@", arr);
    
    //應用實例
    [self loadDataSuccess:^(NSDictionary *json, int code) {
        NSLog(@"請求成功,%@", json);
    } fail:^(NSString *msg) {
        NSLog(@"%@", msg);
    }];
}

//block作爲函數參數-1
//有參數,有返回值的block作爲參數
-(void)test3:(NSString* (^)(NSString * name))block {
    NSString * newName = NSStringFromSelector(_cmd);
    NSLog(@"%@", block(newName));
}
//block作爲函數參數-2
//有參數,無返回值的block作爲參數
-(void)test3_1: (void (^)(NSString * name))block {
    NSString * myName = @"丹頂鶴";
    block(myName);
}

/**
 block作爲函數參數-3
 無參數,有返回值的block作爲參數
 */
-(void)test3_2: (int (^)(void))blcok {
    NSLog(@"我是test3_2");
    int a = blcok();
    NSLog(@"我是block的返回值喲, a = %d", a);
}

/**
block作爲函數參數-5
函數也有返回值
*/
-(NSArray *)test3_3: (NSArray* (^)(NSArray* array))block {
    NSArray * myArray = @[@1,@2,@5,@8,@5,@7,@9,@7,@2,@1];
    return block(myArray);
}

/**
 block作爲函數參數-4
 實際應用:模擬網絡請求
 */
-(void)loadDataSuccess: (SuccessBlock)success fail:(FailBlock)fail {
    NSDictionary * json = @{@"num":@1,
                            @"name":@"小明",
                            @"age":@18,
                            @"phone":@"12345678922",
                            @"email":@"[email protected]"};
    int code = 200;
    if (code == 200) {
        success(json, code);
    } else {
        fail(@"請求失敗了");
    }
}

打印結果:


(4)Block作爲函數返回值;
//調用test5: block作爲函數返回值
-(void)test6 {
    //有返回值,無參數的block作爲函數返回值
    NSString* (^block)(void) = [self test5_1];
    NSLog(@"%@", block());
    
    //有返回值,有參數的block作爲函數返回值
    NSString * (^block2)(NSString * name) = [self test5_2:@"小花花"];
    NSString * bReturn = block2(@"小點點");
    NSLog(@"最終的值: %@", bReturn);
}

//有返回值,無參數的block作爲函數返回值
-(NSString* (^)(void))test5_1 {
    NSString * (^block)(void) = ^ NSString* {
        return @"我們都是好孩子";
    };
    return block;
}

//有返回值,有參數的block作爲函數返回值
-(NSString* (^)(NSString* name))test5_2: (NSString *)testName {
    NSString * (^block)(NSString * name) = ^ NSString* (NSString* name) {
        return [NSString stringWithFormat:@"block的參數:%@",name];
    };
    NSLog(@"函數的參數: %@",testName);
    return block;
}

打印結果:


(5)Block中使用局部變量和全局變量;
@interface BlockTestViewController ()

@property(nonatomic, copy)SumBlock block7;
@property(nonatomic, assign)int num;

@end
-(void)test7 {
    //1.使用局部變量
    //要想在block內部使用局部變量,需要在局部變量前面加上__block
    __block int a = 100;
    int b = 20;
    void (^block)(void) = ^{
        NSLog(@"a = %d, b = %d", a,b);
        a = 200;
    };
    block();
    NSLog(@"after a = %d, b = %d", a,b);
    
    //2.使用全局變量
    self.num = 10;
    double (^block1) (void) = ^{
        self.num = 20;
        double t = self.num * 3.14;
        return t;
    };
    //使用block1
    double r = block1();
    NSLog(@"self.num = %d, r = %.2f", self.num, r);
    
}
- (void)dealloc
{
    NSLog(@"%@銷燬了",NSStringFromClass(self.class));
}

打印結果:


未完待續······

2.Block在項目中常用案例
(1)頁面傳值;
(2)代替代理使用;
(3)作爲參數傳值;
3.Block的循環引用問題
(1)問題產生原因;
(2)如何解決;
4.Block內存機制
5.Block原理

源碼地址:
https://github.com/weiman152/PropertyAndBlockTest

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章