Can an autoreleased NSArray cause a memory leak? - ios

I am trying to create an NSMutableArray using arrayWithArray, add two objects, sort, and store to an ivar as an NSArray. My code looks like this:
NSMutableArray *_mutableItems = [NSMutableArray arrayWithArray:[self.mainViewController.someDictionary allKeys]];
[_mutableItems addObject:#"Buildings"];
[_mutableItems addObject:#"Parking"];
self.curItems = [_mutableItems sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
When I profile the app I get a memory leak for an NSArray after the view is popped. But what I don't understand is: aren't all of these objects autoreleased? Am I increasing the retain count when I assign it to the instance property?

Yes, setting the property is probably increasing the retain count. Specifically, _mutableItems will be autoreleased, but the array you create with sortedArrayUsingSelectoris retained by the property.
Does your property declaration include retain or copy?
#property (retain) NSArray *curItems;
If so, in your class dealloc method, make sure you call release on the array;
- (void)dealloc {
[curItems release];
[super dealloc];
}

Related

Xcode: Working In ARC and NSArrays

Okay so I know ARC auomatically releases objects in memory that don't have at least one retained pointer linked to it so just to clarify, if I were initialize object, objectA, inside a local block of code, then add it to a global array, _objects, and then proceed to remove objectA from that array outside of the scope in which objectA was initialized, would ARC automatically release objectA from memory, because I don't intend to ever refer to it again. See the example below
#implementation MXViewController {
NSMutableArray *_objects;
}
- (void)viewDidLoad {
_objects = [#[] mutableCopy];
NSObject *objectA = [NSObject new];
[_objects addObject:objectA];
NSObject *objectB = [NSObject new];
[_objects addObject:objectB];
}
- (void)someMethod {
[_objects removeObjectAtIndex:0];
// since objectA has no retained
// pointers linked to it, will it be
// released from memory as expected
// by ARC?
}
#end
Yes, it would. Arrays retain objects added to them, and release them when they are removed. Here is your example code with reference counts in comments:
#implementation MXViewController {
NSMutableArray *_objects;
}
- (void)viewDidLoad {
_objects = [#[] mutableCopy];
NSObject *objectA = [NSObject new]; //objectA has a reference count of 1
[_objects addObject:objectA]; //objectA now has a reference count of 2
NSObject *objectB = [NSObject new];
[_objects addObject:objectB];
} //objectA's reference count is 1 because it is out of scope
- (void)someMethod {
[_objects removeObjectAtIndex:0]; //objectA's reference count is 0 and it is deallocated
// since objectA has no retained
// pointers linked to it, will it be
// released from memory as expected
// by ARC?
}
Yes, it will be released in your example. To be sure: make a subclass of NSObject for which you implement the dealloc method; log something therein, and add objects of that type instead of NSObject to your array. Prove it to yourself!
Yes. When you add the object to the array, the retain count is incremented (the array retains the object). After removing the object from the array, the retain count will drop to zero and the object will be released.
It's important to understand that the release won't necessarily occur immediately after the object is removed from the array. If you were to access the removed object in your someMethod property, ARC would postpone the release of the object accordingly.
Yes, it will be deallocated.
The "history" of the file would be something like
+1 (alloc) (reference counter:1) - From the "NSObject *objectA = [NSObject new];" instruction
+1 (retain) (reference counter:2) - "[_objects addObject:objectA];"
-1 (autorelease) (reference counter:1) - At some unknown point that we don't need to know
-1 (release) (reference counter:0) - "[_objects removeObjectAtIndex:0];"
And, of course, when it gets to 0, it will be deallocated.

ARC two-way relationship

So I want to have multiple nodes that are connected. Every node has outgoing and incoming connections. But the NSMutableArrays are creating leaks although i'm using ARC. How can i get the objects to be released properly? I'm already using an autoreleasepool.
The code so far is:
#interface TestObj()
#property(strong) NSMutableArray *incoming;
#property(strong) NSMutableArray *outgoing;
#end
#implementation TestObj
#synthesize incoming,outgoing;
- (id)init
{
self = [super init];
if (self) {
incoming = [NSMutableArray array];
outgoing = [NSMutableArray array];
}
return self;
}
-(void)addIncoming:(TestObj *)incomingN {
if([incoming indexOfObject:incomingN] == NSNotFound) {
[incoming addObject:incomingN];
[incomingN addOutgoing:self];
}
}
-(void)addOutgoing:(TestObj *)outgoingN {
if([outgoing indexOfObject:outgoingN] == NSNotFound) {
[outgoing addObject:outgoingN];
[outgoingN addIncoming:self];
}
}
With ARC, as with manual memory management on iOS, you need to avoid retain cycles. If you have one object that is retaining a second, and the second is retaining the first, those two will never be deallocated even if nothing else references them, so you have a memory leak.
You need to make it so that you aren't referencing them like this. NSArray and NSMutableArray keep strong references to other objects. You can do something like the following to create a weak reference that you can story in the array:
NSValue *val = [NSValue valueWithNonretainedObject:object];
If you store val in the array, it won't have a strong reference to the object, so it can be deallocated. However, you have to be careful that you aren't creating a situation where some of your objects have no strong references, or they will get deallocated before you want them to.
Well this may sound basic, but have tried setting them = to nil?

NSCopying arrays of custom objects

I have a Singleton object that manages all my lists. We'll call it ListStore.
ListStore has a mutable array, which stores Lists.
#interface ListStore : NSObject
#property (nonatomic, copy) NSMutableArray *lists; // an array of List objects
end
Lists has a mutable array, which stores Things.
#interface Wanderlist : NSObject <NSCoding, NSCopying>
#property (nonatomic, copy) NSMutableArray *things; // an array of Thing objects
#end
At any time, a background process might go through ListStore and loop through and process all Lists, while a user might be interacting with a List.
To guard against "object was mutated while being enumerated" type errors, I do this:
// all of this is in a background thread
NSArray *newLists = [[ListStore sharedStore] lists] copy];
for (List *list in newLists) {
// yay, no more crashes, because I'm enumerating over a copied object, so the user
// can do whatever they want while I'm here
for(Thing *thing in list.things) {
// oh crap, my copy and the original object both reference the same list.things,
// which is why i'm seeing the 'mutation while enumerating" errors still
...
}
}
I originally thought that because I made a copy into newLists that all of its members would be properly copied. I now understand that not to be the case: I'm still seeing the "object was mutated while enumerated" errors, but this time it's happening on list.things.
Can I use NSCopying with my setup so that when I say:
[[ListStore sharedStore] copy];
It calls copyWithZone: on Lists, so I can then copyWithZone: on things?
I tried to set it up like this but copyWithZone: wasn't getting called.
I know I could simply say NSArray *newList = [list.things copy] but I'd like to get a better understanding of NSCopying at the very least.
Right before submitting this question I clicked on a question in SO's list of related questions, and found my solution.
Figured it doesn't hurt to post my solution.
Instead of this:
NSArray *newLists = [[ListStore sharedStore] lists] copy];
I had to do:
NSArray *newLists = [[NSArray alloc] initWithArray:[[ListStore sharedStore] lists] copyItems:true];
From the NSArray docs:
- (id)initWithArray:(NSArray *)array copyItems:(BOOL)flag
flag:
If YES, each object in array receives a copyWithZone: message to create a copy of the object—objects must conform to the NSCopying protocol. In a managed memory environment, this is instead of the retain message the object would otherwise receive. The object copy is then added to the returned array.
Once I used initWithArray:copyItems:, it automatically sent copyWithZone to all my List objects, and I was able to then manually perform a copyWithZone on list.things.

how to manage memory with using properties

my.h file
#interface myObject : NSObject {
NSMutableDictionary *myDictn ;
}
i have a property
#property (nonatomic,retain) NSMutableDictionary *myDictn ;
then in .m File
i have a allocated it from id
- (id)init {
if (self=[super init]) {
myDictn= [NSMutableDictionary alloc]init];
}
}
my Question is i have mentioned Retain in Property and i have allocated memory also (is Retain Count goes to 2 in (id)init )
so how to manage memory in this case ?
i am new to this so dnt have much idea regarding memory management .
one more thing if i have a method x in my code and i also allocated memory to myDictn then in that case also how can i use release .??
Thanks in Advance .
The proper way to initialize a retained property is:
NSMutableDictionary *newDict = [[NSMutableDictionary alloc] init];
self.myDictn = newDict;
[newDict release];
or alternatively:
self.myDict = [NSMutableDictionary dictionary];
The first method makes sure not to increase the already retained property's retain count by performing alloc,init (an action that increments retain count) on a temporary object.
The second method uses a convenience method for obtaining an autoreleased instance of the dictionary.
You should first refer to Cocoa Fundamentals Guide. Then, if you're targeting iOS > 4.3, you can rely on Automatic Reference Counting for memory management. But be sure to understand the base concepts and read the fundamental guide or you'll waste a lot of time programming in the dark.

