由Swift柯里化说开到block的循环引用

柯里化是函数式编程中的一个概念, 大致意图就是将一个含有多个参数的方法改为调用原方法首个参数并将调用余下参数的方法作为返回值的方法。

Swift中的柯里化

Swift2.2中的写法如下

//加法函数,需要同时传入两个参数
func add(num1:Int, _ num2:Int) -> Int {
return num1 + num2
}
add(1,2)
//柯里化写法
func add(num1:Int)(_ num2:Int) -> Int {
return num1 + num2
}
let oneAdd = add(1)
oneAdd(2)

Swift3中,这种显式写法已被移除。但是底层实现还在,毕竟这种用法略微鸡肋。我们仍可以通过返回一个闭包来实现柯里化,如下:

func add(_ num1:Int) -> (Int) -> Int {
return {
num1 + $0
}
}
let oneAdd = add(1)
oneAdd(2)


关于闭包

为什么要介绍柯里化?在Swift中,属性调用实例方法时, 属性其实也是作为方法参数的 例如:

struct testStruct {
func testMethod(str:String) {
print("\(str)")
}
}
let ts = testStruct()
//属性调用
ts.testMethod(str: "12")
//柯里化调用
testStruct.testMethod(ts)(str: "12")

这种用法的深层含义其实是:对象并不会持有他的实例方法。
因此当我们把闭包作为参数的时候,对象在调用实力方法时是可以直接在闭包参数中使用对象本身的,并不会产生循环引用。除非:

class testClass1 {
var handler:(() ->())?
func testMethod(handler:@escaping ()->()) {
self.handler = handler
}
func printTs() {
self.handler!()
}
}
var tc = testClass1()
tc.testMethod {
print("\(tc)")
}
tc.printTs()


由此联想到OC,故有此篇

这种情况下,控制器是可以正常释放的:

- (void)viewDidLoad {
[super viewDidLoad];
[self testBlock:^{
NSLog(@"%@",self.view);
}];
}
- (void)testBlock:(void(^)(void))block {
block();
}

原因同样是,实例对象不持有实例方法(runtime层:实例方法在类的method_list中,类方法则是元类持有)

当testBlock方法中用一个block属性强引用block参数,则会产生循环引用,如下:

- (void)viewDidLoad {
[super viewDidLoad];
[self testBlock:^{
NSLog(@"%@",self.view);
}];
}
- (void)testBlock:(void(^)(void))block {
self.block = block;
}

循环引用其实很好理解,但凡对象之间互相强引用形成了一个强引用的引用环就会产生循环引用,一旦这个环的某一节被置为nil或者声明为weak,则不会有循环引用。

这里再提一下,我们平时在写网络请求时,一般会这么写:

__weak typeof(self) ws = self;
//注:sendRequestCallBack的实现中只是执行block,没有强引用block
[[Request shareRequest]sendRequestCallBack:^(NSDictionary *callDic){
ws.callDic = callDic;
// 处理业务逻辑...
}];

很多人都习惯性的用弱引用,其实是没有必要的,并不会产生循环引用。

设想一下这个场景:我们push到一个页面后发出了请求,在请求还没有回调的时候pop出界面。

  • 如果这里,用的弱引用,控制器会被释放,上文block中的操作是不会被执行的
  • 如果这里,用的强引用,在这个方法还没被执行完的时候,block会一直持有控制器,知道方法结束,那么当请求完成上文block中的操作是会被执行的,随后控制器才会释放

所以 我们应该根据业务需求来判断是否使用弱引用,当回调中是处理页面逻辑一般是没必要继续执行的,如果是本地化存储或者一些其他有必要的操作,是需要执行回调的。
当然,出于性能考虑,最好的做法是当控制器pop之后根据业务需要来判断是否终止网络请求或者做其它处理,本篇不再对此详细分析…

坚持原创技术分享,您的支持将鼓励我继续创作!