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

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.

Related

Memory Growth Mystery (Objective-C)

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

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).

NSMutableArray initializing from viewDidLoad

arrayOfElements = [NSMutableArray arrayWithArray:[someObj getArray]];
and
arrayOfElements = [[NSMutableArray alloc] init];
arrayOfElements = [someObj getArray];
What's the difference?
The first arrayOfElements does not seem to lose its objects when it returns count in numberOfRowsInSection:(NSInteger)section, but the second one does. I get EXC_BAD_ACCESS when I do it the second way.
EDIT:
Can I suppose now that this is the best way,
arrayOfElements = [[NSMutableArray alloc] initWithArray:[someObj getArray]];
because I am initializing an array with the contents of whatever will be autorelease'd, and I now have a fully independent array in the current class, that is viewDidLoad, oops sorry, ViewController.
This line creates an NSMutableArray from an existing array
arrayOfElements = [NSMutableArray arrayWithArray:[someObj getArray]];
This combination first creates an NSMutableArray and then instantly discards it replacing it with what is returned by [someObj getArray]
arrayOfElements = [[NSMutableArray alloc] init]; // Create new NSMutableArray
arrayOfElements = [someObj getArray]; // Throw away the newly created array and replace with the result of [someObj getArray]
If you are not using ARC then it is purely by luck that either would work.
In both cases arrayOfElements is being assigned an autorelease'd object - which will be cleared soon (most likely the next runloop). It is only by chance that nothing else has been written over this point of memory which allows one of your implementations to still work.
If you are not using ARC then really you should update your project to be using it will handle a lot of cases like this for you.
You should definitely be using properties (not bare ivars) as this will help reduce memory issues (for non-ARC) and give a more consistent interface to your code.
In your header (or class extension) declare the property like this
#property (nonatomic, strong) NSMutableArray *arrayOfElements;
Now for ARC you can simple do
[self setArrayOfElements:[[someObj getArray] mutableCopy];
for non-ARC you can do
NSMutableArray *array = [[someObj getArray] mutableCopy];
[self setArrayOfElements:array];
[array release]; array = nil;
Also note that getArray is a bad method name.
The use of “get” is unnecessary, unless one or more values are returned indirectly.
Coding Guidelines
When you are adding objects to mutable array from another array, try this:
[arrayOfElements addObjectsFromArray: [someObj getArray]];
If you're not using ARC, you need to make sure its retained.
if (arrayOfElements) {
[arrayOfElements release];
}
arrayOfElements = [[NSMutableArray alloc] initWithArray:[someObj getArray]];

Where are the memory leaks in my for ... in loop with addObject

There seem to be some memory leaks in the following loop:
NSMutableArray *array1 = [[NSMutableArray alloc] init];
for(SomeClass *someObject in array2){ //has already been populated;
if (someObject.field == desiredValue){
[array1 addObject:someObject];
}
}
//EDIT:
//use array1 for very secret operations
[array1 release];
Any ideas why?
Are you releasing all your retained properties in SomeClass of yours? Make sure in dealloc release all retained properties.. Make sure your SomeClass is leak free..

Objective-C: Fixing memory management in a method

I'm almost there understanding simple reference counting / memory management in Objective-C, however I'm having a difficult time with the following code. I'm releasing mutableDict (commented in the code below) and it's causing detrimental behavior in my code. If I let the memory leak, it works as expected, but that's clearly not the answer here. ;-) Would any of you more experienced folks be kind enough to point me in the right direction as how I can re-write any of this method to better handle my memory footprint? Mainly with how I'm managing NSMutableDictionary *mutableDict, as that is the big culprit here. I'd like to understand the problem, and not just copy/paste code -- so some comments/feedback is ideal. Thanks all.
- (NSArray *)createArrayWithDictionaries:(NSString *)xmlDocument
withXPath:(NSString *)XPathStr {
NSError *theError = nil;
NSMutableArray *mutableArray = [[[NSMutableArray alloc] init] autorelease];
//NSMutableDictionary *mutableDict = [[NSMutableDictionary alloc] init];
CXMLDocument *theXMLDocument = [[[CXMLDocument alloc] initWithXMLString:xmlDocument options:0 error:&theError] retain];
NSArray *nodes = [theXMLDocument nodesForXPath:XPathStr error:&theError];
int i, j, cnt = [nodes count];
for(i=0; i < cnt; i++) {
CXMLElement *xmlElement = [nodes objectAtIndex:i];
if(nil != xmlElement) {
NSArray *attributes = [NSArray array];
attributes = [xmlElement attributes];
int attrCnt = [attributes count];
NSMutableDictionary *mutableDict = [[NSMutableDictionary alloc] init];
for(j = 0; j < attrCnt; j++) {
if([[[attributes objectAtIndex:j] name] isKindOfClass:[NSString class]])
[mutableDict setValue:[[attributes objectAtIndex:j] stringValue] forKey:[[attributes objectAtIndex:j] name]];
else
continue;
}
if(nil != mutableDict) {
[mutableArray addObject:mutableDict];
}
[mutableDict release]; // This is causing bad things to happen.
}
}
return (NSArray *)mutableArray;
}
Here's an equivalent rewrite of your code:
- (NSArray *)attributeDictionaries:(NSString *)xmlDocument withXPath:(NSString *)XPathStr {
NSError *theError = nil;
NSMutableArray *dictionaries = [NSMutableArray array];
CXMLDocument *theXMLDocument = [[CXMLDocument alloc] initWithXMLString:xmlDocument options:0 error:&theError];
NSArray *nodes = [theXMLDocument nodesForXPath:XPathStr error:&theError];
for (CXMLElement *xmlElement in nodes) {
NSArray *attributes = [xmlElement attributes];
NSMutableDictionary *attributeDictionary = [NSMutableDictionary dictionary];
for (CXMLNode *attribute in attributes) {
[attributeDictionary setObject:[attribute stringValue] forKey:[attribute name]];
}
[dictionaries addObject:attributeDictionary];
}
[theXMLDocument release];
return attributeDictionaries;
}
Notice I only did reference counting on theXMLDocument. That's because the arrays and dictionaries live beyond the scope of this method. The array and dictionary class methods create autoreleased instances of NSArray and NSMutableDictionary objects. If the caller doesn't explicitly retain them, they'll be automatically released on the next go-round of the application's event loop.
I also removed code that was never going to be executed. The CXMLNode name method says it returns a string, so that test will always be true.
If mutableDict is nil, you have bigger problems. It's better that it throws an exception than silently fail, so I did away with that test, too.
I also used the relatively new for enumeration syntax, which does away with your counter variables.
I renamed some variables and the method to be a little bit more Cocoa-ish. Cocoa is different from most languages in that it's generally considered incorrect to use a verb like "create" unless you specifically want to make the caller responsible for releasing whatever object you return.
You didn't do anything with theError. You should either check it and report the error, or else pass in nil if you're not going to check it. There's no sense in making the app build an error object you're not going to use.
I hope this helps get you pointed in the right direction.
Well, releasing mutableDict really shouldn't be causing any problems because the line above it (adding mutableDict to mutableArray) will retain it automatically. While I'm not sure what exactly is going wrong with your code (you didn't specify what "bad things" means), there's a few general things I would suggest:
Don't autorelease mutableArray right away. Let it be a regular alloc/init statement and autorelease it when you return it ("return [mutableArray autorelease];").
theXMLDocument is leaking, be sure to release that before returning. Also, you do not need to retain it like you are. alloc/init does the job by starting the object retain count at 1, retaining it again just ensures it leaks forever. Get rid of the retain and release it before returning and it won't leak.
Just a tip: be sure that you retain the return value of this method when using it elsewhere - the result has been autoreleased as isn't guaranteed to be around when you need it unless you explicitly retain/release it somewhere.
Otherwise, this code should work. If it still doesn't, one other thing I would try is maybe doing [mutableArray addObject:[mutableDict copy]] to ensure that mutableDict causes you no problems when it is released.
In Memory Management Programming Guide under the topic Returning Objects from Methods (scroll down a bit), there are a few simple examples on how to return objects from a method with the correct memory managment.

Resources