I have prepared a class for storing data retrieved from db, and let's say I have 10 vars in it. What if I will reuse this class for different views and each view will use a different quantity of variables.
tableViewCell will pop-up 3 vars.
View1 will pop-up 6 vars.
View2 will pop-up 10 vars.
Will the unused data cause memory leaks?
A memory leak only happens when you delete all pointers to the memory before freeing it. If you reuse your data structure, you might have some unused memory, but it won't be a leak unless you never free it when the pointers go away (leaving you no way to free it ever again).
Unused variables have nothing to do with memory leaks. You want to see a memory leak?
- (void)leakABunchOfMemory {
for (int i = 0; i < 1000000000; i++) {
NSMutableString *usedButNotUsedCorrectly = [[NSMutableString alloc] initWithFormat:#"%d", i];
}
}
That's a memory leak. An object is created with every [NSMutableString alloc], and none can never be destroyed because you lose your reference to them as soon as that iteration of the loop ends. They just go on existing and taking up space, like text-based zombies that hunger for the RAM of the living. To avoid leaks in Objective-C code, follow the memory management rules, and the equivalent rules for any other libraries you use.
Related
I'm using ARC and NSCache which is created and stored on my app delegate. Then I call it from the controllers trough the app delegate. The NSCache stores images as they are loaded from an url and the memory usage goes up really quick. When I check the profiler for real memory usage, my app reaches even 320 MB of memory usage but on allocations it says it has just allocated 20-30 MB.
On my app delegate I set the cache as follows (it is an ivar):
cache = [[NSCache alloc] init];
[cache setCountLimit:100];
[cache setTotalCostLimit:1500000];
[cache setEvictsObjectsWithDiscardedContent:YES];
I implemented a button to experiment with NSCache and when I click on it it calls:
- (IBAction)eraseCache:(id)sender {
[[appDelegate cache] removeAllObjects];
}
On the profiler, the memory used does not go down, but it actually starts to get the images again, so I know the objects where removed. How can I release this memory usage at will using ARC? How can I get the size of my cache to know when to release it?
In ARC, once there are no pointers to an object, it's automatically released. If the only pointers you had to the object were in the cache, then they have been released.
Note that you don't actually have to remove the objects; if you assign the pointer to a new object (with the result that it no longer points at the old object) then the old object is deallocated.
Ex:
NSArray *array = [NSArray new];
array = [NSArray new]; //the original array gets deallocated because nothing points to it.
From the NSCache Class Reference:
The NSCache class incorporates various auto-removal policies, which
ensure that it does not use too much of the system’s memory. The
system automatically carries out these policies if memory is needed by
other applications. When invoked, these policies remove some items
from the cache, minimizing its memory footprint.
I'm in need of understanding how memory is managed in objective C.
I know the basics, if you create it and own it, you must release it yourself.
However, when it gets to code such as:
self.storeDict = [NSMutableDictionary dictionaryWithContentsOfFile:plistPath2];
Do I own this? Must I release this memory?
self.storeDict = [NSMutableDictionary dictionaryWithContentsOfFile:plistPath2];
//73.3% leak
totalCharacters = [storeDict count];
tagCounter = 1;
dictKeyArray = [[storeDict allKeys] mutableCopy];
//13.3% leak
When Instruments puts a bunch of percentages next to the highlighted leaks, what does that tell me? Does it tell me the size of the leak relative to the total amount of memory leaked?
And one last thing.. Is it normal for the amount of allocated memory to continuously rise? Or should it stabilize somewhere?
Thanks for all the help! Everything is greatly appreciated!
In most cases, you only own objects returned by methods whose names begin with "alloc", "new", "copy", or "mutableCopy". Of course, you also own anything to which you send -retain. Exceptions to these rules should be called out in the documentation for the non-conforming methods.
See https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html#//apple_ref/doc/uid/20000994-SW1
Instruments attributes a leak to the line where the object was created. However, that's not necessarily the code which leaked the object. If the pointer to the object was passed to some other code and that code did not balance its retains and releases, then that code is responsible for the leak. Instruments can show you the history of retains and releases for a specific object, and you'll have to review those to see which code is not discharging its ownership responsibilities properly.
Also, if an object is owned by another object and it's really that second object that was leaked, then everything it owned will have leaked "transitively" as it were. So, look for higher-level objects which have leaked before trying to track down low-level objects which have leaked. Often, it is the objects which have leaked fewer instances which are the root of a graph of leaked objects.
Whether it is normal for memory to keep rising or to stabilize, that depends a little. Usually, memory usage should stabilize. However, if your app really is doing more and more, then it may be normal for its memory usage to keep increasing. For example, if an app is receiving data over the network and accumulating results as it does so, then its memory usage would likely rise as more data arrives. But if it doesn't stop at some reasonable point, that's a problem. On an iOS device, the system will eventually kill it.
I am getting a memory leak in my objective-C code that I don't understand. I have this code in a method that gets called several times:
AnalyzeBpm *analyzer = [[AnalyzeBpm alloc] init];
while( sample != NULL)
{
//do something with analyzer
}
[analyzer release];
When I run this code through Instruments, I get a leak everytime I allocate Analyze Bpm(which is a couple of hundred times). I looked at my AnalyzeBpm class, and everything I allocate in that class gets freed or deallocated. So why is this code creating a memory leak?
When Instruments identifies a leak, it is showing you the line of code that is allocating the leak, not the line of code that causes the leak.
Somewhere something is retaining analyzer without releasing it. You need to find that unbalanced retain. It may or may not be in the AnalyzeBpm class.
Your alloc-init and release are balanced, so it has to be something else — something in your while loop.
I'm still wrapping my head around some of the nuances of memory management in objective-C, and came up with the following case I'm unsure about:
+ (NSDecimalNumber*)factorial: (NSDecimalNumber *)l {
NSDecimalNumber *index = l;
NSDecimalNumber *running = [NSDecimalNumber one];
for (; [index intValue] > 1; index = [index decimalNumberBySubtracting:[NSDecimalNumber one]]) {
running = [running decimalNumberByMultiplyingBy: index];
}
return running;
}
Here decimalNumberByMultiplyingBy and decimalNumberBySubtracting will be creating a lot of NSDecimalNumbers, which will get autoreleased eventually as I understand it, but I worry until that time the containing program will be hanging unto an awful lot of memory.
Should I be introducing an autorelease pool somewhere? (If so where?) Is that going to have a noticeable effect on performance (when compared to the side effect of so much memory in use)?
Is autoreleasing the right mechanism to use here? Should I look at breaking the loop apart and manually releasing memory as I'm done with it?
It's likely a n00b question, but I'm trying to get a flavour for what the best practice(s) is/are in this situation.
It's good practice to avoid creating a bunch of large autoreleased objects within a single pass of the run loop. You already seem aware of the solutions. You can use non-autoreleased objects and release them when you're done with them. You can also create an autorelease pool for sections of your code that create a large number of autoreleased objects. When an object is autoreleased, it gets released when the enclosing autorelease pool is released/drained. Using an autorelease pool would look like this:
NSAutoReleasePool *subPool = [[NSAutoreleasePool alloc] init];
// Code that generates a bunch of autoreleased objects.
[subPool release];
But, before you do any optimization, run some benchmarks and see if you actually need to optimize. My guess is that the method you've shown won't cause any problems. However, let's say you're trying to apply your method to a set of a million random integers within a single loop. In that case, you might benefit from using an autorelease pool.
Check out Apple's Memory Management Programming Guide for Cocoa for more details.
You could set up an autorelease pool within the loop, but why bother?
You are not going to be able to accumulate that many objects in this loop, because you're computing factorials, and the largest exponent an NSDecimalNumber can have is 127.
You'll get an overflow error before you even get to 100 iterations through the loop.
Note that the main autorelease pool gets emptied every time the application makes a trip through the main run loop, so the autoreleased values are not going to hang around very long.
Best way to determine the answer is to write it a few different ways and test. I don't think this is going to be a problem, though, NSDecimalNumbers are going to max out around ~100!, and 100 NSDecimalNumber objects probably won't make a bit of difference.
Answer to your other questions: In situations where it will matter you can manually release your objects. You can also create an autorelease pool inside that loop. Autorelease is super fast.
while(/*condition*/) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// code goes here
[pool drain];
}
Is there anyway to get int (or float) numbers into a NSString object without using alloc and a subsequent release?
int myInt = 25;
NSString *myString = [[NSString alloc] initWithFormat:#"%d",myInt];
...
[myString release];
EDIT:
Thanks for the answers, I should have been a little more clear in the question, I am particularly interested in using this on the iPhone. As #chuck stated I could use a convenience method, but I was under the impression that I should be avoiding these where possible on the iPhone for memory / performance reasons. I could be wrong though.
gary
There's no way to create an NSString without creating it at some point. But you could use a convenience constructor so you don't have the burden of ownership.
NSString *myString = [NSString stringWithFormat:#"%d", myInt];
It will still be created and destroyed (as everything must be), but you don't have to do it yourself.
I could use a convenience method, but I was under the impression that I should be avoiding these where possible on the iPhone for memory / performance reasons.
The “performance reason” is the cost of autoreleasing an object. This was, at one time, expensive on an iPhone. (I don't know whether it still is.) The only alternative is explicitly allocating it and releasing it yourself. As others have pointed out, you can't have an object without having allocated it, and you mustn't allocate it without releasing it. You need both things to happen one way or another.
The memory reason is simply that an autoreleased object lasts longer—specifically, until the autorelease comes due. Autorelease many objects, and your memory usage will pile up; pile it up high enough, and SpringBoard will tell your app to knock it off (and/or just kill it). The solution is to make the objects not last so long, which means either (1) creating and draining your own autorelease pool around a known batch of objects or (2) managing the objects' lifetimes (that is, allocating and releasing them) yourself.
This latter reason is not specific to the iPhone—it affects the Mac as well, although our ceiling is higher: Macs have more short-term memory, plus virtual memory, so we can get away with more memory usage. Even so, we Mac programmers should also try not to waste memory, partly because paging hell wrecks one's day, and partly because we will get email from users if our apps sit too high in the Activity Monitor list.
NSString *mystring = [NSString stringWithFormat:#"Hello: %d",myint];
it should be autoreleased if you create it this way
i think the convention is that unless you see the word init or alloc in the method name then it should return an autoreleased object. i.e. the object is added to the current autorelease pool and flushed when the app advances to next stage in lifecycle
You want
NSString *aStr=[[NSNumber numberWithInt:myInt] stringValue];
It returns an autoreleased string.
You could use an NSMutableString (-appendFormat:) or a standard C-string. However, fundamentally, no, you're going to have to allocate memory somewhere.
Why not define a macro... something like
#define intString(i1) [[[NSString alloc] initWithFormat:#"%d",i1] autorelease];
put it in your prefix header.
Most of the time autorelease will have zero impact on your app's overall memory usage. You only need to be concerned with autorelease if you're accumulating many object instances. For example:
for (int i = 0; i < 1000; ++i)
{
NSString* s = [NSString stringWithFormat:#"%d", i];
...
}
That example will accumulate at least 1000 different string instances before anything is released, which is not desirable. That's a situation where you would look for alternatives.
If you wanted to avoid creating a bunch of string instances you could use NSMutableString:
NSMutableString* s = [NSMutableString stringWithCapacity:20];
[s appendFormat:#"%d", 123];
...
[s setString:#""];
[s appendFormat:#"%d", 456];
...
It's questionable whether that's any faster than simply creating and releasing separate string instances, but that pattern may fit better with what you're trying to accomplish in your code.