我在尝试使用 JSManagedValue 时遇到了问题。根据我基于WWDC 2013 的 Session 615 的理解,当您想要从 Objective-C 到 Javascript 的引用时,反之亦然,您需要使用 JSManagedValue 而不是仅将 JSValue 存储在 Objective-C 中以避免引用循环。
这是我正在尝试做的精简版。我有一个需要引用 Javascript 对象的 ViewController,并且该 Javascript 对象需要能够调用 ViewController 上的方法。ViewController 有一个显示计数的 UILabel,并有两个 UIButton,“Add”增加计数,“Reset”创建并用新的 ViewController 替换当前的视图控制器(基本上只是为了验证旧的 ViewController在我测试这个时被正确清理)。
在viewDidLoad
中,ViewController 调用updateLabel
并能够正确地从 Javascript 对象中获取计数。但是,在该运行循环完成后,Instruments 显示 JSValue 正在被释放。ViewController 仍然存在,它的 JSManagedValue 也是如此,所以我认为应该防止 JSValue 被垃圾收集,但_managedValue.value
现在返回 nil。
如果我只存储 JSValue 而不是使用 JSManagedValue,这一切都可以直观地工作,但正如预期的那样,ViewController 和 JSValue 之间存在一个引用循环,并且 Instruments 确认 ViewControllers 永远不会被释放。
Javascript代码:
(function() {
var _count = 1;
var _view;
return {
add: function() {
_count++;
_view.updateLabel();
},
count: function() {
return _count;
},
setView: function(view) {
_view = view;
}
};
})()
CAViewController.h
@protocol CAViewExports <JSExport>
- (void)updateLabel;
@end
@interface CAViewController : UIViewController<CAViewExports>
@property (nonatomic, weak) IBOutlet UILabel *countLabel;
@end
CAViewController.m
@interface CAViewController () {
JSManagedValue *_managedValue;
}
@end
@implementation CAViewController
- (id)init {
if (self = [super init]) {
JSContext *context = [[JSContext alloc] init];
JSValue *value = [context evaluateScript:@"...JS Code Shown Above..."];
[value[@"setView"] callWithArguments:@[self]];
_managedValue = [JSManagedValue managedValueWithValue:value];
[context.virtualMachine addManagedReference:_managedValue withOwner:self];
}
return self;
}
- (void)viewDidLoad {
[self updateLabel];
}
- (void)updateLabel {
JSValue *countFunc = _managedValue.value[@"count"];
JSValue *count = [countFunc callWithArguments:@[]];
self.countLabel.text = [count toString];
}
- (IBAction)add:(id)sender {
JSValue *addFunc = _managedValue.value[@"add"];
[addFunc callWithArguments:@[]];
}
- (IBAction)reset:(id)sender {
UIApplication *app = [UIApplication sharedApplication];
CAAppDelegate *appDelegate = app.delegate;
CAViewController *vc = [[CAViewController alloc] init];
appDelegate.window.rootViewController = vc;
}
处理此设置的正确方法是什么,以便在 ViewController 的整个生命周期中保留 JSValue 但不创建引用循环以便可以清理 ViewController?