在Swift中混淆强引用循环的例子

Bur*_*ang 10 objective-c ios swift

这是Apple的文档中的一个示例:

class HTMLElement {

    let name: String
    let text: String?

    lazy var asHTML: Void -> String = {
        if let text = self.text {
            return "<\(self.name)>\(text)</\(self.name)>"
        } else {
            return "<\(self.name) />"
        }
    }

    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }

    deinit {
        print("\(name) is being deinitialized")
    }

}
Run Code Online (Sandbox Code Playgroud)

我理解为什么这个闭包属性会导致强引用周期,我知道如何解决它.而且我不打算这么说.

让我困惑的是以下代码:

var heading: HTMLElement? = HTMLElement(name: "h1")
let defaultText = "some default text"
heading!.asHTML = {
    // confusing, this closure are supposed to retain heading here, but it does not
    return "<\(heading!.name)>\(heading!.text ?? defaultText)</\(heading!.name)>"
}
print(heading!.asHTML())
heading = nil
// we can see the deinialization message here, 
// it turns out that there is not any strong reference cycle in this snippet.
Run Code Online (Sandbox Code Playgroud)

据我所知,从Swift文档和我自己的Objective-c经验来看,变量heading将被闭包捕获,因此应该引起一个强大的参考周期.但事实并非如此,这让我很困惑.

我还写了一个这个例子的Objective-c对应物,它确实引起了我预期的强引用周期.

typedef NSString* (^TagMaker)(void);

@interface HTMLElement : NSObject

@property (nonatomic, strong) NSString      *name;
@property (nonatomic, strong) NSString      *text;

@property (nonatomic, strong) TagMaker      asHTML;

@end

@implementation HTMLElement

- (void)dealloc {
    NSLog(@"%@", [NSString stringWithFormat:@"%@ is being deinitialized", self.name]);
}

@end
Run Code Online (Sandbox Code Playgroud)

;

HTMLElement *heading = [[HTMLElement alloc] init];
heading.name = @"h1";
heading.text = @"some default text";

heading.asHTML = ^ {
    return [NSString stringWithFormat:@"<%@>%@</%@>", heading.name, heading.text, heading.name];
};

NSLog(@"%@", heading.asHTML());

heading = nil;
// heading has not been deinitialized here
Run Code Online (Sandbox Code Playgroud)

任何提示或指南将不胜感激.

Leo*_*Leo 5

因为,在后一种情况下

Swift 闭包有一个强大的参考,heading而不是heading那个点的实例

在图像中,它显示如下

如果我们按设置打破红线heading = nil,那么参考圆就会被打破.

更新开始:

但是,如果你没有将航向设置为零,那么仍然有一个参考圆圈,就像我在上面发布的图像一样.你可以像这样测试它

func testCircle(){
    var heading: HTMLElement? = HTMLElement(name: "h1")
    let defaultText = "some default text"
    heading.asHTML = {
        return "<\(heading.name)>\(heading.text ?? defaultText)</\(heading.name)>"
    }
    print(heading.asHTML())
}
testCircle()//No dealloc message is printed
Run Code Online (Sandbox Code Playgroud)

更新结束

我还编写了下面的测试代码来证明闭包没有对内存中的实例进行强有力的引用

var heading: HTMLElement? = HTMLElement(name: "h1")
var heading2 = heading
let defaultText = "some default text"
heading!.asHTML = {
// confusing, this closure are supposed to retain heading here, but it does not
    return "<\(heading!.name)>\(heading!.text ?? defaultText)</\(heading!.name)>"
}
let cloureBackup = heading!.asHTML
print(heading!.asHTML())

heading = HTMLElement(name: "h2")

print(cloureBackup())//<h2>some default text</h2>
Run Code Online (Sandbox Code Playgroud)

所以,测试代码的图像是 在此输入图像描述

你会看到游乐场的日志

<h1>some default text</h1>
<h2>some default text</h2>
Run Code Online (Sandbox Code Playgroud)

没有找到任何关于这个的文件,只是从我的测试和理解,希望它会有所帮助


Sha*_*hiM 0

我认为swift在这件事上表现有点不同:

由此:

如果在闭包作用域之外声明了任何变量,则在闭包作用域内引用该变量会创建对该对象的另一个强引用。唯一的例外是使用值语义的变量,例如Swift中的 Ints、Strings、Arrays 和 Dictionaries 。