2013-10-05 7 views
5

JSManagedValueの使用中に問題が発生しています。 Session 615 at WWDC 2013に基づく私の理解では、Objective-CからJavascriptへの参照、またはその逆の参照が必要な場合は、参照サイクルを避けるためにJSValueをObjective-Cに格納するのではなく、JSManagedValueを使用する必要があります。JSManagedValueを使用して、JSValueを解放せずに参照サイクルを回避するにはどうすればよいですか?

ここでは、私がしようとしているものを取り除いたものです。私は、Javascriptオブジェクトへの参照を必要とするViewControllerを持っており、そのJavascriptオブジェクトはViewControllerのメソッドを呼び出すことができる必要があります。 ViewControllerには、カウントを表示するUILabelがあり、カウントをインクリメントする 'Add'と、現在のView Controllerを作成して新しいViewControllerに置き換える 'Reset'という2つのUIButtonがあります(基本的には古いViewController私はこれをテストしている間に適切にクリーンアップされます)。

viewDidLoadでは、ViewControllerはupdateLabelを呼び出し、Javascriptオブジェクトから正しくカウントを取得できます。しかし、その実行ループが終了した後、InstrumentsはJSValueがリリースされていることを示しています。 ViewControllerはJSManagedValueと同じようにまだ存在しているので、JSValueがガベージコレクトされるのを防ぐべきだと思ったが、_managedValue.valueはnilを返すようになった。

JSManagedValueを使用する代わりにJSValueを格納するだけであれば、すべて視覚的に動作しますが、ViewControllerとJSValueの間に参照サイクルがあり、InstrumentsはViewControllerが解放されないことを確認します。

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をクリーンアップできるように参照サイクルは作成されません。

答えて

1

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

+0

これは正しいと思います。私はセッション615のビデオからソースコードを読んで、これを確認することを余儀なくされています。サンプルプロジェクトがまだ利用可能であればいいでしょう。 – Poulsbo

+0

サンプルコードは現在ここにあります:https://developer.apple.com/downloads/index.action?name=WWDC%202013 – Poulsbo

0

JSContext *コンテキストとJSValue *値のプロパティをクラスのプライベートインターフェイスに追加しようとしましたか?

あなたの初期設定ではJSContext *コンテキストを作成していますが、それはまったく保持していないので、あなたのJSContext全体がARCとJSValue *の値によって予期しないポイントで収集されるようです。しかし、あなたが_managedValueを通してそれを参照しているので難しいです...

私はこれについて100%確信していませんが、私はmanagedValueをまったく必要としないと考えています。 -(void)updateLabelへの呼び出し中にJavascriptからJSValueを渡すことはありません。これは、説明されているWWDCのスピーカーが問題になるシナリオです。あなたのコードがあなたのJSContextからJSValue *値を参照しているので、代わりに行うべきことは、.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は参照bを持たなくなります彼らもViewController(理論的には? :P)。

これが役に立ちます。私はObjective C(JavaScripCoreフレームワークはもちろん)を新しくしました。

関連する問題