RetainCycle Memory leak with AppleScriptObject in NSBlockOperation - objective-c

I'm on X-Code 8.2, OSX not iOS, Objective-C
In a custom NSBlockOperation I included an Applescript (gets compiled by XCode) like that:
SHImportLayoutOperation.m file
#import "SHImportLayoutOperation.h"
#import "Helpers.h"
#class SHIndesignImport; // Applescript
// ################################################
#interface SHImportLayoutOperation()
#property SHIndesignImport *indesignImport;
#end
// ################################################
#interface SHIndesignImport:NSObject
// Methods
- (NSMutableDictionary*)scanIndesign:(NSMutableDictionary*)parameters errors:(NSMutableArray*)errors;
#property NSBlockOperation* operation;
#end
// ################################################
#implementation SHImportLayoutOperation
- (id)init
{
self = [super init];
if (self)
{
_helpers = [[Helpers alloc] init];
init];
_indesignImport = [[NSClassFromString(#"SHIndesignImport") alloc] init];
}
return self;
}
Later on in the operation i need a method of the script. Everything just works fine. BUT: i want to pass a reference of the current operation (self) to the script to check for the isCancelled state. This also works but leads to a memory leak/cycle.
[_indesignImport setOperation:self];
// call scanIndesign
NSMutableDictionary* scanIndesignResult = [_indesignImport scanIndesign:parametersINDD errors:errors];
_indesignImport.operation = nil;
This leads to the cycle. So i tried a weak self
__weak typeof(self) weakSelf = self;
[_indesignImport setOperation:weakSelf];
// call scanIndesign
NSMutableDictionary* scanIndesignResult = [_indesignImport scanIndesign:parametersINDD errors:errors];
_indesignImport.operation = nil;
Same error. Then i tried to release it from within the applescript (at the end):
-- Beginning of script
#property operation: missing value
-- Lots of stuff --
-- End of script
set operation to missing value
Same stuff. If i don't pass the reference everything works fine. No cycle. So this has to be the reason. Any help appreciated.
Edit:
Here's a screenshot of instruments. As you can see, the ref count is still 1 (this is with the weakSelf)

Related

What are the rules that affect __weak variables in a block with regard to GCC_OPTIMIZATION_LEVEL?

In the code below, when I run it with the GCC_OPTIMIZATION_LEVEL set to None, execution works as I expect and it prints the following to the console:
My Obj - SomeObj
However, when I set the GCC_OPTIMIZATION_LEVEL to Fastest, Smallest in the Xcode target configuration (as one typically does for Release builds) I end up with the following printed to the console:
My obj - (null)
The object appears to be getting released when I store it into the __weak id myObj variable in [Foo doSomething]. If I remove the __weak flag from the myObj variable the code runs as expected when the GCC_OPTIMIZATION_LEVEL is set to Fastest, Smallest.
I built this example based on a similar pattern I had in another project and added the __weak flag because I was encountering a retain cycle. The retain cycle warning went away, but when I built for Release, I found that myObj would be nil by the time it got to the spot where I am logging it in this example.
What rules am I violating by setting the __weak flag?
#import "FFAppDelegate.h"
///////////////////////////////////////////////////////
#interface SomeObject : NSObject
#end
#implementation SomeObject
- (NSString *)description; {
return #"SomeObject";
}
#end
///////////////////////////////////////////////////////
#interface Factory : NSObject
#end
#implementation Factory
- (id)generateObj {
id myObj = nil;
if (!myObj) {
SomeObject *anObj = [[SomeObject alloc] init];
myObj = anObj;
}
return myObj;
}
#end
///////////////////////////////////////////////////////
#interface Bar : NSObject
- (id)barObj;
#end
#implementation Bar
{
Factory *factory;
}
- (id)init {
self = [super init];
if (self) {
factory = [[Factory alloc] init];
}
return self;
}
- (id)barObj {
id anObj = [factory generateObj];
return anObj;
}
#end
///////////////////////////////////////////////////////
#interface Foo : NSObject
#property (strong) Bar *aBar;
- (void)doSomething;
#end
#implementation Foo
- (id)init {
self = [super init];
if (self) {
_aBar = [[Bar alloc] init];
}
return self;
}
- (void)doSomething {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
__weak id myObj = [self.aBar barObj];
NSLog(#"My Obj - %#", myObj);
});
}
#end
///////////////////////////////////////////////////////
#implementation FFAppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
Foo *f = [[Foo alloc] init];
[f doSomething];
}
#end
What rules am I violating by setting the __weak flag?
In your example, there is no strong reference to the instance returned from [self.aBar barObj]. Therefore, you cannot assume the instance will be alive by the time you read the myObj variable.
In the unoptimized build, the returned instance has been added to an autorelease pool and is therefore still alive when you print myObj. You can verify this by setting a breakpoint in -[SomeObject dealloc].
In the optimized build, ARC has avoided the autorelease pool and the returned instance is therefore deallocated immediately.
See section 3.2.3 in the Clang ARC documentation for more info. Specifically:
When returning from such a function or method, ARC retains the value at the point of evaluation of the return statement, then leaves all local scopes, and then balances out the retain while ensuring that the value lives across the call boundary. In the worst case, this may involve an autorelease, but callers must not assume that the value is actually in the autorelease pool.
When using __weak to avoid a retain cycle, you must ensure that there is a strong reference somewhere else to keep the object alive.
Maybe you should use Apple's default compiler LLVM and not GCC.
Edit: Also your "factory" method looks wrong. For example, the object is always nil in that if. Take a tour: http://developer.apple.com/library/ios/#documentation/general/conceptual/CocoaEncyclopedia/ClassFactoryMethods/ClassFactoryMethods.html

