Это пример из документа 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")
}
}
Я понимаю, почему это свойство закрытия приведет к сильному эталонному циклу, и я знаю, как его разрешить. И я не буду спорить об этом.
Что меня действительно смущает, так это код:
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.
Насколько я знаю из 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
;
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
Любые подсказки или руководства будут очень признательны.