2013-10-05 3 views
5

JSManagedValue를 사용하는 중에 문제가 발생합니다. Session 615 at WWDC 2013을 기반으로 한 내 이해에서 Objective-C와 Javascript 또는 그 반대로의 참조를 원할 때는 Objective-C에 JSValue를 저장하는 대신 JSManagedValue를 사용해야 만 참조주기를 피할 수 있습니다.JSManagedValue를 사용하여 JSValue가 해제되지 않고 참조주기가 발생하지 않도록하려면 어떻게해야합니까?

다음은 내가 시도한 것의 약식 버전입니다. 나는 자바 스크립트 객체에 대한 참조가 필요한 ViewController를 가지고 있으며, 자바 스크립트 객체는 ViewController에서 메서드를 호출 할 수 있어야합니다. ViewController는 카운트를 표시하는 UILabel을 가지고 있으며 두 개의 UIButtons, 'Count'를 증가시키는 'Add', 그리고 새로운 ViewController로 현재 뷰 컨트롤러를 생성하고 대체하는 'Reset'을 가지고 있습니다 (기본적으로 그냥 이전 ViewController 내가 이것을 테스트하는 동안 적절하게 정리된다).

viewDidLoad에서 ViewController는 updateLabel을 호출하고 Javascript 개체에서 카운트를 올바르게 가져올 수 있습니다. 그러나, 그 실행 루프가 끝나면, Instruments는 JSValue가 릴리즈되고 있음을 보여줍니다. ViewController는 JSManagedValue와 마찬가지로 여전히 존재하므로 JSValue가 가비지 수집되지 않도록해야한다고 생각 했으니 _managedValue.value은 이제 nil을 반환합니다.

JSManagedValue를 사용하는 대신 JSValue를 저장하는 경우 시각적으로 모두 작동하지만 예상대로 ViewController와 JSValue 사이에 참조주기가 있으며 Instruments는 ViewController가 절대로 릴리스되지 않는다고 확인합니다.

자바 스크립트 코드 :

(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 샘플 프로젝트를 읽은 후에는 관리되는 값이 활성 상태로 유지되도록하려면 소유자가 자바 스크립트에 표시되어야합니다 (즉, 컨텍스트의 값에 할당되어야 함). 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 함께 갈 것입니다 (이론적으로? :피).

희망이 도움이됩니다. 저는 Objective C (JavaScripCore 프레임 워크는 말할 것도 없습니다)를 처음 접했기 때문에 실제로 벗어날 수있었습니다!