Memory Growth Mystery (Objective-C) - ios

I have a memory growth issue in my app.
Since
describing the full code here is intimidating,
I narrowed it down to this simple scenario where I switch back and forth between two view controllers to learn basic memory dynamics.
- (void)viewDidLoad {
[super viewDidLoad];
for (int i=0; i<100000; i++)
{
__weak NSString* str = [NSString stringWithFormat:#"abcsdf"];
str = nil;
}
}
This supposed to show no memory growth, because I allocate 'str' and deallocate 'str' by making 'str' becomes nil, thus losing the owner.
But, the memory keeps growing.
Everytime I load this view controller, the memory keeps growing and never coming back.
Can anyone tell me why is that?
I am using ARC.

Your code snippet includes several interesting things about iOS/OS X memory management.
__weak NSString* str = [NSString stringWithFormat:#"abcsdf"];
str = nil;
The code is the same as the following without ARC.
NSString* str = [[[NSString alloc] initWithFormat:#"abcsdf"] autorelease];
str = nil;
Because stringWithFormat: class method does not begin with "alloc", "new", "copy", or "mutableCopy". It's the naming rule. So the NSString object is retained by an Autorelease Pool. The Autorelease Pool might be in the main Runloop. Thus the NSString object was not deallocated immediately. It causes memory growth. #autoreleasepool solves it.
#autoreleasepool {
__weak NSString* str = [NSString stringWithFormat:#"abcsdf"];
str = nil;
}
The NSString object is deallocated at the end of #autoreleasepool code block.
By the way, [NSString stringWithFormat:#"abcsdf"] might not allocate any memory every time. The reason is that it's static string. Let's use this class for further explanation.
#import <Foundation/Foundation.h>
#interface Test : NSObject
+ (instancetype)test;
#end
#implementation Test
- (void)dealloc {
NSLog(#"Test dealloc");
}
+ (instancetype)test
{
return [[Test alloc] init];
}
#end
Here is test code for __weak.
#autoreleasepool {
NSLog(#"BEGIN: a = [Test test]\n");
__weak Test *a = [Test test];
NSLog(#"END: a = [Test test]\n");
a = nil;
NSLog(#"DONE: a = nil\n");
}
The result of the code.
BEGIN: a = [Test test]
END: a = [Test test]
DONE: a = nil
Test dealloc
You said deallocate 'str' by making 'str' becomes nil, thus losing the owner. It is not correct. a weak variable doesn't have ownership of the object. The Autorelease Pool does have the ownership of the object. That's why the object was deallocated at the end of the #autoreleasepool code block. Take a look at the other test code for this case.
NSLog(#"BEGIN: a = [[Test alloc] init]\n");
__weak Test *a = [[Test alloc] init];
NSLog(#"END: a = [[Test alloc] init]\n");
a = nil;
NSLog(#"DONE: a = nil\n");
You can see a compilation warning from the code.
warning: assigning retained object to weak variable; object will be
released after assignment [-Warc-unsafe-retained-assign]
__weak Test *a = [[Test alloc] init];
^ ~~~~~~~~~~~~~~~~~~~
[[Test alloc] init] doesn't register the object to Autorelease Pool. Well, no need #autoreleasepool any more. And a is __weak variable, so the object will not be retained from anything. Thus the result is
BEGIN: a = [[Test alloc] init]
Test dealloc
END: a = [[Test alloc] init]
DONE: a = nil
No ownership no life. The object was deallocated immediately after it was allocated. I think you wanted to write the code without __weak as the following.
NSLog(#"BEGIN: a = [[Test alloc] init]\n");
Test *a = [[Test alloc] init];
NSLog(#"END: a = [[Test alloc] init]\n");
a = nil;
NSLog(#"DONE: a = nil\n");
The result is as expected. The object was released by assigning nil to the strong variable a. Then no one has the ownership of the object, the object was deallocated.
BEGIN: a = [[Test alloc] init]
END: a = [[Test alloc] init]
Test dealloc
DONE: a = nil

Related

"Assigning retained object to weak property" with init?

While playing around with ARC, I noticed that when I have a weak String:
#property (weak, nonatomic) NSString *myString;
And then if I were to do this:
self.myString = [[NSString alloc] init];
or even
[[NSString alloc] initWithString:#""]
Xcode immediately warns me "Assigning retained object to weak property; object will be released after assignment". And while I can understand that, because its reference count is 0, why does this work with no warnings:
self.myString = #"";
What difference does it make for ARC?
Because when you have a weak property, ARC does not increase the reference count...
So when you write:
self.myString = [[NSString alloc] init];
ARC adds a release just after, because it is the same as writing:
[[NSString alloc] init];
The main difference with #"" it is that it's a static string, in a way retained somewhere else...
Each time you use #"" it points to the same object.
Edit: The difference between #"" and [[NSString alloc] initWithString:#""] is that the first is a static string and is processed at compile time. If it is used elsewhere the other use will point to the same static string. The second, is processed at runtime. It really creates a new object, with its retain count and so on... That's why ARC does its job for the created instance (you specifically call a alloc) and why it does not care for static strings.
For ' Ok, so why does it show the same warning if I do this: [[NSString alloc] initWithString:#""] ? '
When you use [[NSString alloc] initWithString:#""], system will create a new string with #"" and it will be allocated in memory heap.

Memory leak in NSString stringWithUTF8String: with ARC enabled

In my application i have enabled the ARC. But in my application following lines gives me memory leaks according to instruments. It is in ios 7.0.
-(id)init{
variables = [[NSMutableArray alloc] init]; // Leak
events = [[NSMutableArray alloc] init]; //Leak
return self;
}
Update
But in my app if i do something like below it does not show me any leak. But i can't add items in to the variables.
-(id)init{
variables = [[[NSMutableArray alloc] init] copy]; // No Leak
events = [[[NSMutableArray alloc] init] copy]; //No Leak
return self;
}
--
NSString *utfString =[NSString stringWithUTF8String:(const char *)attr->children->content];//Leak
--
-(NSObject*)createObjectForClass:(NSString*)className{
Class cls = NSClassFromString(className);
NSObject *object = [[cls alloc]init]; //Leak
if(cls != nil){
CFRelease((__bridge CFTypeRef)(cls));
}
return object;
}
Does anyone has any idea how to fix this?
My guess right now is that your entire object is leaking, which means that the NSMutableArrays created in -init also leak. The version that calls copy isn't leaking because the copy is probably returning a singleton instance of NSArray (as there are zero elements in it, and it's an immutable NSArray, there's probably a singleton instance for that).

Why can I send messages to a deallocated instance of NSArray?

I just noticed a surprising behavior of NSArray, that's why I'm posting this question.
I just added a method like:
- (IBAction) crashOrNot
{
NSArray *array = [[NSArray alloc] init];
array = [[NSArray alloc] init];
[array release];
[array release];
}
Theoretically this code will crash. But In my case it never crashed !!!
I changed the NSArray with NSMutableArray but this time the app crashed.
Why this happens, why NSArray not crashing and NSMutableArray crashes ?
In general, when you deallocate an object the memory is not zeroed out, it’s just free to be reclaimed by whoever needs it. Therefore if you keep a pointer to the deallocated object, you can usually still use the object for some time (like you do with your second -release message). Sample code:
#import <Foundation/Foundation.h>
#interface Foo : NSObject
#property(assign) NSUInteger canary;
#end
#implementation Foo
#synthesize canary;
#end
int main(int argc, const char * argv[])
{
#autoreleasepool {
Foo *foo = [[Foo alloc] init];
[foo setCanary:42];
[foo release];
NSLog(#"%li", [foo canary]); // 42, no problem
}
return 0;
}
There are no checks against this by default, the behaviour is simply undefined. If you set the NSZombieEnabled environment value, the messaging code starts checking for deallocated objects and should throw an exception in your case, just as you probably expected:
*** -[Foo canary]: message sent to deallocated instance 0x100108250
By the way, the default, unchecked case is one of the reasons why memory errors are so hard to debug, because the behaviour might be highly non-deterministic (it depends on memory usage patterns). You might get strange errors here and there around the code, while the bug is an over-released object somewhere else. Continuing in the previous example:
Foo *foo = [[Foo alloc] init];
[foo setCanary:42];
[foo release];
Foo *bar = [[Foo alloc] init];
[bar setCanary:11];
NSLog(#"%li", [foo canary]); // 11, magic! (Not guaranteed.)
As for why is NSArray different from NSMutableArray, an empty array looks like a special beast indeed:
NSArray *foo = [[NSArray alloc] init];
NSArray *bar = [[NSArray alloc] init];
NSLog(#"%i", foo == bar); // yes, they point to the same object
So that might have something to do with it. But in general case, working with deallocated objects might do anything. It might work, it might not, it might spill your coffee or start a nuclear war. Don’t do it.
The simplest thing I can think of is that an empty NSArray is some kind of "constant" in the Foundation framework - e.g. an object similar to a NSString literal, which would have a retainCount (if you were to invoke it) of -1, and it could never be -dealloc'd.

NSCoding and saving/loading default values issue

I'm writing some code for an iPhone app, and I'm having issues getting default data to load in correctly. I am basing my code off some example from the "Learning Cocos2d" book by Ray Wenderlich.
It seems that even when I delete the app outright and try to start from fresh data that the app inconsistently either doesn't try to load the data, or incorrectly thinks that there is data, and loads null.
I'm using containsValueForKey to check if a value exists and then load it or load some default value, but even on a fresh installation the containsValueForKey finds data and doesn't load the defaults. In xcode's organizer I checked my device's file structure and the Documents folder, where I specified to save, doesn't look like it contains any files, so I'm not sure what it's grabbing.
My guess is that the problem is something to do with the initWithCoder function. It seems to mysteriously go through the function sometimes, but not all the time. Another weird thing is that I call [[GameManager sharedGameManager] save] when the player gets a highscore (not shown here, but the code is the exact same as this objectiveList, only an int) and it appears to save it correctly.
And now the code:
GCDatabase.h
#import <Foundation/Foundation.h>
id loadData(NSString * filename);
void saveData(id theData, NSString *filename);
GCDatabase.m
#import "GCDatabase.h"
NSString * pathForFile(NSString *filename) {
// 1
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
// 2
NSString *documentsDirectory = [paths objectAtIndex:0];
// 3
return [documentsDirectory stringByAppendingPathComponent:filename];
}
id loadData(NSString * filename) {
NSString *filePath = pathForFile(filename);
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
NSData *data = [[[NSData alloc] initWithContentsOfFile:filePath] autorelease];
NSKeyedUnarchiver *unarchiver = [[[NSKeyedUnarchiver alloc] initForReadingWithData:data] autorelease];
id retval = [unarchiver decodeObjectForKey:#"Data"];
[unarchiver finishDecoding];
return retval;
}
return nil;
}
void saveData(id theData, NSString *filename) {
NSMutableData *data = [[[NSMutableData alloc] init] autorelease];
NSKeyedArchiver *archiver = [[[NSKeyedArchiver alloc] initForWritingWithMutableData:data] autorelease];
[archiver encodeObject:theData forKey:#"Data"];
[archiver finishEncoding];
[data writeToFile:pathForFile(filename) atomically:YES];
}
GameManager.h
#interface GameManager : NSObject <NSCoding>{
NSMutableArray *objectiveDescriptions;
}
#property (nonatomic, retain) NSMutableArray * objectiveDescriptions;
+(GameManager*)sharedGameManager;
-(void)save;
-(void)load;
-(void)encodeWithCoder:(NSCoder *)encoder;
-(id)initWithCoder:(NSCoder *)decoder;
#end
GameManager.m (I added the load function, in an attempt to force it to load, but it doesn't seem to work)
+(GameManager*)sharedGameManager {
#synchronized([GameManager class])
{
if(!sharedGameManager) {
sharedGameManager = [loadData(#"GameManager") retain];
if (!sharedGameManager) {
[[self alloc] init];
}
}
return sharedGameManager;
}
return nil;
}
+(id)alloc {
#synchronized([GameManager class]){
NSAssert(sharedGameManager == nil, #"Attempted to allocate a second instance of the Game Manager singleton");
sharedGameManager = [super alloc];
return sharedGameManager;
}
return nil;
}
- (void)dealloc {
[objectiveList release];
[super dealloc];
}
- (void)save {
saveData(self, #"GameManager");
}
-(void)load {
loadData(#"GameManager");
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:objectiveList forKey:#"objectiveList"];
}
- (id)initWithCoder:(NSCoder *)decoder {
self = [super init];
if (self != nil) {
if ([decoder containsValueForKey:#"objectiveList"]) {
objectiveList = [decoder decodeObjectForKey:#"objectiveList"];
} else {
[objectiveList addObjectsFromArray:[NSArray arrayWithObjects:#"1",#"2",#"3",#"4",#"5", nil]];
}
}
return self;
}
#end
I have not read your full code.. But I found a problem in code....
You have not allocated memory to objectiveList array.. Unless and until you allocate memory to array, objects will not be added...
I think go for
objectiveList = [[NSMutableArray alloc] initWithArray:[NSArray arrayWithObjects:#"1",#"2",#"3",#"4",#"5", nil]];
instead of
[objectiveList addObjectsFromArray:[NSArray arrayWithObjects:#"1",#"2",#"3",#"4",#"5", nil]];
Check for the syntax.. Hope this may help as it troubled me also in the past where I forgot to allocate memory to the array.. And kept on adding objects resulting in null... :)
In case it doesn't solve your problem, I'll look for code later completely.. :)
I seem to see the problem. When the constructor is called the first time, the objectiveList is not even created as the "initWithCoder" is never called. You have to override the init method as well in order for the objectiveList array to be constructed. Basically, the code that is calling the init method is in here:
+(GameManager*)sharedGameManager {
#synchronized([GameManager class])
{
if(!sharedGameManager) {
sharedGameManager = [loadData(#"GameManager") retain];
if (!sharedGameManager) {
[[self alloc] init]; // GOES INTO INIT METHOD, NOT INITWITHCODER!
}
}
return sharedGameManager;
}
return nil;
}
On a side note, that singleton implementation gave me a headache. Just saying. :)
There is (as far as I can see from the code you have provided) a logic flaw in your code. Consider what would happen if decoder did not contain an objectiveList key; the else clause would execute, but you never allocated objectiveList so the addObjectsFromArray: call will silently fail.
To test this theory, alter your code as show below, and rerun. If the assertion fires then the above theory is correct, if not you need to hunt a bit more!
- (id)initWithCoder:(NSCoder *)decoder
{
self = [super init];
if (self != nil)
{
if ([decoder containsValueForKey:#"objectiveList"])
{
objectiveList = [decoder decodeObjectForKey:#"objectiveList"];
}
else
{
NSAssert(objectiveList, #"objectiveList must be non-nil to add objects.");
[objectiveList addObjectsFromArray[NSArrayarrayWithObjects:#"1",#"2",#"3",#"4",#"5", nil]];
}
}
return self;
}
By the way, objectiveList is never declared as an ivar... I am sort of assuming that objectiveList and objectiveDescriptions are meant to be the same.
The method in GameManager.m should look like this:
- (id)initWithCoder:(NSCoder *)decoder {
self = [super init];
if (self != nil) {
if ([decoder containsValueForKey:#"objectiveList"]) {
objectiveList = [[decoder decodeObjectForKey:#"objectiveList"] retain];
} else {
objectiveList = [[NSMutableArray alloc] initWithObjects:#"1",#"2",#"3",#"4",#"5", nil];
}
}
You have two cases: either objectiveList is present, in which case you have previously saved some data, or it is not present and you need to create the default data (1, 2, 3, 4, 5). In the code above, I have changed the first case to retain the array returned by decodeObjectForKey, since Apple's docs state that this method returns an autorelease object. You need to retain it here to prevent the memory from being reused for some other objects that are created later in your app. By not retaining objectiveList, when accessing it later you were probably accessing garbage results (i.e. random memory) rather than what you had just decoded.
On a similar note, in the second case where objectiveList was not already present - i.e. for a new install of the app where there is no saved data present - you are not allocating objectiveList before trying to add objects to it. I have changed this line to actually alloc the object (and therefore the memory required), and then init with the default values you want. Since you were previously trying to add items to an array that had not been created, you would again get garbage data when trying to access the values from it. Note that I assume you are using an NSMutableArray here, but you might also be using an NSMutableSet.

creating and releasing NSMutableArrays inside a GCDQueue. Is this a right way?

Here I am parsing my JSON Data in a GCD Queue . Here i am using a class method to get the values. In my GCD queue i am allocating and releasing some arrays . Is this a right way to do in a GCD queue or i have to use the __block specifier. Want to clear my confusion
+ (void)startProcessingFeeds:(NSData *)fetchedData{
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(concurrentQueue, ^(void) {
NSMutableArray *idArrayTemp = [[NSMutableArray alloc] init];
NSMutableArray *titleArrayTemp = [[NSMutableArray alloc] init];
NSMutableArray *filesArray = [[NSMutableArray alloc] init];
MBFeeds *feeds = [MBFeeds getFeeds];
NSString *response = [[[NSString alloc] initWithData:fetchedData encoding:NSASCIIStringEncoding] autorelease] ;
NSMutableDictionary *newsDic = [response JSONValue];
NSMutableDictionary *tempNested = [newsDic valueForKey:#"data"];
/* here i am taking the values from dictionary and storing that to a array in my Singleton class */
[idArrayTemp release];
[titleArrayTemp release];
[filesArray release];
});
}
If I understand you correctly you are reading data and then storing it in a singleton so you can access it somewhere else. As long as the singleton is ensuring the data is retained (by using retained properties for example) then there should be no problem with your code.
__block is only needed when you want to create a variable out side of a block and have it manipulated inside a block. An example is when using a block to process array data.

Resources