NSKeyedUnarchiver memory leak issue - iphone

I have problem with this code, it's working on debug environment. On the instruments I'm seeing memory leak problem on this function, instruments is giving warning that
Category Event Type Timestamp Address Size Responsible Library Responsible Caller
27 SocialNetwork Malloc 00:19.951 0x3d64d20 80 Foundation -[NSKeyedUnarchiver _decodeArrayOfObjectsForKey:]
- (NSMutableArray *)GetDataInstanceToUserDefaults{
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSData *storedObject = [userDefaults objectForKey:#"MyDataKey"];
NSMutableArray *data;
if(storedObject != nil)
{
NSArray *savedArray = [NSKeyedUnarchiver unarchiveObjectWithData:storedObject];
if(savedArray != nil)
data = [[NSMutableArray alloc] initWithArray:savedArray];
else
data = [[NSMutableArray alloc] init];
}else{
data = [[NSMutableArray alloc] init];
}
return data;
}
I didn't understand where is the my problem ?
Thank you for your support
Edit : By the way I should give more detail about this problem,this function (as you can see) is storing my object. My object is custom class and storing in the NSMutableArray.
I already added these methods inside of the my custom class
-(void)encodeWithCoder:(NSCoder *)coder{
-(id)copyWithZone:(NSZone*)zone {
-(id)initWithCoder:(NSCoder *)coder{

I think the problem is most likely in the initWithCoder: method of your custom class. It is leaking but the analyzer reports it as being in the archiver.
Unrelated to your problem, I would caution you against using [[NSMutableArray alloc] init] to initialize collections, especially mutable collections. Instead use, [[NSMutableArray alloc] initWithCapacity:1]. I've seen strange problems using just init that were cleared up by using initWithCapacity.

Related

NSMutableArrays not holding their objects in a shared instance

I have a sharedInstance which consists of many NSMutableArrays which holds all my form data that is spread across many views in my App.
An example of their declaration in .h looks like
#property (atomic, retain) NSMutableArray *i_date;
#property (atomic, retain) NSMutableArray *i_tailNumber;
#property (atomic, retain) NSMutableArray *i_pic;
#property (atomic, retain) NSMutableArray *i_sic;
and in .m (- init)
self = [super init];
_i_date = [NSMutableArray array];
_i_tailNumber = [NSMutableArray array];
_i_pic = [NSMutableArray array];
_i_sic = [NSMutableArray array];
The shared instance function is
+(id) sharedInstance
{
static id sharedInstance = nil;
if (sharedInstance == nil) {
sharedInstance = [[self alloc] init];
}
return sharedInstance;
}
EDIT:
I instantiate all the arrays with an empty string on the first view as seen below
[_fd.i_date addObject:#""];
[_fd.i_tailNumber addObject:#""];
Finally I interact with my data this way in the prepare for segue function
[_fd.i_date replaceObjectAtIndex:leg withObject:transferDate];
rinse and repeate
My problem, which I've spent many hours investigating today by reading various blogs, the documentation, and all the wonderful resources here at SO, is that my arrays lose any objects I place in them when I segue to another view.
When I hit the code line above, I get the classic
signal SIGABRT
error, which complains that my arrays are empty (0 objects), so it cannot replace anything.
I suspect I'm not handling my shared instance properly and that ... maybe, perhaps, multiple instances of my arrays are being allocated, but I'm certain that I'm turned around in my understanding.
Any ideas what is occurring? I think I provided all the necessary code snippets but if I forgotten something just ask. Thanks again!
Jesse
EDIT:
You guys are FAST! :D I created my OBJ-C file with ARC enabled, so I think I am using it. I'm a C++ guy, mostly so this optional hand holding mem stuff is natively ambiguous to me.
EDIT:
The complete error is
2013-04-10 20:25:03.239 ProjectName[4230:c07] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM replaceObjectAtIndex:withObject:]: index 0 beyond bounds for empty array'
*** First throw call stack:
(0x1caf012 0x10ece7e 0x1c65ba9 0x7c7e 0x478b87 0x478c14 0x1100705 0x342c0 0x270a64 0x1100705 0x342c0 0x34258 0xf5021 0xf557f 0xf46e8 0x63cef 0x63f02 0x41d4a 0x33698 0x1c0adf9 0x1c0aad0 0x1c24bf5 0x1c24962 0x1c55bb6 0x1c54f44 0x1c54e1b 0x1c097e3 0x1c09668 0x30ffc 0x222d 0x2155)
libc++abi.dylib: terminate called throwing an exception
Assuming you are not using ARC, your init method is using poor memory management. Since you are directly accessing the ivars (and not the properties) you need to retain the arrays:
_i_date = [[NSMutableArray alloc] init];
_i_tailNumber = [[NSMutableArray alloc] init];
_i_pic = [[NSMutableArray alloc] init];
_i_sic = [[NSMutableArray alloc] init];
You are not using the properties but you create autoreleased objects.
Either use the properties like
+(id) sharedInstance
{
static id sharedInstance = nil;
if (sharedInstance == nil) {
sharedInstance = [[self alloc] init];
sharedInstance.i_date = [NSMutableArray array];
sharedInstance.i_tailNumber = [NSMutableArray array];
sharedInstance.i_pic = [NSMutableArray array];
sharedInstance.i_sic = [NSMutableArray array];
}
return sharedInstance;
}
or do as rmaddy suggested.
[_fd.i_date replaceObjectAtIndex:leg withObject:transferDate];
this lines crash, as the array yet doent have an object at index leg
try
[_fd.i_date addObject:transferDate];
You should alloc and init your NSMutableArrays:
_i_date = [[NSMutableArray alloc] init];
And then use your singleton class i.e.:
[[_fd sharedInstance] i_date] addObject...]
Unbeknownst, I used the function "didMoveToParentViewController", which is called after a new view is displayed if it's overloaded and used by the view. In it, I was clearing all the arrays, thinking that it would only be called WHEN moving BACK to the parent view, since the word "did" which is a past tense word. Since that isn't the case, I was clearing my arrays right after page load, which resulted in empty arrays during the segues.

Memory leak when using NSString inside for loop

I have 100 images in my resource bundle named like image1.jpg,image2.jpg.
Basically what i am trying to do is create path names to those images dynamically inside a for loop.
While testing in simulator,the images loaded fine and the app did not crash.But while testing the app with instruments i was shocked to see the heavy memory leak that was happening while i was creating the path1 object.
I am pasting the entire method here for reference
- (id)init {
self = [super init];
if (self) {
self.arrayImages = [[[NSMutableArray alloc] init] autorelease];
for(int i=1 ; i<100 ; i++){
NSString *str = [NSString stringWithFormat:#"Century%d",i];
NSString *path1 = [[NSBundle mainBundle] pathForResource:str ofType:#"jpg"];
[self.arrayImages addObject:path1];
}
}
return self;
}
As i have not made use of any alloc inside the loop i dont have any ownership and hence no right to release the object.What is the reason for this memory leak??
Kindly explain the problem and provide the necessary solution in order to fix it..
As always,any help is highly appreciated..
arrayImages is retaining path1, and so if you do not release arrayImages it will leak. How are you creating arrayImages, and are you releasing it anywhere?
Edited based on comments:
Make sure you release arrayImages in your -dealloc method like so: [arrayImages release]; (note the lack of self).
There is no leak in the code you've shown.
There are (at least) two possibilities:
You have a leak in code you didn't paste into your question
Everything is fine and Instruments gave you a false-positive
Your loop will create a lot of autoreleased variables. These won't be deallocated until after the loop has finished, but that's how it's supposed to work.
The reason for the leak would be this line right here:
NSString *str = [NSString stringWithFormat:#"Century%d",i];
By using convenience methods in Objective-C, what happens in the background is the following:
NSString *str = [[[NSString alloc] initWithFormat:#"Century%d", i] autorelease];
Not using alloc/init to create a weak reference is a misconception. You are always the owner of a created object, no matter how you create it. The convenience method simply does the alloc/init and autoreleases it for you.
Here's what I would suggest you do to avoid leaking memory:
- (id)init {
self = [super init];
if (self) {
self.arrayImages = [[[NSMutableArray alloc] init] autorelease];
NSAutoreleasePool *tmpPool = [[NSAutoreleasePool alloc] init];
for(int i = 1 ; i < 100 ; i++) {
NSString *str = [NSString stringWithFormat:#"Century%d",i];
NSString *path1 = [[NSString alloc] initWithString:[[NSBundle mainBundle] pathForResource:str ofType:#"jpg"]];
[self.arrayImages addObject:path1];
[path1 release];
}
[tmpPool drain];
}
return self;
}
Let me know if this works better for you.
-EDIT- Allocating the path1 object and releasing it after adding to arrayImages.

memory leak situation in iphone

I have a memory leak when i call a method that return me a string----
the method definition is as follows
-(NSMutableArray *)read
{
NSMutableArray *dataArray = [[NSMutableArray alloc] init];
//picking data from database here
return dataArray;
}
this show a big memory leak
i also tried--- NSMutableArray *dataArray = [[[NSMutableArray alloc] init]autorelease];
but this time leack checking process gets hanged
i also cannot release that array before return
please help
-(NSMutableArray *)read
{
NSMutableArray *dataArray = [[NSMutableArray alloc] init];
//picking data from database here
return dataArray;
}
Anything that uses the method read will expect to get back an object it does not own. However, as written here, dataArray is still owned at the point of return. You can't release it because that might make it go away altogether. You must, in this instance autorelease the array. You can either do this:
-(NSMutableArray *)read
{
NSMutableArray *dataArray = [[[NSMutableArray alloc] init] autorelease];
//picking data from database here
return dataArray;
}
or this
-(NSMutableArray *)read
{
NSMutableArray *dataArray = [[NSMutableArray alloc] init];
//picking data from database here
return [dataArray autorelease];
}
You say "leak checking process get hanged" but I'm really not sure what you mean by that. Whether it hangs, crashes or plays the Botswana National Anthem, you definitely need to autorelease the returned array and any other problem is actually a different problem. Possibly, you are forgetting to retain the data elsewhere.
Another answer more...
There are many conventions in cocoa/cocoa-touch, there is one of them that says that if a method has the prefix init then you will have the ownership of that object (hence you have to release it)
This is NOT your case, hence if you do:
DatabaseReader *dbReader = [[DatabaseReader alloc] init];
NSMutableArray *mutArray = [dbReader read];
[dbReader release];
you are NOT supposed to release mutArray. BUT, the object created HAS to be released by someone. So you can do as JeremyP wrote. alloc/init and put it into a autorelease pool inside read method implementation. Or, you can do:
-(NSMutableArray *)read
{
NSMutableArray *dataArray = [NSMutableArray array];
//IMPORTANT:
//Did you noticed that I am not using any method
//with init prefix for the creation of dataArray ?
//so I don't need to release by my self ;)
//picking data from database here
return dataArray;
}
Which is basically the same. ;)
Ownership of the returned object may be returned to the object that receives from this function. You may do some debugging with the object's retain count using something like this...
NSLog(#"Retain count: %i", [dataArray retainCount]);
Turn on the debugging console (Command + R in Xcode) to see the NSLog output.

UserDefaults/KeyedArchiver Frustrations

I'm working on a homework app that uses custom Assignment objects for each assignment. I am trying to store an NSMutableArray (casted to an NSArray via initWithArray:) in standardUserDefaults but I'm having trouble with saving and reloading the array.
I have a table view from which you can choose to add a new assignment (which loads NewAssignmentViewController). When you save the assignment, it is pushed back to an array in AssigmentsViewController. And then you call it every time you load the UITableView which shows the assignments.
Here is the relating code:
-(void)saveToUserDefaults:(NSArray*)myArray{
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
if (standardUserDefaults) {
[standardUserDefaults setObject:[NSKeyedArchiver archivedDataWithRootObject:myArray] forKey:#"Assignments"];
[standardUserDefaults synchronize];
}
}
-(void)retrieveFromUserDefaults{
NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
NSData *dataRepresentingSavedArray = [currentDefaults objectForKey:#"Assignments"];
if (dataRepresentingSavedArray != nil) {
NSArray *oldSavedArray = [NSKeyedUnarchiver unarchiveObjectWithData:dataRepresentingSavedArray];
if ([oldSavedArray count] != 0) {
[assignments setArray:[[NSMutableArray alloc] initWithArray:oldSavedArray]];
}
else {
assignments = [[NSMutableArray alloc] initWithCapacity:100];
}
}
}
-(void)backButtonPressed {
[self saveToUserDefaults:[[NSArray alloc] initWithArray:assignments]];
[self.navigationController popViewControllerAnimated:YES];
}
Please help. It does not load the array but does not give any error. Any tips about UserDefault or KeyedArchiver in general would be greatly appreciated.
Couple of things here:
If I understand you correctly, you're trying store an array whose contents are the assignment objects.
If you want to serialize these objects for storage into NSUserDefaults, the Assignment objects themselves need to conform the NSCoding protocol by overriding these methods:
- (void)encodeWithCoder:(NSCoder *)encoder;
- (id)initWithCoder:(NSCoder *)decoder;
Since you didn't post the code for your Assignment objects, dunno if you did this properly or at all. If you have you should be able to encode the object. See the Archives and Serializations Programming Guide for more.
As for NSUserDefaults, by my read, you're basically trying to store your application's object model there. Not the best idea. NSUserDefaults is best suited for use with light-weight persistent data: basic preferences, strings, scraps of universal data.
What I would do is write out your archived data to a file and load it when your view loads.
Here's some code from Beginning iPhone Development on that subject:
Creating an archive from an object or objects that conforms to NSCoding is relatively easy. First, we create an instance of NSMutableData to hold the encoded data and then create an NSKeyedArchiver instance to archive objects into that NSMutableData instance:
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
After creating both of those, we then use key-value coding to archive any objects we wish to include in the archive, like this:
[archiver encodeObject:myObject forKey:#”keyValueString”];
Once we’ve encoded all the objects we want to include, we just tell the archiver we’re done, write the NSMutableData instance to the file system, and do memory cleanup on our objects.
[archiver finishEncoding]; BOOL success = [data writeToFile:#”/path/to/archive” atomically:YES];
[archiver release];
[data release];
To reconstitute objects from the archive, we go through a similar process. We create an NSData instance from the archive file and create an NSKeyedUnarchiver to decode the data:
NSData *data = [[NSData alloc] initWithContentsOfFile:path];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
After that, we read our objects from the unarchiver using the same key that we used to archive the object:
self.object = [unarchiver decodeObjectForKey:#”keyValueString”];
You'd also need to get your application's documents directory to save and load the files.
It's a wildly useful book, full of drop in code snippets. The chapter on persistence might be helpful for you. You might be much happier using Core Data for this task, come to think of it.
I'm not sure if this will fix your problem, but you don't have to pull the array out of Defaults as NSData. Check the NSUserDefaults reference and you'll see that Arrays are valid default objects.

Objective c, Memory Leak, reading from sqlite and assigning values to a NSDictionary and NSAarray

I have a list of shops in a ListController file.
I've setted up a sqlite db, in which i've stored 60 shops.
On the top of the list i have a search bar.
I've made a class called DataController, that is responsible to load and store db datas.
#interface DataController : NSObject {
sqlite3 *database;
NSArray *shops;
NSDictionary* dictionaryOfShops;
}
#property (nonatomic, retain) NSDictionary *dictionaryOfShops;
#property (nonatomic, retain) NSArray* shops;
-(void)initializeShops;
initializeShops method loads data from the db, and stores results into the 2 props in this way:
-(void)initializeShops{
[dictionaryOfShops release];
[shops release];
NSMutableDictionary *dictionary = [[[NSMutableDictionary alloc] init] autorelease];
if (sqlite3_open(....))
NSString *query = ....
if (sqlite3_prepare_v2(database, [query UTF8String],-1, &statement, nil) == SQLITE_OK)
{
while (sqlite3_step(statement) == SQLITE_ROW) {
int rId = sqlite3_column_int(statement, 0);
char *rName = (char *)sqlite3_column_text(statement, 1);
Shop* s = [[Shop alloc] init];
s.ID = rId;
if(sName != nil) s.Name = [NSString stringWithUTF8String:rName];
NSString *shopID = [[NSString alloc] initWithFormat:#"%d",s.ID];
[dictionary setObject:s forKey:shopID];
[shopID release];
[s release];
}
sqlite3_finalize(statement);
}
[query release];
dictionaryOfShops = [[NSDictionary alloc] initWithDictionary:dictionary];
shops = [[NSArray alloc] initWithArray:[dictionary allValues]];
dictionary = nil;
[dictionary release];
//Sorting
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"Name" ascending:YES];
NSArray *sortedList =[self.shops sortedArrayUsingDescriptors:[NSArray arrayWithObject:sort]];
self.shops = sortedList;
[sort release];
}
The problem is that when user enters some text into the search
bar, I change the value of the query (adding LIKE....) and then call the initializeShops method again. This second time makes
so many leaks, (related to the Shop class properties) and
leaks also a NSDictionary and a NSArray.
Before posting this to you I've tried different solutions, but
at least this doesn't leaks anything the first time I call
initilizeShops.
I accept any suggestion, since I'm really stuck
on it.
MORE:
The really strange thing is memory management of my var dictionary and the 2 props shops and dictionaryOfShops. With this code
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
//add data to dictionary
dictionaryOfShops = [[NSDictionary alloc] initWithDictionary:dictionary];
shops = [[NSArray alloc] initWithArray:[dictionary allValues]];
[dictionary release]
Considering that dictionaryOfShops and shops are two properties (nonatomic,retain) synthesized, how can I change value to them without leaks?
The very first time I pass through this method nothing gets leaked, from the second time it starts to leak so many objects (the contents of the collections).
The first question is Why not just use Core Data? It is very likely going to be faster, will require less code, and will be significantly easier to maintain over time. To be blunt; SQLite is deceptively hard. Easy to get started, exceptionally difficult to get right.
In any case, the memory management of dictionary is wrong. It only isn't crashing because you swapped the order of the nil assignment and release as kennyTM suggested. I would suggest not creating an autoreleased dictionary.
Otherwise, the code as written seems pretty leakless at first glance. So:
Can you provide some more code?
Anything interesting memory wise
going on elsewhere?
Are you using threading at all (or
NSOperationQueue)?
Have you run under the Leaks
instrument and retrieved the
backtraces of allocation of the
specific objects being leaked?
dictionary = nil;
[dictionary release];
Please swap these 2 statements. In this form it means [nil release] which is a no-op.
Ok, I've found the error.
In my class Shop, i realize i didn't implement the method
-(void)dealloc
So when I release the old dictionary (to prepare for a new assignment), all the fields inside of it didn't get released.