ObjectiveC and JavaScriptCore: Will using this CallBacks invocation method cause memory problems?

DISCLAIMER: This is a long post, but it can be very valuable for those who are trying to cope using the new ObjectiveC JavascriptCore infrastructure and perform asynchronous encoding between ObjC and JS.

Hi, I am super new to Objective-C and integrate the javascript message library into my iOS app.

In any case, I'm trying to use the new ObjectiveC JavaScriptCore platform introduced in iOS7. This is quite surprising for the most part, although it is still pretty poorly documented.

This is indeed a strange mixture of linguistic conventions, but also a kind of liberation in some way.

I must add that I, of course, use ARC, so this helps a lot from the Javascript world. But I have a question that is quite specific in terms of memory usage when moving between ObjectiveC and JSContext call servers. For example, if I execute a function in Javascript that then executes some asynchronous code and then accesses a specific ObjectiveC block and then calls a specific JS callback ... I just want to make sure that I am doing it right (i.e. not memory leak to some place)!

Just to make things right (because I refer to the self class, to call ObjectiveC calls I create weakSelf , so it plays fine with ARC (link to the question: capturing yourself in this block will probably lead to a save loop ):

 __unsafe_unretained typeof(self) weakSelf = self; 

Now let's say I have an JSContext and add a function to it. I want this function to execute the callBack function and call it using "Hello" as an argument , and also pass the ANOTHER function as a callBack . i.e.

 // Add a new JSContext. JSContext context = [[JSContext alloc] initWithVirtualMachine:[[JSVirtualMachine alloc] init]]; // Add a function to the context. This function takes a callBack function and calls it back with "Hello" [context evaluateScript: @"var functionA = function(callBack){ var aMessage = "Foo"; callBack(aMessage, function(message){ /* message should say: Foo Bar */ }); }" ]; // Note, if you try to copy this code, you will have to get rid of the returns in the JS script. 

Okay, so we have the main side of things. Now add the complexity of ObjectiveC. I am going to add the first ObjectiveC CallBack block:

 context[@"functionB"] = ^(NSString *theMessage, JSValue *theCallBack){ [weakSelf objCFunction:theMessage withCallBack:theCallBack]; }; 

In the same class, all this happens, I also have a method definition. This is the place I am most concerned about :

 -(void)objCFunction:(NSString *)message withCallBack:(JSValue *)callBack { NSString *concatenatedString = [NSString stringWithFormat:@"%@%@", message, @"Bar"]; [callBack callWithArguments:@[concatenatedString]]; } 

So when I call:

 [context evaluateScript: @"functionA(functionB);" ]; 

He has to go through the chain, and he does exactly what I expect from this.

My main concern is that I hope that I did not somehow capture JSValue somewhere along this chain, which then flows.

Any help helping me understand how ARC / JSMachine will manage this approach to calling callBacks between Objective-C and Javascript will be very valuable!

In addition, I hope this question helps others who are experimenting with this infrastructure.

Thanks!

+6
source share
2 answers

The problem with save loops occurs when you have two objects, each of which saves part of the other. This does not apply to JavascriptCore. This is not even block specific, although blocks make the problem much easier to make mistakes.

eg.

 @interface ObjcClass : NSObject @property (strong,nonatomic) JSValue *badProp; - (void) makeEvilRetainWithContext:(JSContext *) context; @end - (void) makeEvilRetainWithContext:(JSContext *) context{ context[@"aFunc"]=^(JSValue *jsValue){ self.badProp=jsValue; }; } 

self.context[@"aFunc"] now saves the ObjcClass object, because self.badProp now inside the obj function, inside the context created by assigning the @"aFunc" block. Similarly, the context is preserved because one of its own highly stored values ​​is stored in self.badProp .

Indeed, the best way to avoid all this is to simply not try to store JSValue in objective-c objects ever. It seems that there is no need to do this, for example.

 @property (strong,nonatomic) NSString *goodProp; - (void) makeGoodFunc:(JSContext *) context; @end - (void) makeGoodFunc:(JSContext *) context{ context[@"aFunc"]=^(JSValue *jsValue){ self.goodProp=[JSValue toString]; }; } 

You code is not a problem because simply passing a JSValue (even a function) using a method will not save it.

Another way to think about this could be: After executing objCFunction:withCallBack: will there be an object represented by self to access the JSValue passed as callBack ? If not, then there is no retention cycle.

+2
source

Check out the introductory WWDC “Integrating JavaScript into Native Applications” on the Apple developer network: https://developer.apple.com/wwdc/videos/?id=615 - it contains a section on blocks and avoiding capturing JSValue and JSContext

In the above code example, all JSValues ​​are passed as arguments (as Apple recommends), so links exist only at runtime (no JSValue objects).

+2
source

All Articles