Swift 3 中如何实现转义闭包(底层)?它们是否像 objective-c 中那样隐含地 block_copied/retained?
How are escaping closures implemented in Swift 3 (under the hood)? Are they implicitly block_copied/retained like in objective-c?
我想了解转义闭包在 Swift 3 中是如何工作的?来自 Objective-C 世界,对于闭包可以逃脱其封闭函数的 return 的场景,您必须按照以下方式做一些事情:
@property (nonatomic, copy/strong) void (^callback)(NSData *rawData);
-(BOOL)someFunctionThatConsumesABlock:(void (^)(NSData *rawData))block
{
if (callback) {
self.callback = block;
return YES;
}
return NO;
}
-(void)someFunctionThatExecutesAtSomeLaterPoint
{
NSData *data = [self bytesFromSomeProcess];
self.callback(data);
}
基本上在上面objective-C中的代码中,消费块的对象执行堆复制并强保留块。这种强大的保留是有道理的,因为该块也需要超出它作为参数传递的函数的范围(因为它是一个转义 closure/block)。
在Swift3中,上面的似乎已经没有必要了。需要做的只是将闭包注释为 "escaping." 那么 Swift 3 在幕后做了什么?它和 objective-C 做同样的事情吗?本质上,Swift 3 中的转义闭包是否也被传递的对象隐含地强烈保留?我不知道该语言还能如何实现这一目标并拥有闭包 "stick around."
当您将闭包另存为属性或其他方式时,闭包确实会(强烈地)隐式保留。来自 Swift 编程语言,Automatic Reference Counting:
… closures, like classes, are reference types. When you assign a closure to a property, you are assigning a reference to that closure.
(这就是存在捕获列表的原因:帮助避免意外的循环保留。)
但是,我认为您可能误解了 @escaping
的用途(或者 Swift 的旧版本中缺少 @noescape
)。这 不会 自动为您保存闭包。相反,它只是向调用者表明您 可能 保存闭包(闭包可能 逃避 函数的执行)。这允许编译器执行额外的优化,例如跳过保留。它还允许调用者在闭包内省略 self.
:
class Foo {
var x = 3
func test() {
[1, 2, 3].map { [=10=] + x }
// `self.x` is not necessary, because the map function's
// closure is non-escaping
}
}
(如果您有兴趣了解 @escaping
背后的真实情况,我不知道此类信息的权威来源,但您可能会在this talk about SIL, the SIL.rst docs in the open-source project, and perhaps Understanding Swift Performance 来自 WWDC16。)
我想了解转义闭包在 Swift 3 中是如何工作的?来自 Objective-C 世界,对于闭包可以逃脱其封闭函数的 return 的场景,您必须按照以下方式做一些事情:
@property (nonatomic, copy/strong) void (^callback)(NSData *rawData);
-(BOOL)someFunctionThatConsumesABlock:(void (^)(NSData *rawData))block
{
if (callback) {
self.callback = block;
return YES;
}
return NO;
}
-(void)someFunctionThatExecutesAtSomeLaterPoint
{
NSData *data = [self bytesFromSomeProcess];
self.callback(data);
}
基本上在上面objective-C中的代码中,消费块的对象执行堆复制并强保留块。这种强大的保留是有道理的,因为该块也需要超出它作为参数传递的函数的范围(因为它是一个转义 closure/block)。
在Swift3中,上面的似乎已经没有必要了。需要做的只是将闭包注释为 "escaping." 那么 Swift 3 在幕后做了什么?它和 objective-C 做同样的事情吗?本质上,Swift 3 中的转义闭包是否也被传递的对象隐含地强烈保留?我不知道该语言还能如何实现这一目标并拥有闭包 "stick around."
当您将闭包另存为属性或其他方式时,闭包确实会(强烈地)隐式保留。来自 Swift 编程语言,Automatic Reference Counting:
… closures, like classes, are reference types. When you assign a closure to a property, you are assigning a reference to that closure.
(这就是存在捕获列表的原因:帮助避免意外的循环保留。)
但是,我认为您可能误解了 @escaping
的用途(或者 Swift 的旧版本中缺少 @noescape
)。这 不会 自动为您保存闭包。相反,它只是向调用者表明您 可能 保存闭包(闭包可能 逃避 函数的执行)。这允许编译器执行额外的优化,例如跳过保留。它还允许调用者在闭包内省略 self.
:
class Foo {
var x = 3
func test() {
[1, 2, 3].map { [=10=] + x }
// `self.x` is not necessary, because the map function's
// closure is non-escaping
}
}
(如果您有兴趣了解 @escaping
背后的真实情况,我不知道此类信息的权威来源,但您可能会在this talk about SIL, the SIL.rst docs in the open-source project, and perhaps Understanding Swift Performance 来自 WWDC16。)