ObjectiveC and JavaScriptCore: Will using this method of calling CallBacks cause memory issues? - objective-c

DISCLAIMER: This is a long post, but could prove very valuable for those grappling with using the new ObjectiveC JavascriptCore framework and doing asynchronous coding between ObjC and JS.
Hi there, I'm super new to Objective C and am integrating a javascript communication library into my iOS app.
Anyway, I've been trying my hand at using the new ObjectiveC JavaScriptCore Framework introduced in iOS7. It's pretty awesome for the most part, though quite poorly documented so far.
It's really strange mixing language conventions, but also kind of liberating in some ways.
I should add that I am of course using ARC, so that helps a lot coming from the Javascript world. But I have a question that's pretty specific around memory use issues when moving between ObjectiveC and the JSContext callBacks. Like if I execute a function in Javascript that then does some asynchronous code, and then calls back to a defined ObjectiveC block, and then that calls a defined JS callback... I just want to make sure I'm doing it right (ie. not leaking memory some place)!
Just to do things proper (because I reference a the class self to call the ObjectiveC callBacks I create a weakSelf so it plays nice with ARC (referenced from question: capturing self strongly in this block is likely to lead to a retain cycle):
__unsafe_unretained typeof(self) weakSelf = self;
Now, say I have a JSContext and add a function to it. I want this function to take a callBack function and call it with "Hello" as an argument as well as pass ANOTHER function as a callBack. ie.
// 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 our basic JS side of things. Now to add the ObjectiveC complexity. I'm 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 is happening in I also have the method definition. This is the place that causes the most concern to me:
-(void)objCFunction:(NSString *)message withCallBack:(JSValue *)callBack
{
NSString *concatenatedString = [NSString stringWithFormat:#"%#%#", message, #"Bar"];
[callBack callWithArguments:#[concatenatedString]];
}
So when I call:
[context evaluateScript: #"functionA(functionB);" ];
It should pass through the chain, and it does exactly what I expect it to do.
My main concern is that I hope I'm not somehow capturing a JSValue somewhere along this chain that is then leaking out.
Any help in helping me understand how ARC/the JSMachine would manage this approach to calling callBacks fluidly between Objective C and Javascript, would be super valuable!
Also, I hope this question helps others out there who are experimenting with this framework.
Thanks!

The problem with retain cycles occurs when you have two objects, each of which retains part of another. It's not specific to JavascriptCore. It's not even specific to blocks although blocks make the problem much easier to blunder into.
E.g.
#interface ObjcClass : NSObject
#property (strong,nonatomic) JSValue *badProp;
- (void) makeEvilRetainWithContext:(JSContext *) context;
#end
- (void) makeEvilRetainWithContext:(JSContext *) context{
context[#"aFunc"]=^(JSValue *jsValue){
self.badProp=jsValue;
};
}
The self.context[#"aFunc"] now retains the ObjcClass object because self.badProp is now inside the function obj inside the context created by assigning the block to #"aFunc". Likewise, the context is retained because one of its own strongly retained values is retained in self.badProp.
Really, the best way to avoid all this is just to not try and store JSValue in objective-c objects ever. There really doesn't seem to be a need to do so e.g.
#property (strong,nonatomic) NSString *goodProp;
- (void) makeGoodFunc:(JSContext *) context;
#end
- (void) makeGoodFunc:(JSContext *) context{
context[#"aFunc"]=^(JSValue *jsValue){
self.goodProp=[JSValue toString];
};
}
You code isn't a problem because simply passing a JSValue (even a function) through a method won't retain it.
Another way to think of it might be: After, objCFunction:withCallBack: executes, would there be anyway for the object represented by self to access the JSValue passed as callBack? If not, then no retain cycle.

Check out the WWDC introduction "Integrating JavaScript into Native Apps" session on Apple's developer network: https://developer.apple.com/wwdc/videos/?id=615 - it contains a section on Blocks and avoiding capturing JSValue and JSContext
In your sample code above, all the JSValues are passed as arguments (the way Apple recommends) so the references only exist whilst the code is executed (no JSValue objects are captured).

Related

Objective-C remove all oberservers for a certain notification

I have an iPad app that uses a proprietary library object which registers for a "UIScreenDidConnectNotification". Occasionally this object is deallocated and reallocated behind the scenes. As it is in a library, I cannot ensure that it is properly removing this observer.
Is there a way for me to manually remove all/any observers for a specific notification (i.e. UIScreenDidConnectNotification) without having any access to the object that has registered. This would keep the application from sending the message to a deallocated object.
Update: Here is the easiest way to fix my problem. I wish I could do a better job, but life is too short.
#import
#import
#interface NSNotificationCenter (AllObservers)
#end
#implementation NSNotificationCenter (AllObservers)
// This function runs before main to swap in our special version of addObserver
+ (void) load
{
Method original, swizzled;
original = class_getInstanceMethod(self, #selector(addObserver:selector:name:object:));
swizzled = class_getInstanceMethod(self, #selector(swizzled_addObserver:selector:name:object:));
method_exchangeImplementations(original, swizzled);
// This function runs before main to swap in our special version of addObserver
+ (void) load
{
Method original, swizzled;
original = class_getInstanceMethod(self, #selector(addObserver:selector:name:object:));
swizzled = class_getInstanceMethod(self, #selector(swizzled_addObserver:selector:name:object:));
method_exchangeImplementations(original, swizzled);
}
/*
Use this function to remove any unwieldy behavior for adding observers
*/
- (void) swizzled_addObserver:(id)notificationObserver selector:(SEL)notificationSelector name:(NSString *)notificationName object:(id)notificationSender
{
NSString *notification = [[NSString alloc] initWithUTF8String: "UIScreenDidConnectNotification" ];
// It's a hack, but I just won't allow my app to add this type of notificiation
if([notificationName isEqualToString: notification])
{
printf("### screen notifcation added for an observer: %s\n", [notificationSender UTF8String] );
}
else
{
// Calls the original addObserver function
[self swizzled_addObserver:notificationObserver selector:notificationSelector name:notificationName object:notificationSender];
}
}
As it is in a library, I cannot ensure that it is properly removing this observer.
If the object is created in a library, it's not your responsibility to remove the object. If the library is deallocating the object without removing it from the notification center, that's a clear bug in the library.
Is there a way for me to manually remove all/any observers for a specific notification... without having any access to the object that has registered.
There's nothing in the API for NSNotificationCenter that lets you do that. Just the opposite, in fact -- the methods that let you remove the observer all require a pointer to a specific object.
I agree with both of Caleb's points: it is not your responsibility to perform this task and there is nothing in the API to support it.
However... if you feel like hacking something in to perform this task for whatever reason, refer to this thread: How to retrieve all NSNotificationCenter observers?
The selected answer of that thread has a category for NSNotificationCenter that allows you to retrieve all observers for a given notification name. Again, this is not recommended though.

Synthesized copy property does not get deallocated with ARC

in my project I'm managing several Drawing objects. I'm trying to add a copy of a SmartPath object to a drawing object. This works. But when the drawing gets deallocated the SmartPath does not. I have put some extra code in the dealloc of the Drawing to explicitly set clear the pointer to the SmartPath. For some reason this works (the retain count was 1). I know I can probably copy the SmartPath and assign that to a strong parameter to fix this leak. But I'm relatively new to IOS and want to know how to use the copy parameters properly in combination with ARC.
Here is the code:
Drawing.h:
#interface Drawing : NSObject{
#private
SmartPath* rawLinePath; //path that is build up from straight lines between input points
SmartPath* smoothLinePath; //smoothened version of rawLinePath
}
#property(atomic,copy)SmartPath* rawLinePath;
#property(atomic,copy)SmartPath* smoothLinePath;
Drawing.m
#implementation Drawing
#synthesize rawLinePath;
#synthesize smoothLinePath;
-(id)init
{
if (self = [super init])
{
[NSThread detachNewThreadSelector:#selector(pointMonitor)
toTarget:self
withObject:nil];
}
return self;
}
-(void)dealloc{
rawLinePath=nil;
}
SmartPath.m
/*
* Init - set all variables in the correct state
*/
-(id)init
{
if (self = [super init])
{
visible=TRUE;
color = [UIColor redColor].CGColor;
width = SMARTPATH_LINE_WIDTH;
path = CGPathCreateMutable();
lock = [[NSLock alloc]init];
}
return self;
}
/*
* dealloc - clean up after self
*/
-(void)dealloc{
CGPathRelease(path);
}
/*
* copy method to be able to pass a SmartPath to a copy property
*/
-(id)copyWithZone:(NSZone *)zone{
SmartPath *pathCopy = [[SmartPath allocWithZone: zone] init];
pathCopy.visible =visible;
pathCopy.color = color;
pathCopy.width = width;
return pathCopy;
}
I hope any of you knows the answer to this problem.
Best regards
Your problem is your call to -detachNewThreadSelector:toTarget:withObject:. This retains target, which is self, and does not release it until pointMonitor exits. I suspect this never happens, so you've effectively created a retain loop.
You should almost never use -detachNewThreadSelector:toTarget:withObject:. It can create an unbounded number of threads. Instead, you should generally use dispatch queues, NSTimer, NSOperation or other async mechanisms. NSThread objects are generally only appropriate for long-lived producer/consumer threads (and usually those are still handled better with the newer tools like dispatch queues).
I'm not certain what pointMonitor does, but is there any reason it needs its own thread at all? You can do a lot of very good Cocoa development and never fork a thread. Could you use an NSTimer here? Note that most of these techniques retain their target until they fire (just like NSThread). If they didn't, you'd crash when they fired.
Without knowing what you're trying to do, I'm not certain which approach to recommend. You may want to put that together as a new question.
By not starting instance variable names with an underscore, you end up with code where you never know whether you are using an accessor method or an instance variable. As a result, you can never be sure whether a copy is made or not.
If you do that in other places, there's a good chance that a reference to your SmartPath object gets stuck somewhere. And what are you doing creating NSLock objects? Do you need to do anything that #synchronized can't do with much less code?
And if you use a newer Xcode version, get rid of all the instance variables and #synthesize statements. Just declare the properties.
And excuse me, but detaching a thread from an init method is just sick.

ARC, ivars in Blocks and Reference Cycles via Captured Self

I’m working in a pure iOS5/ARC environment, so I can use __weak references as needed. I do reference ivars in a block in many situations, most notably, animation blocks that move views around, which are properties of say, my view controller class.
My question:
In the most trivial use of ivars in a block, am I creating a reference cycle? Do I need to use the __weak self / strong self technique everytime I write a block that manipulates instance variables of the containing object?
I’ve been re-watching the 2011 WWDC Session #322 (Objective-C Advancements in Depth) to understand the nuances regarding the 3 minute segment starting at time index 25:03 about “Reference Cycle Via Captured Self”. To me, this implies any usage of ivars in a block should be safeguarded with the weak self / strong self setup as described in that segment.
The sample method below on a view controller, is typical of animations I do.
In the openIris block, is it wrong to reference ivars “_topView” and “_bottomView” as I have?
Should I always setup a __weak reference to self before the block, then a strong reference inside the block to the weak reference just setup prior, and then access the ivars through that strong reference within my block?
From the WWDC session, I understand that referencing ivars in a block is really creating a reference to the implied self that these ivars hang off of.
To me, this implies that there really isn’t any simple or trivial case where it is correct to access ivars in a block without the weak/strong dance to ensure no cycles. Or am I reading to much into a corner case that doesn’t apply to simple cases, such as my example?
- (void)openIrisAnimated:(BOOL)animated
{
if (_isIrisOpened) {
NSLog(#"Asked to open an already open iris.");
return; // Bail
}
// Put the common work into a block.
// Note: “_topView” and “_bottomView” are the backing ivars of
// properties “topView” and “bottomView”
void (^openIris)() = ^{
_topView.frame = CGRectMake(....);
_bottomView.frame = CGRectMake(....);
};
// Now do the actual opening of the iris, whether animated or not:
if (animated) {
[UIView animateWithDuration:0.70f
animations:^{
openIris();
}];
}
else {
openIris();
}
_irisOpened = YES; // Because we have now just opened it
}
Here’s how I’d re-write the openIris block piece using the guidance from Session #322, but I’m just wondering if all my similar blocks require this weak/strong reference dance to ensure correctness and stability:
__weak MyClass *weakSelf = self;
void (^openIris)() = ^{
MyClass *strongSelf = weakSelf;
if (strongSelf) {
strongSelf.topView.frame = CGRectMake(....);
strongSelf.bottomView.frame = CGRectMake(....);
}
};
Is this in fact, necessary?
There is only a cycle here if self then goes on to hold a reference to the block (or something owned by self). If not you're good to go as the lifetime of the block is not dictated by the self it retained.
So in your particular example, you seem to be in the clear. Animation blocks don't need to participate in the weak/strong self dance.
The case to worry about is something like addObserverForName:object:queue:usingBlock:. The docs say, "The block is copied by the notification center." Under ARC, that word "copy" is a red flag; now you need to take steps so that you (the caller) will not leak.
EDIT: Also, sometimes ARC itself will alert you. The completion block of -[UIPageViewController setViewControllers:direction:animated:completion:] is a case in point. I would never have suspected that using self here might cause a retain cycle, but ARC warned that it would, so I did the weak-strong dance just in case.

Obj-C using #selector on a static method from a static method in the same class?

I have two static methods/selectors in the same class, one passes the other as a callback to an external method. However, how I have it coded I get an error. This worked when both the methods were instance methods, and I've read it can work when the first method is an instance method using [self class]. However, I haven't found information when both are static, and I haven't got it to work.
+(void)Validate {
Callback *managerCallback = [[[Callback alloc] initWithTarget:self Action:#selector(Parse:)] autorelease];
...
}
+(void)Parse:(Callback *)managerCallback {
...
}
Thanks!
Callback *managerCallback = [[[Callback alloc] initWithTarget:self Action:#selector(Parse:)] autorelease];
That line of code is setup to call the instance method Parse:, not a class method as you have it defined.
Objective-C does not have static methods. It has class methods and instance methods.
As well, your methods should start with lowercase letters.
Herp-da-derp. Dave is right.
Given this:
+(void)Validate {
Callback *managerCallback = [[[Callback alloc] initWithTarget:self Action:#selector(Parse:)] autorelease];
...
}
+(void)Parse:(Callback *)managerCallback {
...
}
Some comments:
methods should start with lowercase letters
it is exceedingly odd to use a class in such a role; even if you really only ever need one of 'em, use an instance. At the least, the instance is a convenient bucket to toss state in and it'll make refactoring in the future much easier if you ever need two.
The above pattern makes the assumption (and I ASSumed) that the instance of Callback is retained. For callbacks, timers, and some other patterns, this is typical; retain the target until the target is called for the last time. Then release (or autorelease). However, notification centers do not do this. Nor are delegates retained, typically.
Turns out the code is written correctly to do what I wanted to, but because callback was set to autorelease, the object was getting released before the callback was being processed.

Why can't my singleton class return a value that will stay in scope

Stick with me. I'm visually impaired, have never used this site before, and will probably not post this in precisely the format that you are all used to. I apologize for any unintentional faux pas's herein.
Using Objective-C in an iOS project…
I have a singleton class, set up in what appears to be the usual way for Objective-C. It is, in the main, a series of methods which accept NSString values, interprets them, and return something else. In the code below, I'm simplifying things to the barest minimum, to emphasize the problem I am having.
From the singleton class:
- (NSUInteger) assignControlState:(NSString *)state {
// excerpted for clarity...
return UIControlStateNormal; // an example of what might be returned
}
Now, an instance of another class tries to use this method like so:
- (void) buttonSetup:(UIButton*)button {
[button setTitle:#"something" forState:[[SingletonClass accessToInstance] assignControlState:#"normal"]];
}
This code actually works. HOwever, when the system goes to draw the UI which includes the button whose title was set in this way, an EXC_BAD_ACCESS error occurs.
If the assignControlState method is moved into the same class as the buttonSetup method, no error is generated.
I'm guessing this is something about Apple's memory management that I'm not fully understanding, and how things go in and out of scope, but for the life of me, I can't figure out where I'm going wrong.
HOpe someone can help. Thanks.
The problem is in your accessToInstance method. I'll bet you are under-retaining. The implementation should be more like this:
static SingletonClass *sSingletonClass = nil;
#implementation
+ (id)accessToInstance {
if (sSingletonClass == nil) {
sSingletonClass = [[[self class] alloc] init];
}
return sSingletonClass;
}
#end
Now, if your program is following normal memory management rules, the singleton will stay around. You can check by writing:
- (void)dealloc {
[super dealloc]; // <-- set a breakpoint here.
}
If the debugger ever stops at this breakpoint, you know something in your program has over-released the singleton.
You know that bit you excerpted for clarity? I think you need to show us what it is because there's probably an over release in it somewhere.
Specifically, I think you release an autoreleased object. If you do that and don't use the object again, everything will carry on normally until the autorelease pool gets drained. The autorelease pool gets drained automatically at the end of the event at about the same time as the drawing normally occurs.
That would also explain the delayed crash following the NSLogs.