NSMutableArray as instance variable alway null

After many hours wasted, I officially turn to the experts for help!
My problem lies with using a NSMutableArray as an instance variable, and trying to both add objects and return the array in a method in my class. I am obviously doing something fundamentally wrong and would be grateful for help...I have already tried all the suggestions from other similar questions on stackoverflow, read apples documentation, and basically all combinations of trial and error coding I can think of. The mutable array just alway returns (null). I've even tried creating properties for them, but still the array returns (null) and then I also am running into memory management problems due to the retain while setting the property, and the init in the init method for the class.
Here is what I am trying to do:
1) Loop through a series of UISwitches and if they are 'switched on', add a string to the NSMutableArray
2) Assign this mutable array to another array in another method
Any help much appreciated,
Andy
And for some code...
fruitsViewController.h
#import <UIKit/UIKit.h>
#interface fruitsViewController : UIViewController
{
NSMutableArray *fruitsArr;
UISwitch *appleSwitch;
UISwitch *orangeSwitch;
}
#property (nonatomic,retain) NSMutableArray *fruitsArr; // ADDED ON EDIT
#property (nonatomic,retain) IBOutlet UISwitch *appleSwitch;
#property (nonatomic,retain) IBOutlet UISwitch *orangeSwitch;
- (IBAction)submitButtonPressed:(id)sender;
#end
fruitsViewController.m
#import "fruitsViewController.h"
#implementation fruitsViewController
#synthesize fruitsArr; // ADDED ON EDIT
#synthesize appleSwitch, orangeSwitch;
/* COMMENTED OUT ON EDIT
-(id)init
{
if (self = [super init]) {
// Allocate memory and initialize the fruits mutable array
fruitsArr = [[NSMutableArray alloc] init];
}
return self;
}
*/
// VIEW DID LOAD ADDED ON EDIT
- (void)viewDidLoad
{
self.fruitsArr = [[NSMutableArray alloc] init];
}
- (void)viewDidUnload
{
self.fruitsArr = nil;
self.appleSwitch = nil;
self.orangeSwitch = nil;
}
- (void)dealloc
{
[fruitsArr release];
[appleSwitch release];
[orangeSwitch release];
[super dealloc];
}
- (IBAction)submitButtonPressed:(id)sender
{
if ([self.appleSwitch isOn]) {
[self.fruitsArr addObject:#"Apple"; // 'self.' ADDED ON EDIT
}
if ([self.orangeSwitch isOn]) {
[self.fruitsArr addObject:#"Orange"; // 'self.' ADDED ON EDIT
}
NSLog(#"%#",self.fruitsArr); // Why is this returning (null) even if the switches are on?!
[fruitsArr addObject:#"Hello World";
NSLog(#"%#",self.fruitsArr); // Even trying to add an object outside the if statement returns (null)
}
#end
It seems like your init function is never called. If you're initializing this view controller from a NIB, you need to use initWithCoder. If not, just declare your fruitsArr in viewDidLoad.
Use view did load instead of init...
- (void)viewDidLoad
{
fruitsArr = [[NSMutableArray alloc] init];
}
Change that init for viewDidLoad and see what happens
Is your init method ever being called (in complicationsViewController). Add a NSLog to check this, you might be calling initWithNib: maybe.
At viewDidUnload you should remove self.fruitsArr = nil;, or, if you want to keep it, then initialize the fruitsArr in viewDidLoad (and remove it from init).
because fruitsArr don't be init.
you should do this first:
fruitsArr = [[NSMutableArray alloc] init];
so, I think you don't run - (id)init before you use fruitsArr.

Problem with releasing an object

I've got this code:
Entry.h
#import <Foundation/Foundation.h>
#interface Entry : NSObject {
id object;
SEL function;
}
#property (retain) id object;
#property (assign) SEL function;
-(Entry*) initWithObject:(id)object selector:(SEL)function;
#end
Entry.m
#import "Entry.h"
#implementation Entry
#synthesize object;
#synthesize function;
-(Entry*) initWithObject:(id)obj selector:(SEL)sel {
self = [super init];
[self setObject:obj];
[self setFunction:sel];
return self;
}
-(void) dealloc {
[super dealloc];
if ([self object] != nil)
[[self object] release];
}
#end
And when I do this:
Entry *hej = [Entry alloc];
[hej release];
I get:
objc[2504]: FREED(id): message object sent to freed object=0xf5ecd0
Program received signal: “EXC_BAD_INSTRUCTION”.
What am I doing wrong?
(And this insert code thing at stack overflow doesnt work, unless I'm doing something wrong and you're not supposed to click "code sample" and then paste.)
+alloc only allocates memory. You need -init to actually create the object in that memory space. Since you are only allocating memory and not creating an object there, calling -release on a chunk of memory is giving you an error. Further, you want your [super dealloc] call to appear at the end of you -dealloc method. Change those two things and the following should work:
Entry *hej = [[Entry alloc] init];
[hej release];
there are two problems here:
1) you need to check that self = [super init] does not return nil. Typical usage would be to follow wrap your initialization code with the conditional:
if ((self = [super init]) != nil) {
// initialize the object...
}
2) but where you are getting stuck is on instantiating your object: you should do it like this:
Entry *hej = [[Entry alloc] initWithObject:myObj selector:mySelector];
(assuming that you want to go through the custom initializer you just defined...
else just use the default init method.) but 'alloc' must be followed by an init.
Entry *hej = [[Entry alloc] init]; // will do the trick...
Firstly, you need an init to go with your alloc. Second, in dealloc, you send a message to self after calling [super dealloc]. You can't do that. The final deallocation should go at the end.
I would also recommend changing:
if ([self object] != nil)
[[self object] release];
to:
[self setObject:nil];
It's less code and functionally equivalent. =)
There are many things wrong with your code. I'll try to go through them.
First, its better to use a different ivar name to your property name so its clear where you are using each. Apple normally uses an underscore prefix, but any prefix will do.
#interface Entry : NSObject {
id _object;
SEL _function;
}
#property (retain) id object;
#property (assign) SEL function;
#synthesize object = _object;
#synthesize function = _function;
Next, you aren't using the standard init template (although this probably wont make any difference normally).
-(Entry*) initWithObject:(id)obj selector:(SEL)sel {
self = [super init];
if (self != nil) {
// initializations
}
return self;
}
Next, Apple (for good reasons) recommends against using getters/setters in your init/dealloc. So your init would be:
-(Entry*) initWithObject:(id)obj selector:(SEL)sel {
self = [super init];
if (self != nil) {
_object = [obj retain];
_object = sel;
}
return self;
}
Next, after [super dealloc] your object is destroyed, so you cannot reference self (and hence your ivars) after that, so your dealloc should look like:
-(void) dealloc {
// your deallocations
[super dealloc];
}
Further, as above, Apple recommends you should not use setters or getters in your dealloc routine, so your deallocation would initially look like:
if (_object != nil)
[_object release];
But further still, Objective C allows (and Cocoa encourages) that sending a method to nil does nothing. This is in stark contast to most other languages where messaging nil would cause a crash, but it is how Objective C/Cocoa work and you need to get used to it. So your deallocation is actually just:
[_object release];
And finally, alloc only allocates the memory for your object, you have to initialize it, so the initialization would be something like:
Entry *hej = [[Entry alloc] initWithObject:myobj selector:#selector(mymethod)];

objective c Class code not executing? what is my problem?

Having some issues with code not executing within the classes I created and thought I initialized and implemented correctly here are all the files. There is a class with an array of another class. Then implemented in the code finally but for some reason none of the NSLog calls seem to execute except the one immediately before [mobdefs createTable] in the main code. All help appreciated...
// Mobdefs.h
#interface Mobdefs : NSObject {
#public NSMutableArray *mobInfo;
}
#property(retain) NSMutableArray *mobInfo;
-(void) createTable;
#end
// Mobdefs.m
#import "Mobdefs.h"
#import "Mobrec.h"
#implementation Mobdefs
#synthesize mobInfo;
- (id) init
{
mobInfo = [[NSMutableArray alloc] init];
return self;
}
-(void) addmobrec
{
MobRec *aNewMobRec = [[MobRec alloc] init];
aNewMobRec.mName=#"newbie";
[mobInfo addObject:aNewMobRec];
[aNewMobRec release];
NSLog(#"MobRec Added\n");
}
-(void) createTable
{
NSLog(#"Populating mob table.\n"); // *** THIS CODE NEVER SEEMS TO GET EXECUTED
}
#end
//main.h
Mobdefs *mobdef;
//main.m
NSLog(#"just before createTable call\n");
[mobdef createTable];
although the createTable code is called in the main the only NSLog output I get is the 'just before createtable...'
It doesn't seem that you have initialized mobdef. Add the following:
mobdef = [[Mobdefs alloc] init];
to your main.m before you invoke the method on it.
Objective-C silently ignore calls on nil, as mobdef would be initialized to initially.
are you allocating and initializing mobdef in main.m?

Having trouble adding objects to NSMutableArray in Objective C

I am using the iPhone SDK and have an issue doing something simple. I am trying to add an NSNumber object to an NSMutableArray instance variable. I tried adding NSNumber card to NSMutableArray viewedCardsArray, however without breaking, it does not get added to the array. Here is the code.
/////////////////////////////////////////////////////
// Inside the header file Class.h
#interface MyViewController : UIViewController {
NSMutableArray *viewedCardsArray;
//snip ...
}
#property (nonatomic, retain) NSMutableArray *viewedCardsArray;
#end
/////////////////////////////////////////////////////
// Inside the methods file Class.m
#import "StudyViewController.h"
#implementation StudyViewController
#synthesize viewedCardsArray
//snip ...
- (IBAction)doShowCard {
//snip ...
NSNumber *cardIdObject = [[NSNumber alloc] initWithInt:(int)[self.currentCard cardId]];
[viewedCardsArray addObject: cardIdObject];
[cardIdObject release];
}
So this code executes, and does not seem to leak (according to the Leaks performance tool). However when stepping through the code, at no point does CardIdObject appear in viewedCardsArray.
Looking through SO, I know these basic questions are pretty common to ObjC newbies (like me) so apologies in advance!
Have you initialized your viewedCardsArray? If not you need to somewhere - this is usually done in the init method for your class:
- (id)init
{
self = [super init];
if(self) {
viewedCardsArray = [[NSMutableArray alloc] init];
}
return self;
}
Then it is released in the dealloc method:
- (void)dealloc
{
[viewedCardsArray release];
[super dealloc];
}
Perspx has outlined one way of initializing the array. However, you can also use the class methods provided by NSArray:
self. viewedCardsArray = [NSMutableArray array];
This can go in init or elsewhere.
Note: The object will be autoreleased.