objc的“block”和Swift的“闭包"表达式

要理解block和闭包需要和变量进行比较

首先在C中定义一个变量a:int a = 10;

定义一个block:

    // 定义一个sumBlock
    int(^sumBlock)(int, int) = ^(int a, int b) {
        return a+b;
    };
    int c = sumBLock(1,2);// 使用block代码

定义一个闭包

let sum:(Int, Int) -> Int =  { (a, b) -> Int in
            return a + b
        }
        print(sum(1,2))

从上面的定义可以看出:
block可以归纳为:

    // 定义一个sumBlock
    返回值类型(^block名字)(形参列表) = ^(实参列表) {
        表达式
        return 返回数据;
    };
    返回类型 接收数据的变量名 = block名(实参列表);// 使用block代码

闭包的一般表达式

let 闭包名:(形参列表) -> 返回值类型 =  { (实参列表) -> 返回值类型 in
            表达式
            return 返回值
        }
        print(包名(实参列表)) // 使用闭包

Blcok相关语法

BLock类型变量在BLock语法下,一旦使用了BLock就相当于生成了可复制给BLock类型变量的值,Block既指源码中给你的BLock语法也指有BLock生成的值

typedef int(^blk)(int); // 此时blk是一个BLock数据类型
    blk bk = ^(int count){
        return count + 1;
    };
    int (^blk1)(int) = bk;
    int(^blk2)(int);
    blk2 = blk1;

block会截获局部变量

int a = 10;
    
    void(^logBlock)(void) = ^(void) {
        NSLog(@"a=%d",a);
    };
    
    a = 20;
    logBlock();
    // 输出的是a=10

如果想在block中修改自动变量的值:

int val = 0;
void(^blk)(void) = ^{
    val = 1;
    };
    blk();
    printf("val = %d\n",val);
    //会报错
    }

如果想在block中修改外部变量需要在自动变量加上__block修饰词

__block iint val = 0;
void(^blk)(void) = ^{
    val = 1;
};

blk();
printf("val is %d",val);
// 执行结果是 val is 1

在以下代码是不会报错:

NSMutableArray *arrayM = [NSMutableArray array];
    void(^blk)(void) = ^(void) {
        id obj = [[NSObject alloc] init];
        [arrayM addObject:obj];
    };
    blk();

如果外部变量没有使用__block修饰,对该变量进行赋值就会报错,但是对数组进行添加是没有对数组进行赋值,是不会报错。

block作为参数的时候:

- (void)modelWithBLock:(void(^)(int, int))plus number:(int)a title:(NSString* )title
{
    
}

// 定义一个变量和一个block
//int a;
// void(^plus)(int, int);
// NSString *title;

在使用的时候直接使用括号包装起来,把变量名放到括号外面就好了
//(int)a
// (void(^)(int, int))plus
// (NSString*)title

闭包相关语法

利用typealias为闭包类型定义别名
typealias类似OC语法中的typedef
用法很简单,直接用 = 赋值就行了.

    // 定义一个没有参数也没有返回值的别名
    typealias Nothing = () -> ()
    // 如果没有返回值也可以这样写
    typealias Anything = () -> Void
    // 接收一个参数没有返回值
    typealias PringtNumber = (Int) -> ()
    // 有参数,有返回值的
    typealias Add = (Int, Int) -> (Int)
    typealias Add = (_ num1: Int, _ num2: Int) -> (Int)
    // 创建一个Add类型的闭包常量addCloser1
    let addCloser1:Add
    // 为已经创建好的常量进行赋值
    addCloser1 = { 
        (_ num1:Int, _ num2: Int) -> Int in
        return num1 + num2
    }
    // 调用闭包并接收返回值
    let result = addCloser1(1,2)

2.闭包类型申明和变量的穿件合并在一起

let Add:(_ num1: Int, _ num2: Int) -> (Int)
Add = { 
(_ num: Int, _ num2: Int) -> (Int) in
    return num + num2
}
// 调用闭包并接收返回值
let result  = Add(2,3)

3.省略闭包接收的形参、省略闭包体中返回值

let Add:(Int,Int) -> (Int)
Add = { 
(num,num2) -> (Int) in
    return num + num2
}
// 调用闭包并接收返回值
let result  = Add(2,3)

4.在3的基础上继续精简

let Add:(Int, Int) -> (Int)
Add = 
    { (num, num2) in
        return num + num2
}
// 调用闭包并接收返回值
let result  = Add(2,3)

5.如果闭包没有参数可以省略in

// 创建一个 () -> (String)类型的闭包常量:addCloser1并赋值
let addCloser1:() -> (String) = {
    return "这个闭包没有参数,但是有返回值"
}
// 调用闭包并接收返回值
let result = addCloser1()

6.简写的实际参数名

// 创建一个(String,String) -> (String) 类型的闭包常量:addCloser1并赋值
let addCloser1:(String, String) -> (String) = {
    return "闭包的返回值是:\($0),\($1)"
}
// 调用闭包并接受返回值
let result  = addCloser1("Hello", "Swift")

$0和$1分别表示闭包的第一个和第二个String类型的实际参数

闭包作为函数的参数

1.尾随闭包
//函数的定义
func combie2(times: Int, handle:(String, String) -> (Void)) -> Void {
    for i in 1...times {
        handle("\(i)", "456")
    }
}
// 函数的调用
combie2(times: 3) { (str1, str2) -> (Void) in
    print("\(str1),\(str2)")
}

如果函数只有唯一的闭包参数,没有其他的参数可以省略函数的小括号

func combie2(handle:(String, String) -> (Void)) -> Void {
        handle("123", "456")
    }
combie2 { (str1, str2) -> (Void) in
    print("\(str1), \(str2)")
}
2.逃逸闭包

后续补充。。。

在Swift中使用闭包不当可能引起循环强引用

class MainVC: UITabBarController {
    var callBack:((String)->())?
    

    override func viewDidLoad() {
        super.viewDidLoad()

        PringtString { (text) in
            print(text)
            // 闭包中捕获self
            self.view.backgroundColor = UIColor.red
        }
    }
    func PringtString(callBack:@escaping(String) -> ()) {
        callBack("这个闭包返回一段文字")
        // 控制器self强引用着callBack
        self.callBack = callBack;
        
    }

解决办法1、

class MainVC: UITabBarController {
    var callBack:((String)->())?
    

    override func viewDidLoad() {
        super.viewDidLoad()

        weak var weakSelf = self
        PringtString { (text) in
            print(text)
            // 闭包中捕获self
            weakSelf?.view.backgroundColor = UIColor.red
        }
    }
    func PringtString(callBack:@escaping(String) -> ()) {
        callBack("这个闭包返回一段文字")
        // 控制器self强引用着callBack
        self.callBack = callBack;
        
    }

可以使用weak进行修饰

解决办法2、

可以在闭包的第一个大括号后面插入这段代码[weak self],后面的代码直接使用self?也能解决循环引用的问题

class MainVC: UITabBarController {
    var callBack:((String)->())?
    

    override func viewDidLoad() {
        super.viewDidLoad()

//        weak var weakSelf = self
        PringtString { [weak self]
            (text) in
            print(text)
            // 闭包中捕获self
            self?.view.backgroundColor = UIColor.red
        }
    }
    func PringtString(callBack:@escaping(String) -> ()) {
        callBack("这个闭包返回一段文字")
        // 控制器self强引用着callBack
        self.callBack = callBack;
        
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容