У меня возникают проблемы при попытке использовать JSManagedValue. Насколько я понимаю, на основе сеанса 615 на WWDC 2013, когда вы хотите получить ссылку от цели -C в Javascript и наоборот, вам нужно будет использовать JSManagedValue вместо того, чтобы просто сохранять JSValue в Objective-C, чтобы избежать цикла ссылок.
Вот урезанная версия того, что я пытаюсь сделать. У меня есть ViewController, которому нужна ссылка на объект Javascript, и этот объект Javascript должен иметь возможность вызывать метод в ViewController. ViewController имеет UILabel, который отображает счетчик, и имеет две кнопки UIButton: «Добавить», который увеличивает счетчик, и «Сброс», который создает и заменяет текущий контроллер представления новым 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;
}
Каков правильный способ обработки этой настройки, чтобы значение JSValue сохранялось на протяжении всего жизненного цикла ViewController, но не создавалось эталонных циклов, чтобы можно было очистить ViewController?