My app is receiving memory warnings because it's asking for lot of memory. I try to release every allocation. However, sometimes I don't know how to do it.
For example: I have two pairs of .h and .m file. One of them makes connections with a server and the other with local SQLite.
Usually, the code which calls to a method from those files are like this:
-(NSMutableArray *) getRecentActivity{
LocalStorageController *local = [[LocalStorageController alloc]init];
return [local getRecentActivity];
}
getRecentActivity returns a NSMutableArray.
Well, in that piece of code we can see that I am allocating memory for the LocalStorageController but I never call to the release method so, I suppose, the more I call that function, the more memory I will be allocating.
If I call autorelease after init, it will crash.
Moreover, usually, I use this other kind of code:
ServerConnection *serv = [[ServerConnection alloc]init];
NSMutableArray list = [serv getMyListOfContacts];
Which uses ASIHTTPRequest and, if I call [serv release]; after the second line, the app crashes with EXC_BAD_ACCESS pointing to a line in ASIHTTPRequest library.
How is suppose to manage this situation?
Thank you very much!
The first case is easy;
-(NSMutableArray *) getRecentActivity{
LocalStorageController *local = [[LocalStorageController alloc]init];
NSMutableArray *tmp = [local getRecentActivity];
[local release];
return tmp;
}
The second case is hard to solve in a general way without seeing more of the actual code.
Using serv as a property would be fixing this retain/release problem.
In your .h:
#property (nonatomic, retain) ServerConnection *server;
In your .m:
#synthesize server;
- (void)dealloc {
[server release];
// The rest of your releases here...
[super dealloc];
}
- (void)yourMethod {
ServerConnection *myServConnection = [[ServerConnection alloc] init];
self.serv = myServConnection;
[myServConnection release];
NSMutableArray list = [self.serv getMyListOfContacts];
}
Just keep on using self.serv in this class from that point on and you won't have a problem with having the object being released.
Related
I am making an application which as a part of it needs to download information from 2 RSS feeds.
However these feeds will have that information used across the entire app and may or may not be reloaded as the user requires to see their selected UIViewController.
So my question is:
Is it advisable to setup these arrays on application load in the UIAppDelegate or is that a big no-no in regards to performance?
I have the code working, I am just wanting to know what the best place would be to execute it to minimize data usage and also at the same time be best accessible throughout the app?
Any help would be great, because there is a lot of code to shift around if I need to keep re-doing this.
Thanks for the advice.
PS I know there is a lot of reading I can do and am doing, using SO is part of this research to get developers opinions. I hope that it does not break any rules or upset people :-)
Create a File as an NSObject, do header like this;
#interface myData : NSObject
{
NSMutableArray *myDataArray;
}
#property (nonatomic, retain) NSMutableArray *myDataArray;
+ (myData *)sharedData;
#end
Then in the .m add this
#import “myData.h”
static myData *sharedData;
#implementation myData
#synthesize myDataArray;
+ (myData *)sharedData
{
if(!sharedData)
{
sharedData = [[myData alloc] init];
}
return sharedData;
}
- (id)init
{
self = [super init];
if(self)
{
myDataArray = [[NSMutableArray alloc] init];
}
return self;
}
Then in each of your VCs, simply #import "myData.h" and when you need it do this;
myData *localShared = [myData sharedData];
localShared.myDataArray = // This is your array and will look the same throughout
NSString *myStringObj = localShared.myDataArray[4]; // So you can do this kind of stuff
This from memory, I have not tested but I think this is very close. Hope it helps.
I ran analyze on my 1st iPhone app, and I see a few potential memory leaks. The app itself works fine on the simulator.
I would like to do the right thing and clear the potential memory leaks, but some are quite puzzling. Maybe someone could help me here?
Thanks in advance.
Pier.
Error 1) The Analyzer says "Potential leak of an object stored in tempDate and tempAns"
#import "Answer.h"
#implementation Answer
#synthesize answerTiming;
#synthesize xPosition;
#synthesize earlyOrLate;
#synthesize hit;
+ (Answer *) createAnswerWithTiming :(NSDate *)paramTiming andXPosition :(float) xPosition
{
NSDate * tempDate = [[NSDate alloc] initWithTimeInterval:0 sinceDate:paramTiming];
Answer * tempAns = [[Answer alloc] init ];
[tempAns setAnswerTiming:tempDate];
[tempDate release];
[tempAns setXPosition:xPosition];
[tempAns setEarlyOrLate:0];
[tempAns setHit:false];
return tempAns;
}
- (void)dealloc {
[answerTiming release];
[self release];
[super dealloc];
}
#end
Error 2) Analyzer says (see below)
- (void)viewDidLoad
{
[super viewDidLoad];
........
...
UIImage * perfectImage = [UIImage imageNamed: #"perfect.png"];
self.perfectImageView2 = [[UIImageView alloc]initWithImage:perfectImage];
// method returns an objective C content with a +1 retain count
[self.perfectImageView2 setFrame:CGRectMake(145.0f,
150.0f,
self.perfectImageView2.image.size.width,
self.perfectImageView2.image.size.height)];
self.view.backgroundColor = [UIColor whiteColor];
UIImage * levelUpImage = [UIImage imageNamed:#"levelup.png"];
self.levelUpImageView = [[UIImageView alloc] initWithImage:levelUpImage];
[self.levelUpImageView setFrame:CGRectMake(100.0f,
400.0f,
self.levelUpImageView.image.size.width,
self.levelUpImageView.image.size.height)];
//object leaked, allocated object is not referenced later in this execution path and has a retain count of +1
self.view.backgroundColor = [UIColor whiteColor];
}
Error 3)
- (NSMutableArray *) generateQuestionTapAnswers:(NSString *) answersString withFirstBeat: (NSDate *) firstBeatTime
{
NSArray * notesToDraw = [answersString componentsSeparatedByCharactersInSet: [NSCharacterSet characterSetWithCharactersInString: #" "]];
float noteValueOffset = 0.0;
NSMutableArray * answerArray = [[NSMutableArray alloc] init ];
// Method returns an objective C object with a +1 retain count
for (int i=1; i < notesToDraw.count; i++) // i = 0 is the time signature
{
.....
}
return answerArray;
// Object returned to caller as an owning reference (single retain count transferred to caller)
// Object leaked: Object allocated and stored into answerArray is returned from a method whose name generateQuestionTapAnswers does not start with copy, mutableCopy
}
Regarding your first and third warnings, the compiler will assume that you'll be creating an object in "a method whose name begins with alloc, new, copy, or mutableCopy" (see Advanced Memory Management Programming Guide). So, if you're returning a +1 object, make sure your method name begins with one of those four prefixes. If you create a +1 object without one of those prefixes, it won't be happy. So, for error #1, if you rename that method to be something like newAnswerWithTiming, that warning should go away. (If the calling method doesn't clean up after them, though, the warning will just be shifted to that routine, but let's take it one step at a time.)
Likewise for error #3, if you rename that method to be something like newAnswerArrayFromQuestionTapAnswers (or whatever ... I'm not sure if I understand your method name ... just make sure it starts with new), you'll similarly eliminate that warning. In both cases, just make sure to release it at the appropriate point because whomever called these two respective methods now "owns" those objects and is responsible for their cleanup.
As an aside, you don't need to do [self release] in your dealloc in your discussion of error #1. (Frankly, I'm surprised it doesn't generate an error.) You don't need it because if self wasn't already at retainCount of zero, you never would have gotten to dealloc in the first place.
Regarding error #2, how are the properties perfectImageView2 and levelUpImageView defined? If retain or strong, you're creating something with a +2 retainCount, and you probably want to add an autorelease after the alloc/init.
There's a little bit uncommon situation in my app, that is,
I have to reload some retain properties everytime when the view is going to appear,
the code looks like this:
// .h
#property (nonatomic, retain) NSArray *myData;
// .m
#synthesize myData;
- (void)viewWillAppear:(BOOL)animated {
... // get FetchRequest and so on
self.myData = [self.context executeFetchRequest:request error:&error]; // Line 1
[super viewWillAppear:animated];
}
- (void)viewDidUnload {
self.myData = nil;
[super viewDidUnload];
}
- (void)dealloc {
[myData release]; // Line 2
[super dealloc];
}
there are several points:
1st. as you see, the property "myData" is retain, so I think every I set some object for it, it would automatically retain that object?
2nd. I have to reload "myData" everytime the view will appear, just like the code of Line 1 above.
3rd. Since it is a retain property, I have to release it myself correctly.
Now, question is, do I correctly managed the memory without any leaking of "myData" using the codes above?
If the view would appear many times before it is dealloc, (like push in a further view in a UINavigationController and pop out for several times),
then myData would retain some object more than once, but I only release it in the dealloc for 1 once in Line 2, so is that ok?
But if I add this method the to viewController,which I think is more safe for avoiding memory leaks:
- (void)viewWillDisappear:(BOOL)animated {
self.myData = nil;
[myData release];
[super viewWillDisappear:animated];
}
- (void)dealloc {
// [myData release]; // don't release it here.
[super dealloc];
}
my app would crash after one or two times I push in and pop out the view,
So which one is really wrong?
Thanks a lot!
You are not only releasing it in Line 2, it will be also released in Line 1 when replaced as well as in viewDidUnload, so your code on top is just fine. The key is that
self.myData = anything;
is expanded to
[self->myData release];
self->myData = [anything retain];
so by assigning anything (including nil) you are already calling release implicitly. You could in fact replace Line 2 with self.myData = nil; to have never to call release since you don't have any explicit retain.
.h
#property (nonatomic, retain) NSArray *myData;
.m
#synthesize myData;
By including these lines in your code a setter and getter is created for your property myData. The setter generated at run time for objects looks something like this,
- (void)setMyData: (id)newValue
{
if (myData != newValue)
{
[myData release];
myData = newValue;
[myData retain];
}
}
The total effect is that whenever you access the property by appending self in front you are actually calling the setters and getters. So the following two lines are the exact same.
self.myData = nil;
[self setMyData:nil];
So your original code was already correct.
I must have misunderstood some of the memory management rules, because when I try to fix a memory leak, the App crashes. Let me show you some code:
calendarRequestLog is a property of type MutableDictionary in a singleton object, that exists as long as the App runs. Here's the declaration in the .h file:
#property (nonatomic, retain, readonly) NSMutableDictionary *calendarRequestLog;
I allocate it with (in init):
calendarRequestLog = [[NSMutableDictionary alloc] init];
I fill it with this (notice the retain, that creates the memory leak):
[calendarRequestLog setObject:[[NSMutableArray arrayWithObject:delegate] retain] forKey:date];
I sometimes access it with this:
NSMutableArray* delegates = [calendarRequestLog objectForKey:date];
if(delegates != nil) {
// add delegates
}
I empty it with this:
NSMutableArray* delegates = [calendarRequestLog objectForKey:date];
if(delegates != nil) {
for (id <ServerCallDelegate> delegate in delegates) { … }
// clear the request from the log
[calendarRequestLog removeObjectForKey:date];
}
Here's the code that crashes when I remove the retain above:
NSMutableArray* delegates = [calendarRequestLog objectForKey:date];
if(delegates != nil) {
if([delegates containsObject:delegate]) // crash
[delegates removeObject:delegate];
}
It crashes because delegates is deallocated but not nil. To be more precise, I get an EXC_BAD_ACCESS Exception.
All these methods may be called in different orders or multiple times.
I cannot figure out, why this happens. I thought, collections are supposed to retain their objects - as this array-object (delegates) is still in the collection, it should not be deallocated. Other code cannot be responsible, I showed you all occurrences of calendarRequestLog.
I appreciate all the help I can get!
#Edit
I think I got it.
I call the crashing method when the delegate gets deallocated, so that I do not call the delegate per accident later.
But: I retain the delegates in my calendarRequestLog, so it cannot get deallocated as long as this doesn't get called:
// clear the request from the log
[calendarRequestLog removeObjectForKey:date];
...which in turn, deallocates the delegate and calls the crashing method. As the calendarRequestLog has removed the delegates, but not yet the key, we crash.
Ok, I will solve this differently. Thanks for all the comments - thanks to you, I looked elsewhere!
Did you try retaining when fetching so nobody releases your object while you're using it?
NSMutableArray* delegates = [[calendarRequestLog objectForKey:date] retain];
if(delegates != nil) {
if([delegates containsObject:delegate]) // crash
[delegates removeObject:delegate];
}
[delegates release];
Common practice is the following, because you already retain in the .h file:
//create local instance, then copy that to the class wide var
NSMutableDictionary *_calendarRequestLog = [NSMutableDictionary alloc] init];
self.calendarRequestLog = _calendarRequestLog;
[_calendarRequestLog release];
Also, I don't really understand why you would retain here:
[calendarRequestLog setObject:[[NSMutableArray arrayWithObject:delegate] retain] forKey:date];
Why not just change that to:
[calendarRequestLog setObject:[NSMutableArray arrayWithObject:delegate] forKey:date];
Write instead
calendarRequestLog = [[NSMutableDictionary alloc] init];
this
self.calendarRequestLog = [NSMutableDictionary dictionary];
and try to use property instead ivar
I'm writing an iPhone app, and I'm surprised that there seem to be no NSQueue or NSStack classes in Apple's Foundation Framework. I see that it would be quite easy to roll my own, starting with an NSMutableArray, so I'll do that unless I've missed something. Have I missed something?
Here's my Stack class, in case it's useful to those who come after me. As you can see, the pop method involves enough code that you'd want to factor it out.
Stack.h:
#import <Foundation/Foundation.h>
#interface Stack : NSObject {
NSMutableArray *contents;
}
- (void)push:(id)object;
- (id)pop;
#end
Stack.m
#import "Stack.h"
#implementation Stack
// superclass overrides
- (id)init {
if (self = [super init]) {
contents = [[NSMutableArray alloc] init];
}
return self;
}
- (void)dealloc {
[contents release];
[super dealloc];
}
// Stack methods
- (void)push:(id)object {
[contents addObject:object];
}
- (id)pop {
id returnObject = [[contents lastObject] retain];
if (returnObject) {
[contents removeLastObject];
}
return [returnObject autorelease];
}
#end
as far as I know there is no generic class avaialbe. Try using the NSMutableArray, add via addObject and get first/last via objectAtIndex and removeObjectAtIndex.
Another easy way would be to extend NSMutableArray's capabilities by making use of Objective C's categories. You can do that by adding two files to your project:
NSMutableArray+Stack.h
#interface NSMutableArray (StackExtension)
- (void)push:(id)object;
- (id)pop;
#end
NSMutableArray+Stack.m
#import "NSMutableArray+Stack.h"
#implementation NSMutableArray (StackExtension)
- (void)push:(id)object {
[self addObject:object];
}
- (id)pop {
id lastObject = [self lastObject];
[self removeLastObject];
return lastObject;
}
#end
Now you can use a regular NSMutableArray in every other file of your project like a stack and call push or pop on that object. Don't forget to #import NSMutableArray+Stack.h in those files. Here is some sample code how you can use your new NSMutableArray as a stack:
NSMutableArray *myStack = [[NSMutableArray alloc] init]; // stack size = 0
NSString *aString = #"hello world";
[myStack push:myString]; // stack size = 1
NSString *anotherString = #"hello universe";
[myStack push:anotherString]; // stack size = 2
NSString *topMostStackObject;
topMostStackObject = [myStack pop]; // stack size = 1
NSLog("%#",topMostStackObject);
topMostStackObject = [myStack pop]; // stack size = 0
NSLog("%#",topMostStackObject);
The log output will be:
hello universe
hello world
I'm a bit late to this party, but are you aware of CHDataStructures?
http://cocoaheads.byu.edu/code/CHDataStructures
I have put a working iOS Objective C queue object on GitHub. The code was taken from various posts and by no means is owned by me.
https://github.com/esromneb/ios-queue-object/
If you see any problems please fork, and make a pull request!
Yes, an NSMutableArray doubles as a stack or queue. (It would be slightly inefficient as a queue.)
You could also use C++'s stack and queue adapter, but it makes memory management a bit messy if you want to store Objective-C objects with it.
ObjectiveSugar is a very popular CocoaPod that provides, among a bunch of other great stuff, push and pop API calls on NSMutableArray. Sure, it's not in the iOS SDK, but I'm sharing it here because I was looking for the same thing, and this was the solution I went with (and it certainly didn't hurt that we were already using this CocoaPod in our codebase).
No. You missed nothing. That's all. Objective-C is higher level language look like C. Low level control is not required.
Cocoa classes are designed for easier use than efficiency. If you want to deal with performance, you have an option of raw C (or C++) implementation. Otherwise, just use easy way. Of course, early-optimization is evil.
If you want a kind of encapsulation, just make a new class which contains NSMutableArray within it. Hide inner NSMutableArray and just expose what you want. But you'll realize this is unnecessary.