Ho riscontrato problemi durante il tentativo di utilizzare JSManagedValue. Dalla mia comprensione basata su Session 615 at WWDC 2013, quando si desidera avere un riferimento da Objective-C a Javascript e viceversa, sarà necessario utilizzare JSManagedValue invece di archiviare JSValue in Objective-C per evitare un ciclo di riferimento.Come posso utilizzare JSManagedValue per evitare un ciclo di riferimento senza che JSValue venga rilasciato?
Ecco una versione ridotta di ciò che sto tentando di fare. Ho un ViewController che ha bisogno di un riferimento ad un oggetto Javascript, e che l'oggetto Javascript deve essere in grado di chiamare un metodo sul ViewController. ViewController ha un UILabel che visualizza un conteggio e ha due UIButtons, 'Aggiungi' che incrementa il conteggio e 'Reset', che crea e sostituisce il controller di visualizzazione corrente con un nuovo ViewController (fondamentalmente solo per verificare il vecchio ViewController viene ripulito correttamente mentre sto testando questo).
In viewDidLoad
, ViewController chiama updateLabel
ed è in grado di recuperare correttamente il conteggio dall'oggetto Javascript. Tuttavia, al termine del ciclo di esecuzione, Instruments sta mostrando che JSValue viene rilasciato. Il ViewController esiste ancora, così come il suo JSManagedValue, quindi ho pensato che dovrebbe impedire a JSValue di essere raccolto in modo non corretto, ma ora _managedValue.value
restituisce zero.
Se memorizzo JSValue invece di utilizzare JSManagedValue, tutto funziona visivamente, ma c'è un ciclo di riferimento tra ViewController e JSValue, come previsto, e Instruments conferma che ViewControllers non viene mai rilasciato.
codice 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;
}
Che cosa è il modo corretto di gestire questa configurazione in modo che JSValue venga mantenuto per tutta la durata del ViewController ma non vengono creati cicli di riferimento in modo che il ViewController possa essere ripulito?
Penso che sia corretto. Sono costretto a leggere il codice sorgente dal video per la sessione 615 per confermare questo però: Sarebbe bello se il progetto di esempio fosse ancora disponibile. – Poulsbo
Il codice di esempio è attualmente disponibile: https://developer.apple.com/downloads/index.action?name=WWDC%202013 – Poulsbo