5

JSManagedValue を使用しようとしているときに問題が発生しました。WWDC 2013 のセッション 615 に基づく私の理解から、Objective-C から Javascript への参照、およびその逆の参照が必要な場合は、JSValue を Objective-C に格納するだけでなく、JSManagedValue を使用して参照を回避する必要があります。サイクル。

これは、私がやろうとしていることの簡素化されたバージョンです。Javascript オブジェクトへの参照が必要な ViewController があり、その Javascript オブジェクトは ViewController でメソッドを呼び出すことができる必要があります。ViewController には、カウントを表示する UILabel と、カウントをインクリメントする 'Add' と、現在のビュー コントローラを作成して新しい ViewController に置き換える 'Reset' の 2 つの UIButton があります (基本的には、古い ViewController を確認できるようにするためです)。これをテストしている間、適切にクリーンアップされます)。

ではviewDidLoad、ViewController が呼び出さupdateLabelれ、Javascript オブジェクトからカウントを正しく取得できます。ただし、その実行ループが終了した後、Instruments は JSValue が解放されていることを示しています。JSManagedValue と同様に、ViewController はまだ存在するため、JSValue がガベージ コレクションされるのを防ぐべきだと考えましたが、_managedValue.valuenil を返します。

JSManagedValue を使用する代わりに JSValue を保存すると、視覚的にはすべて機能しますが、予想どおり、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 の存続期間全体にわたって保持されるように、このセットアップを処理する正しい方法は何ですか?

4

2 に答える 2

2

これには私も戸惑いました。JavaScriptCore ヘッダーと ColorMyWords サンプル プロジェクトを読んだ後、管理された値がその値を維持するには、所有者が JavaScript に表示される (つまり、コンテキスト内の値に割り当てられる) 必要があるようです。-loadColorsPluginこれは、ColorMyWords プロジェクトの AppDelegate.mのメソッドで説明されています。

于 2013-12-01T23:34:07.543 に答える
0

ちょっと興味がありますが、クラスのプライベート インターフェイスに JSContext *context プロパティと JSValue *value プロパティを追加してみましたか?

私の推測では、初期化で JSContext *context を作成していますが、それをまったく保持していないため、予期しない時点で JSContext *value だけでなく、ARC を介して JSContext 全体が収集される可能性があるようです。しかし、_managedValue を介して参照しているため、注意が必要です...

これについて 100% 確信があるわけではありませんが、managedValue はまったく必要ないはずです。通話中に JavaScript から JSValue を渡していません。-(void)updateLabelこれは、説明されている WWDC のスピーカーが問題になるシナリオです。代わりに、コードが JSContext から JSValue *value を参照しているため、.m.

また、必要な ObjC メソッドを実行するたびに、カウントをリロードして関数を JSValues に追加していることにも気付きました。一度だけロードされるように、それらをクラスの一部として保持することができます。

したがって、代わりに:

@interface CAViewController () {
    JSManagedValue *_managedValue;
}
@end

次のようになります。

@interface CAViewController ()
@property JSContext *context;
@property JSValue *value;
@property JSValue *countFunc;
@property JSValue *addFunc;
@end

クラスの残りの部分は次のようになります。

@implementation CAViewController

- (id)init {
    self = [super init];
    if (self) {
        _context = [[JSContext alloc] init];

        _value = [_context evaluateScript:@"...JS Code Shown Above..."];
        [_value[@"setView"] callWithArguments:@[self]];

        _addFunc = _value[@"add"];
        _countFunc = _value[@"count"];
    }
    return self;
}

- (void)viewDidLoad {
    [self updateLabel];
}

- (void)updateLabel {
    JSValue *count = [self.countFunc callWithArguments:@[]];
    self.countLabel.text = [count toString];
}

- (IBAction)add:(id)sender {
    [self.addFunc callWithArguments:@[]];
}

- (IBAction)reset:(id)sender {
    UIApplication *app = [UIApplication sharedApplication];
    CAAppDelegate *appDelegate = app.delegate;
    CAViewController *vc = [[CAViewController alloc] init];
    appDelegate.window.rootViewController = vc;
}

ViewControllerを強制終了するだけですべてをクリアしたいとおっしゃっていたので、JSContextとJSValueもViewControllerと一緒に移動するため、参照がなくなります(理論的には?:P)。

お役に立てれば。私はObjective C(JavaScripCoreフレームワークは言うまでもなく)を初めて使用するので、実際にはかなりずれている可能性があります。

于 2014-01-17T14:41:36.020 に答える