Releasing NSMutableArray in dealloc causes the app to crash

I have a UIView which holds a UITableView. This table is being populated by an NSMutableArray.
I initialized both the array and the table in the UIView's initWithFrame: method.
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
bookTable = [[UITableView alloc]initWithFrame:CGRectMake(100, 108, 260, 535)];
bookArray = [[NSMutableArray alloc]init]; //this array will be populated later
//other codes
}
return self;
}
I release them in dealloc:
- (void)dealloc
{
[bookTable release];
[bookArray release];
[super dealloc];
}
The problem is when the app crashes in dealloc. It produces the error: [CALayer release]: message sent to deallocated instance 0x82db660
I looked everywhere in my code and I'm sure that I didn't overreleased anything. I decided to remove the [bookArray release] in dealloc, and it stopped crashing. I ran the Analyze and it doesn't give me any potential leaks.
Can anyone explain to me why releasing the array in dealloc causes the crash?
NOTE:
Both the table and the array are instance variables of the view.
EDIT:
Code for populating the array:
NSString *sql = [NSString stringWithFormat:#"SELECT BookName FROM books WHERE UserID=%d", userId];
booksArray = [sqlite executeQuery:sql];
Please note that I am using a sql wrapper.
Here lies the problem
booksArray = [sqlite executeQuery:sql];
The array returned by the sqlite is an autoreleased object. You must either retain it or remove release from `dealloc'.
booksArray = [[sqlite executeQuery:sql] retain];
Since it is autoreleased object, releasing it in dealloc results in crash when system also tries to release it.
And do remove the array initialization in init method, it's redundant.
Problem is here only, I assume.
booksArray = [sqlite executeQuery:sql];
the executeQuery method might be returning an autoreleased NSMutableArray or something like this might be written inside executeQuery
NSMutableArray* tempArray = [NSMutableArray arrayWithCapacity:0];
If its like this, it should already be in autoreleased mode, hence no release in dealloc method.
UPDATE
Also, as0sign property to the array.
#property (nonatomic, retain) NSMutableArray* booksArray;
And then, wherever you use it, use it as self.booksArray
self.booksArray = [sqlite executeQuery:sql];
This way, it would be proper memory management and then release the array in dealloc.
Change the return statement of your executeQuery method like [array autorelease]
Replace booksArray = [sqlite executeQuery:sql]; by booksArray = [[sqlite executeQuery:sql] retain];
If you wrote property for the booksArray like #property(nonatomic, retain) NSMutableArray *bookArray; then you can replace step 2 by self.bookArray = [sqlite executeQuery:sql];
Your issue is you are returning an released object from the executeQuery method (You mentioned it on your comment). That's why you are getting the error
I would suggest to create a property for the booksArray like this:
#property (nonatomic, retain) NSMutableArray *bookArray;
An when you populate it try this code:
self.bookArray = [NSMutableArray arrayWithArray:[sqlite executeQuery:sql]];
That'll create an autoreleased copy of the returned array which is then "auto"-retained by the property.
Of course you should make sure, that the array return by [sqlite executeQuery:sql] is autoreleased.

Resources