Swift的闭包函数

2017-11-01  本文已影响0人  Latte_Bear
闭包函数的结构

介绍

Swift官方开发文档对于闭包的介绍是这样的:闭包是可以在代码中被传递和引用的功能性独立模块。Swift 中的闭包类似于其他语言的匿名函数,和 C Objective-C 中的 Block 很像。主要作用是捕捉和存储定义在上下文中的任何常量和变量的引用,处理关于捕获的内存管理的操作(看了这些介绍很懵逼,我只看懂了一句”和 Block 很像“)。

闭包的应用场景

闭包的定义

闭包有特殊的定义方式,定义格式为 {参数列表 -> 返回值 in 实现代码},如果没有参数也没有返回值可以省略前面的定义部分(同时省略关键字 in)直接写实现代码。

使用闭包回调传递参数

在异步执行任务获取结果通过闭包回调,与 Objective-C 中的 Block 的用法完全一致。

override func viewDidLoad() {
   super.viewDidLoad()
   loadData { 
            (result) in
            print(result)
        }
}
func loadData(completion: @escaping ([String])  -> Void) -> () {
    DispatchQueue.global().async {
        print("此处执行耗时操作\(Thread.current)")
        // 模拟耗时操作,让线程休眠10秒钟
        Thread.sleep(forTimeInterval: 10)
        // 异步加载网络数据(这里只做演示)
        let jsonData = ["八卦", "头条", "新闻"]
            
        // 回到主线程更新界面
        DispatchQueue.main.async(execute: {
            // 回调执行闭包,传递异步获取到的结果
            completion(jsonData)
        })
    }
}

以上代码为 Swift4.0 的最新写法,在早期的 Swift 中闭包可以使用外部参数,但在 Swift3.0 更新了以后苹果禁止了闭包设置外部参数,并且抛弃了 @noescaping 的使用改用 @escaping,如果闭包的回调需要逃逸必须使用@escaping 修饰,闭包默认是不逃逸的。需要注意的是在闭包在回调的时候需要手动键入回调回来的返回值形参,例如以下图片中的方式:

Swift4.0语法强调
闭包回调接收值的类型推导
打印结果输出:
使用闭包回调的输出结果

尾随闭包

如果函数的最后一个参数是闭包那么该函数参数可以提前结束,最后一个参数可以直接使用 {} 包装(闭包里嵌套尾随闭包除外)。
以下为闭包的完整写法:

override func viewDidLoad() {
   super.viewDidLoad()
   loadData(completion : {
      (result) -> () in
      print(result)
   })
}
func loadData(completion : @escaping ([String]) -> ()) -> () {
   DispatchQueue.Global().async {
      print("此处执行耗时操作\(Thread.current)")
      // 模拟耗时操作,让线程休眠 10 秒钟
      Thread.sleep(forTimeInterval: 10)
      // 此处模拟从网络获取获取数据
      let resultData = ["八卦", "头条", "新闻"]
      // 回到主线程更新UI
      DispatchQueue.main.async(execute : {
         // 回调执行闭包,传递异步获取到的网络数据
         completion(resultData)
      })
   }
}
尾随闭包的原始写法

以下为尾随闭包的简写版:

override func viewDidLoad() {
   super.viewDidLoad()
   loadData{
      (result) in
      print(result)
   }
}
func loadData(completion : @escaping ([String]) ->()) {
   DispatchQueue.Global().async {
      print("此处执行耗时操作\(Thread.current)")
      // 模拟耗时操作,让线程休眠 10 秒钟
      Thread.sleep(forTimeInterval: 10)
      // 此处模拟网络获取到的数据
      let resultData = ["八卦", "头条", "新闻"]
      
      // 回到主线程更新 UI
      DispatchQueue.main.async {
         // 回调执行闭包,传递异步获取到的网络数据
         completion(resultData)
      }
   }
}
尾随闭包的简写

输出结果:


尾随闭包的输出结果

闭包中出现循环引用的解决方式

做苹果平台软件的开发者对于循环引用都不会陌生,在使用 Objective-CBlock 时,当某一个类拥有该 block 块并且在本类中会使用该 block 块进行操作的时候,如果在这个 block 块中使用 self 会形成循环引用(这段话写的也是啰嗦到没谁了),不啰嗦了直接上代码:

#import "ViewController.h"

@interface ViewController ()
// 定义 Block 属性
@property (nonatomic, copy) void (^testBlock) (void);
@end
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // 调用方法
    [self loadData: {
         NSLog(@"%@", self.view);
    }];
}
- (void)loadData:(void (^) (void))completion {
     // 使用属性记录 block
     self.testBlock = completion;
     dispatch_async(dispatch_get_global_queue(0, 0), ^{
          NSLog(@"耗时操作");
          [NSThread sleepForTimeInterval: 2.0];
          dispatch_async(dispatch_get_main_queue(), ^{
               //执行block
               completion();
          });
     });
}
@end

以上代码是最典型的循环引用代码,可以看到控制器定义 ```block属性拥有该block块,又在block实现中使用强引用self造成block引用控制器,以下为Objective-C`的循环引用解决方式:

#import "ViewController.h"

@interface ViewController ()
// 定义 Block 属性
@property (nonatomic, copy) void (^testBlock) (void);
@end
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // 此处打断循环
    __weak typeof(self) weakSelf = self;
    // 调用方法
    [self loadData: {
         NSLog(@"%@", weakSelf.view);
    }];
}
- (void)loadData:(void (^) (void))completion {
     // 使用属性记录 block
     self.testBlock = completion;
     dispatch_async(dispatch_get_global_queue(0, 0), ^{
          NSLog(@"耗时操作");
          [NSThread sleepForTimeInterval: 2.0];
          dispatch_async(dispatch_get_main_queue(), ^{
               //执行block
               completion();
          });
     });
}
@end

前面说了在 Swift 中闭包与 block 的应用场景完全一致,那么在 Swift 中出现循环引用该如何解决呢?

Swift 中循环引用的解决方式

总结

上一篇 下一篇

猜你喜欢

热点阅读