Small Memory Issue with Objective-C - objective-c

I made a Mac OS X app that basically runs an NSTask. The thing is, I made a class called XXTask to handle a file, and a class called XXController to handle the drag and drops in the GUI and asking the XXTask to handle a file now and then.
I had made almost the same thing in the past, and it worked fine. This time, I added a delegate protocol, and made XXController the delegate of XXTask.
When XXTask fails, I ask the delegate to show a particular view, and thus call a method like this :
[delegate showView];
This works, but when trying to relaunch using the information I stored on the first launch, the app outputs errors. I used NSLog to see what exactly was wrong, and it seems like three instance variables (two NSStrings and one NSMutableArray) are (null).
These are the three instance variables :
NSString *curFilePath;
NSArray *lastArgs;
NSString *lastLaunchPath;
I create them like this :
curFilePath = filename;
// filename is an NSString passed to the method where I first create curFilePath
// the object passed to the method is a retained NSString (an instance variable of XXController)
lastArgs = [[NSMutableArray arrayWithObjects:curFilePath, [curFilePath stringByDeletingLastPathComponent], nil] retain];
lastLaunchPath = [[[NSBundle mainBundle] pathForResource:#"xxtask" ofType:#""] retain];
All three variables are null, but the XXTask object is not, as it can still execute code.
Errors :
2011-01-15 16:38:57.233 App[24179:a0f] PATH : (null)
2011-01-15 16:40:52.846 App[24212:a0f] LAST ARGS : (null)
2011-01-15 16:40:52.847 App[24212:a0f] LAST LP : (null)
2011-01-15 16:40:52.847 App[24212:a0f] Exception detected while handling key input.
2011-01-15 16:40:52.848 App[24212:a0f] *** -[NSCFArray insertObject:atIndex:]: index (1) beyond bounds (1)
When I don't draw the view by calling the delegate, everything is okay. I could put that view method in the XXTask class, but I'd rather find a neat explanation to this first. What happens when I call the delegate? (It's the first time I tried using delegate protocols)
Thanks for answering!
If this code looks horrible to you, I have two excuses :
I've been messing around for a long
time, read the memory management
docs and did the most silly things.
Reference-count memory management is quite new to me. I've
never made real big apps like this
one in the past, so I normally can't
do a lot wrong, but this time memory
is way more important.

If I understand this correct (it's pretty hard without any code) you store some information held in XXTask, right?
Do you retain/copy the data? If not, it gets released as soon the XXTask gets released.
If I misunderstand your question, I ask you to provide some code which demonstrates your problem. This makes finding errors much easier.

Related

Confusion with Passing Obj-C objects into methods and retaining the changes to that object

I'm writing a program and trying to learn more about threads, multiprocessing, and such.
My architecture is a Model/View/Controller type.
I have my own subclass of NSImageView (ThumbnailView) and I wanted to be clever and have it listen for a message to clear itself (so all the thumbnails just clear themselves without me having to loop through them).
The problem is my ThumbnailView is controlled by a ThumbnailViewController which is really the one listening for the message. When it gets the message it spins off a new thread with a class object that is the command (ClearThumbnailViewCommand). It passes as an argument a dictionary item containing the associated ThumbnailView object and a key. Within the ClearThumbnailViewCommand I set the image of the ThumbnailView object to be some neutral image (like gray.jpg).
All this works fine, however, the Thumbnail object that changed is not the same Thumbnail object that went in. So I figure I need to pass a pointer rather than the object. I remember something about using MyObject** as opposed to MyObject* and passing via &MyObject but I can't seem to untangle the various combinations. Not being able to reason it out I fell back to my, normally, foolproof system of trying random combinations of things but this time it's not helping.
It seems that even if I'm able to construct a class that passes a pointer (Not sure if I'm using these terms correctly), I'm not able to assign it correctly to the NSDictionary, which doesn't want an id** .
I'll try and include the basics below, if that helps at all.
ThumbnailVew : NSImageView {
ThumbnailVewController * _controller;
}
init {
_controller = [[ControllerClass alloc] initWithControlObject: &self];
}
ThumbnailVewController : ControllerClass {
id ** _controlObject;
}
initWithControlObject: (id**)object {
_controlObject = object;
}
Then when messages are posted a ThumbnailVewController method is called which ultimately does this…
Which, of course will not let me pass in &_controlObject
when it is all re-written so that I can pass _controlObject, I don't get an error, however the ThumbnailView I change is only local to the method.
if([command isEqualToString:#"CLEAR_THUMBNAILS"]) {
NSDictionary * dict;
dict = [[NSDictionary alloc] initWithObjectsAndKeys: &_controlObject, #"thumbnail", nil];
[self newThreadWithCommand:[[[ClearThumbnailViewCommand alloc] initWithArgument:dict] autorelease]];
}
Is this even possible?
Thanks for the feedback. I am indeed just trying to explore some various situations. I understand I may be going a round about way to get at some things but it does help me understand the boundaries more clearly.
In case it might help anyone else, I found a solution to my particular issue. As it turns out I was making the reference to the _controlObject in the init phase of the ThumbnailView. The object created during that phase is different than the ThumbnailView object created when awakeFromNib is called.
when I move the _controlObject assignment to the awakeFromNib method all works as I expected. (Of course I reset the code to not include any of the fancy ** and & declarations.
Again, thank you for helping me understand a bit more about this language. I'm starting to like it quite a bit.

ARC: Memory does not get reclaimed?

I am working on an iPad (only) app and I stumbled across a weird problem. The app gets terminated after a memory warning on iPad 1 but works fine on iPad 2.
I am using ARC and targeting iOS 5. I use nibs and most of my assets are displayed using UIImageViews. I also have a few hundred buttons and plenty of gesture recognizers... I re-watched the WWDC11 videos (sessions 323 and 322) on ARC and I don't seem to be doing anything special.
The app is UIImage intensive, I am doing lots of animations using UIImage. I am however using the initWithContentsOfFile constructor rather than the imageNamed call. I'm trying to prevent the images from being cached by the system.
I'm also using GCD to schedule sound effects and to animate views. I'm always doing this on the main thread.
The app uses a UINavigationController that never has more than 1 UIViewController on it's stack. I can confirm that this is true because the didReceiveMemoryWarning only gets called on the current view controller (I'm logging the call).
The thing I don't understand is why Instruments is reporting high numbers (as if the view controllers don't get deallocated) in both the Allocations and VM Tracker instruments. The Allocations instrument shows a small drop when I navigate from one view controller to another (which is what I expect) but the VM Tracker Instrument shows that the Dirty Size is not dropping when I do the same thing. Eventually the app uses too much memory and gets terminated (on iPad 1). When I get memory warnings on the iPad 2 the app does NOT get terminated though...
It feels as if my images, sounds or views don't get destroyed and the memory does not get reclaimed... My object hierarchy is very basic and there should not be any retain cycles of any sort. I don't even have simple delegates...
Do you have any suggestions? I really don't want to release this app only for the iPad 2 or newer... It's an app for kids and it would be a pitty... I'd be so much happier to learn that I'm doing something wrong, as I really want to make sure this app is the best it can be...
Cheers,
Nick
There are ways to say, 'optimise' your objects by setting their properties to nil when certain things aren't needed -- so while you can't write a dealloc method anymore, you can do self.object = nil (when pertinent) which ends up doing something like this in a non-ARC world for an 'retain' (i.e., strong) property:
- (void)setObject:(id)newObject
{
[object release]; // send release message to current object
object = newObject; // set reference to newObject
[object retain]; // send retain message to newObject
}
Now while in ARC you don't/can't write retain/release yourself in your code, the compiler inserts these calls for you, meaning that in practise, setting a property to nil would do this in the above example:
[object release]; // send release message to current object
object = nil; // set reference to nil
[object retain]; // send retain message to nil (no effect)
Moreover, this is just the tip of the iceberg -- you should make sure that there are no retain cycles in your code, which could be resulting in objects leaking without recourse to their destruction. This means, that there may be places where you're using strong references to a property (i.e., an object), when you should be using a weak property. The difference being, that strong references are retained, and weak references are assigned, the former having its retainCount incremented and the latter resulting in a property assignment that looks like this if handwritten:
- (void)setObject:(id)newObject
{
object = newObject;
}
I don't like answering my own question but I figured it could be helpful to future googlers. I implemented my own UIImage based animation and I no longer use the animationImages property. Turns out my memory issues are now gone as I no longer need to store all the images in memory and I load them as they are required using a timer.
I actually think that rolling out a custom animation is beneficial since it allows for callbacks and more powerful customisation options.
Once I'm happy with it and I feel like it's ready to be shared I will post the class(es) on GitHub.

Objective-C Object gone due to memory management in NSMutableArray

I have a problem regarding the memory management when adding objects to a NSMutableArray. Weird thing is that it's all working fine for the first 8 objects I add, but when adding a 9th, the application crashes when retrieving this object.
UploadStatus *status = [[UploadStatus alloc] initWithStatus:[NSString stringWithFormat:#"%#: %d", NSLocalizedString(#"uploadPictureSucceeded", #""), pic_id]
andImageInProgress:nil
imageForSuccess:nil
imageForFailed:nil];
[self.delegate notify:status];
[status release];
This is being done on several places with different texts. But this object contains my status that I display in a UITableView.
In the notify method of the delegate I add the UploadStatus object to the NSMutableArray and I reload the UITableView that shows the objects inside that array.
The first 8 times I add a UploadStatus object to the array and reload the table, it shows correctly. But the 9th time I get the error [CFString retain]: message sent to deallocated instance 0x5c655c0. This error occurs when reloading the table in the cellForRowAtIndexPath method.
Weird thing is that it always shows that the objects inside the NSMutableArray are out of scope like in this screenshot:
Nevertheless if I fetch the item, convert it into the UploadStatus class and get the status from it, it all goes smoothly (for the first 8 objects).
Does anybody have a clue why it goes wrong after adding the 9th UploadStatus object to the NSMutableArray?
Thanks a lot for your help!
The problem is with this code:
[NSString stringWithFormat:#"%#: %d", NSLocalizedString(#"uploadPictureSucceeded", #""), pic_id]
You aren't retaining the string, so it goes away on the next execution of the run loop. You're getting lucky with the first 8. They happen to not get overwritten for some reason, or possibly some other object is retaining them. But the 9th one isn't and you finally see the results of the mistake.
You need for the UploadStatus object to retain that string (and later release it).
I note that you're directly accessing your ivars in this block of code rather than using accessors. This is almost certainly the root of your problem (it is the #1 cause of memory management problems in ObjC). Switch to accessors and most of your memory management problems will go away.
You should also go ahead and run the static analyzer (Build>Analyze). It might shed light. The problem is likely not in the above code; it's somewhere that you're storing something, most likely in an an ivar.

How to implement CHCircularBuffer in iOS project?

for my game iOS project I need a ring buffer. It should work similar to a queue where elements go out and go in but the total amount of elements in the buffer should stay the same.
I implemented the ring buffer successfully using java but I am not so familar with objective-c. I found a ring buffer implementation on the web called CHCircularBuffer: https://bitbucket.org/devartum/chdatastructures/src/4d6d7194ee94/source/CHCircularBuffer.m However I failed implementing it correctly.
The circular buffer is a property of a class called TerrainManager which does all the mathematical terrain generation.
#interface TerrainManager : NSObject{
int terrainParts;
CHCircularBuffer* circularTerrainBuffer;
}
#property(nonatomic, retain) CHCircularBuffer *circularTerrainBuffer;
#end
This how the ring buffer is initialized in the implementation of TerrainManager
circularTerrainBuffer = [[CHCircularBuffer alloc] initWithCapacity:parts];
This creates an instance of the buffer and sets the size property to parts. Now I add objects to the buffer using addObject method:
[circularTerrainBuffer addObject:[NSNumber numberWithDouble:0.2]];
Sometimes this line receives an error "exec_bad_access". E.g. when I init the buffer with capacity of 15 everything is fine, with 20 I get the error.
I now try to access the buffer from the terrain class where the drawing takes place. But whenever I try to access the objects I get an "bad_access" error.
NSArray *arr = [terrainManager.circularTerrainBuffer allObjects];
E.g. this line would create the error.
So there is something wrong with my code. Maybe I dont understand the buffer and add objects the wrong way. I dont know. Any ideas or suggestions?
The snippets of code you are showing are correct. I implemented a small project to test CHCircularBuffer the way you specify and it works correctly. So, the problem must be somewhere else.
The only way around this is, IMHO, put a breakpoint on the line that fails and step into the addObject function to see where exactly it fails. The array could be reallocated in there, so may be this is failing and giving the bad access. The same for allObjects.
Anyway, I have to say that I could execute my test without any issue, adding objects, removing them from head and tail, and getting the array of all objects with no issues.
If you post more code, we can maybe help a bit more.

How do I initialize a variable instance which is an object properly?

I am an Objective C newbie and I'm sure this is an easy question but I can't figure this out:
I have a class which declares an instance variable called myDeck, which is part of a custom class I created called Deck. Right now I have this in my code:
In the #interface:
Deck *myDeck;
In my init method:
Deck *ourDeck = [[Deck alloc]init];
myDeck = ourDeck;
So this seems to create the myDeck just fine, and I can stick values in it and run it's methods for a while, but I'm running into a spot where it ceases to exist and I get an EXC_BAD_ACCESS error when trying to use it.
I have tried adding
[myDeck retain];
to no avail, it still fails in the same spots. I don't really know how I should be alloc and initting this, I have a feeling I am missing something, anyone?
Everything you're doing there seems right; except I think you might have a typo in your first example - the instance variable is called myDeck, not deck, right? If your object is disappearing it's because you called release or autorelease too many times. Memory management in Cocoa is pretty straightforward. Go check out the documentation for all the information you could ever want.