we add an object to the pool and when the pool is drained the objects are flushed out of memory, and if i don't add them into the pool they will remain into the memory and can be accessed after the calling of pool drain method. What i have done in my code is that i have not added the object of my class into the pool and have called the method after the pool drain here's my code
#import <Foundation/Foundation.h>
#interface Myclass : NSObject
{
}
-(void)fun;
#end
#implementation Myclass
-(void)fun
{
NSMutableArray *arr = [[NSMutableArray alloc]init];
char ch[10];
NSString *str;
for(int i =0;i<3;i++)
{
scanf("%s",ch);
str = [NSString stringWithCString:ch];
[arr addObject:str];
}
for(int i =0;i<3;i++)
{
NSLog(#"The values of mutable array are: %#", [arr objectAtIndex:i]);
}
}
#end
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Myclass *obj = [[Myclass alloc]init];
[obj fun];
// insert code here...
NSLog(#"\nEnter pool drain");
[pool drain];
[obj fun];
return 0;
}
now when the fun method is called after pool drain each time i add a value to the array i get an error which says
NSautoreleaseNoPool(): Object 0x105a80 of class NSCFString autoreleased with no pool in place - just leaking Stack:(0x4dlfof ox3de442)
but even after this msg is shown i continued to add the data to my array it was working fine but every time showed the above msg. Can you please tell me why is this so?
Also i wanted to know is their any function with the help of which we can clear the console screen i tried ncurses.h but was not able to do that.
Please help me out regarding these two problems
You don't add any of your objects to the autorelease pool. When it is drained, it is empty. To add objects to the autorelease pool you must autorelease them. e.g. in fun
NSMutableArray *arr = [[[NSMutableArray alloc]init] autorelease];
In main:
Myclass *obj = [[[Myclass alloc]init] autorelease];
With those two modifications, you will see one of two things happen:
on the second [obj fun] after the drain you might get an exception for sending a message to a dealloc'd object.
you might get lucky and the memory for obj is still intact on the second [obj fun] in which case you should see a message posted to the console log saying that arr will leak because there is no autorelease pool in place.
Edit following the comment
This message
NSautoreleaseNoPool(): Object 0x105a80 of class NSCFString autoreleased with no pool in place - just leaking Stack:(0x4dlfof ox3de442)
is occurring because the method invoked by this line:
str = [NSString stringWithCString:ch];
tries to put the string into an autorelease pool before returning it to you. However, you have already drained the only pool you ever had, so the attempt fails. The string will therefore leak.
Related
Background
I come from a C background and find giving up manual memory management extremely distressing. The old objective c retain and release model was ok if not a little clumsy.
I have written a osX app that reads data from an unspecified data source with an unknown size and makes a visual presentation of the data. In order to stress the app I generated a very large dataset which generates over a hundred million objects in less that 3 minutes (timing is a guess) but when I tare down the view containing the mutable arrays with hundred million objects the app beach balled while deallocating the objects. I solved this problem by passing the array to a background thread which did the release. Anyway I had to redesign The app so it could be sandboxed and I decided to use ARC and now I am back to the same stress test and the beach balling is back. Is there a way I can get a background thread to do the release of the objects created under ARC or do I need to go back a non ARC design and what if I wanted to re write the app in Swift.
Regards Christian Andersen
Is there a way I can get a background thread to do the release of the objects created under ARC
Yes. Just assign nil to the variable on the background thread. But you should ensure that the variable is the only one variable for referring the value.
#import <Foundation/Foundation.h>
#import <pthread.h>
pthread_t g_mainThread;
#interface Test : NSObject
#end
#implementation Test
- (void)dealloc
{
NSLog(#"Test %p was dealloced on %s", self,
g_mainThread == pthread_self() ? "the main queue" : "global queue");
}
#end
// main
int main()
{
g_mainThread = pthread_self();
NSMutableArray *array = [[NSMutableArray alloc] init];
for (int i = 0; i < 10; ++i) {
Test *test = [[Test alloc] init];
[array addObject:test];
}
__block NSMutableArray *arrayOnGlobalQueue = array;
/*
* The array object was referenced from *array* and *arrayOnGlobalQueue* variables.
* The reference count of the array object is 2.
*/
array = nil;
/*
* The *array* variable doesn't refer the array object any more.
* The reference count of the array object is 1.
*/
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_async(queue, ^{
/*
* Assign nil to the variable for releasing the array object
*/
arrayOnGlobalQueue = nil;
/*
* The *arrayOnGlobalQueue* variable doesn't refer the array object any more.
* The reference count of the array object is 0.
* Thus the array object was dealloced on the background thread(queue).
*/
});
dispatch_main();
return 0;
}
The result is this.
Test 0x7f96aac033b0 was dealloced on global queue
...
But, in common situation, Autorelease Pool refers objects, and Autorelease Pool releases the objects on the main thread.
For instance, if you use NSMutableArray +array class method to instanciate the array object instead of +alloc and -init method,
NSMutableArray *array = [NSMutableArray array];
// NSMutableArray *array = [[NSMutableArray alloc] init];
It means the array object was registered to Autorelease Pool. So, the reference count of the array object is 2. You can avoid the situation using #autoreleasepool.
do I need to go back a non ARC design
Use release under non-ARC instead of assigning null to the variable under ARC.
what if I wanted to re write the app in Swift
Do exactly the same as Objective-C.
I made simple experiment and found some strange behavior. Here some code - part of long method with ARC enabled:
MKObject *obj = [[MKObject alloc]init];
NSMutableArray *temp = [[NSMutableArray alloc]init];
[temp addObject:obj];
obj = nil;
temp = nil;
//here deallocating is called on obj (MKObject)
//other stuff
but if I change NSMutableArray to NSArray and literal initialisation
NSArray *temp = #[obj];
deallocating executed before autoreleasepool closed, not after setting nil to all references. Did I missed something ?
A few observations:
In your first example, neither the MKObject nor the NSMutableArray is an autorelease object, so these objects will be deallocated immediately, not waiting for the autorelease pool to drain:
MKObject *obj = [[MKObject alloc] init];
NSMutableArray *temp = [[NSMutableArray alloc] init];
[temp addObject:obj];
obj = nil;
temp = nil;
In your second example, the NSArray is an autorelease object, so the NSArray (and therefore, the MKObject) will not be deallocated until the autorelease pool is drained.
MKObject *obj = [[MKObject alloc] init];
NSArray *temp = #[obj];
obj = nil;
temp = nil;
To understand why the array literal, #[], creates an autorelease object, one should note that it expands to +[NSArray arrayWithObjects:count:]. Autorelease objects are created whenever you instantiate an object with any method other than using alloc followed by an init (whether init, or one of the permutations, such as initWithObjects:).
As you observed, when an app creates autorelease object, the object will not be immediately be deallocated, but it will when the autorelease pool drains. Since we generally yield back to the runloop quickly (at which point the pool will be drained), the choice of autorelease objects or non-autorelease objects has little practical impact in simple cases. But if the app, for example, has a for loop in which it creates many autorelease objects without yielding back to the runloop, it could be problematic (especially if MKObject was large or you were doing this many times). For example:
for (NSInteger i = 0; i < 100; i++) {
MKObject *obj = [[MKObject alloc] init];
NSArray *temp = #[obj];
// Note, because they are local variables which are falling out of scope, I don't have to manually `nil` them.
}
Because we are instantiating autorelease NSArray objects in this example, the above would keep all 100 arrays and objects in memory until you yielded back to the runloop and the autorelease pool had a chance to drain. This means that the app's "high water mark" (the maximum amount memory it uses at any given time), would be higher than it might need to be. You could remedy this by either:
use a non-autorelease object (such as by using alloc/init) instead of using the array literal:
for (NSInteger i = 0; i < 100; i++) {
MKObject *obj = [[MKObject alloc] init];
NSArray *temp = [[NSArray alloc] initWithObjects:obj, nil];
}
or
by introducing your own, explicitly declared #autoreleasepool:
for (NSInteger i = 0; i < 100; i++) {
#autoreleasepool {
MKObject *obj = [[MKObject alloc] init];
NSArray *temp = #[obj];
}
}
In this final example, the autorelease pool will be drained for each iteration of the for loop, resolving any challenges with autorelease objects that would otherwise have their deallocation deferred until the end of the loop.
One final caveat: In general, methods that begin with alloc and init (or a variation of the init method), will not generate autorelease objects, whereas all other methods, such as arrayWithObjects:count: will generate autorelease objects. One notable exception is the NSString class, which due to internal memory optimizations, does not conform to this rule. So, if you have any doubt, you can employ your own manual #autoreleasepool if you are repeatedly instantiating and releasing objects, and you are unsure as to whether the objects are autorelease objects or not. And, as always, profiling your app with the Allocations tool in Instruments is a good way of observing the app's high water mark. For an illustration of how these various techniques can impact the memory usage of your app, see https://stackoverflow.com/a/19842107/1271826.
The array was being retained by the autorelease pool. As described in Clang's Objective-C Literals documentation, the array literal syntax expands to a call to +[NSArray arrayWithObjects:count:], which creates an autoreleased array.
A couple of things I see, though I'm not entirely clear on the question so I can't say which applies:
Adding an object to an NSArray or NSMutableArray increments the object's retain count.
In the first instance, you manually instantiate obj, which gives it retain count 1.
Adding it to the NSMutableArray makes its retain count 2.
In this case, obj = nil decrements retain to 1;
temp = nil tells the array to handle releasing its contents. Those w/retain count 0 get dealloc'd immediately.
In the 2nd instance with #[] literal creation, the literal syntax under the hood creates an autoreleased object using the method arrayWithObjects: count:. When this is no longer needed it goes to the autorelease pool for eventual deallocation.
It isn't an issue of the objects IN the array but the way the arrays themselves were created.
Edited my original response to address comments below - I was confusing the issue.
The error I receive is as follows:
int main(int argc, char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil); //breakpoint that says Thread 1: Program Received Signal: "EXC_BAD_ACCESS".
[pool release];
return retVal;
}
My two questions can be found at the bottom of this post :)
I am currently working on an assignment for an iOS programming class and have hit a road bump.
I have found a fix, shown below, but it doesn't make sense to me. Check it out:
#implementation MyClass
// This class method takes an (NSMutableArray *) and returns an NSString with its contents printed out.
+ (NSString *)myString:(NSMutableArray)anArray
{
// NSString *myString = [[NSString alloc] init]; OLD CODE THAT CAUSES MEMORY LEAK
NSString *myString = [[[NSString alloc] init] autorelease]; //NEW CODE THAT RELEASES FIRST ALLOCATION OF myString WHEN THE FIRST stringByAppendingFormat: IS CALLED
NSString *vp = VARIABLE_PREFIX; //#defined above to be #"%
for (id object in anArray) {
if ([object isKindOfClass:[NSString class]]) {
if ([object hasPrefix:vp]) {
myString = [myString stringByAppendingFormat:#"%#",[object substringFromIndex:1]];
}else{
myString = [myString stringByAppendingFormat:#"%#",object];
}
}else if ([object isKindOfClass:[NSNumber class]]) {
myString = [myString stringByAppendingFormat:#"%#",object];
}
}
return myString; //shouldn't I autorelease myString right before this line? NO NOT ANY MORE. THIS myString IS NOT THE ORIGINAL THAT I alloc-init, BUT AN AUTORELEASED OBJECT RETURNED BY THE stringByAppendingFormat: message.
}
When I try to send the message [myString autorelease];, the program crashes with the above error. It is working fine now as shown above, but I do not understand why.
Every time I send a message containing the "magic words" alloc, init, copy I have to call release, it don't I? Or are the rules different in a Class method (can the Class itself own a file?). I do not call retain in the object that is calling this file.
Here are my two questions:
Why does this crash when I try to release theDescription using autorelease?
Does my code create a memory leak?
This is my very first question on stack overflow! Thank you for your help!
Why does this crash when I try to release theDescription using autorelease?
Assuming you mean myString, it crashes because myString is already autoreleased. You got it by calling -stringByAppendingFormat:, which returns an autoreleased string. Now, you're probably thinking: "But I created it by calling +alloc, so I should release it." That's true, but NSStrings are immutable, and when you call -stringByAppendingFormat: you get a different string back, and that string is autoreleased. Autoreleasing it a second time is an error.
Does my code create a memory leak?
Yes, but not really. The "leaked" object is the empty string that you allocate in the beginning. You never release that string, so you've got a leak. However, NSString is apparently optimized so that [[NSString alloc] init] returns a singleton, so in this particular case it doesn't make any difference that the empty string isn't released. The other strings that are assigned to myString are all autoreleased, so none of those objects are leaked.
Why does this crash when I try to release theDescription using
With the updated code, the problem is that ypu are reassigning the pointer to myString using the methid which already returns an autoreleases object: stringbyappending, therefore if you call autorelease on this object which is already going to get autoreleased i will crash.
Aditionaly the first assugnment in the alloc init gives a memory leak when ypu reassign with stringbyappendingstring, since you lose the reference to the previously created string with alloc init and therefore you will never be able to release it.
Sorry for the formatting on my iPhone atm =)
I found an example of Objective-C/cocoa framework has the following code.
int main()
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Create an array
NSArray *month = [NSArray arrayWithObjects:# ... nill];
[pool drain];
}
Q1 : What's the magic behind this (Why do I need to have the NSAutoreleasePool code?)? What magic is happening between the NSAutoreleasePool and pool drain block? I see I don't need to release*month myself. Is this because it's inside the NSAutoreleasePool and pool drain block?
Q2 : With Xcode, I'm already given the main() function. In this case, how can I use the NSAutoreleasePool and pool drain?
For example :
int main(int argc, char *argv[])
{
//NSAutoreleasePool *pool = [[[NSAutoreleasePool] alloc] init];
return NSApplicationMain(argc, (const char **) argv);
}
Q1: The magic is that NSObject -autorelease instance method calls NSAutoreleasePool +addObject: class method. The NSObject instance is pooled in the current NSAutoreleasePool instance. And NSAutoreleasePool -drain instance method calls release method of pooled instances.
It is not the exactly same between Cocoa implementation of Apple and GNUstep, but it is similar.
NSObject.m
NSAutoreleasePool.m
I'm not sure why month is not released, it should be release by drain.
Q2: You can use NSAutoreleasePool wherever you want to use at. Instantiate a NSAutoreleasePool means the current pool will be changed by the new instance. drain will go back the current pool to the previous instance.
Besides NSApplicationMain never returns. It calls the exit function to exit the application and terminate the process.
Q1:
You don't need to release the month instance in the example you give because the NSArray class method you're calling (arrayWithObjects:) returns an instance that is autoreleased. By convention in Cocoa, class methods that start with the name of the class will return autoreleased instances of that class. These examples:
[NSString stringWithFormat:#"Holla %#", #"back girl!", nil];
[NSArray arrayWithObjects:#"Holla", #"back", #"girl!", nil];
Will both return instances of their respective objects that are autoreleased.
I'm fairly new to Objective C, but let me give this a crack:
The autorelease pool is a way for Objective C to handle garbage collection in a somewhat easier way than manually.
It does this by counting references, or in this case every time you call "retain" or "release".
So if you have an instance of an object in "a", you could do:
This puts it into the AutoreleasePool:
[a autorelease];
This tells the autorelease pool that you want to hold on to it for a while:
[a retain];
When you call [pool drain] now, it will notice that you have one reference to a, and it won't deallocate the memory. However, if you later call [a release], the next time [pool drain] is called, it'll see that there are no further references to a remaining, and deallocate the memory.
I have a sneaking suspicion that I just talked myself in circles without making a whole lot of sense, but here is the wikipedia article on reference counting: http://en.wikipedia.org/wiki/Reference_counting
In a very small number of cases in my iphone app, I get a crash after the for loop in the code below:
ABAddressBookRef addressBookInit = ABAddressBookCreate();
CFMutableArrayRef abContacts =
(CFMutableArrayRef)ABAddressBookCopyArrayOfAllPeople(addressBookInit); // get array of all contacts
CFArraySortValues (abContacts, CFRangeMake(0, CFArrayGetCount(abContacts)), (CFComparatorFunction)ABPersonComparePeopleByName, (void *)ABPersonGetSortOrdering());
NSArray *copypeople = (NSArray *) abContacts;
NSMutableArray *tempTheadlist = [[NSMutableArray alloc] init];
for (int i=0; i < copypeople.count; i++)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
ABRecordRef record = [copypeople objectAtIndex:i];
if (blah blah)
[tempThreadList addObject: someObject];
[pool release];
}
// POINT OF CRASH AFTER LOOP ENDS
if (tempTheadlist.count > 0)
[NSThread detachNewThreadSelector: #selector(loading_pictures:) toTarget:self withObject:tempTheadlist];
[tempTheadlist release];
[copypeople release];
CFRelease(addressBookInit);
Any reason why it should crash at any point here?
(1) ABAddressBookCopyArrayOfAllPeople() returns a CFArrayRef, not a CFMutableArrayRef.
(2) All the casts between CFArrayRef, CFMutableArrayRef, and NSArray are irrelevant. NSArray and CFArray are synonymous.
(3) The autorelease pool in the for() loop is irrelevant. There aren't any new objects being created in the for() loop and, thus, nothing to fall in the pool. Nor will objectAtIndex: retain/autorelease the objects.
(4) That you are sorting the array returned by ABAddressBookCopyArrayOfAllPeople() may likely be a crash trigger. That function is declared as returning a CFArrayRef() and it should be treated as immutable.
If the app is crashing, post the backtrace of the crash. Without that, it is hard to tell what is specifically triggering the crash. Is it crashing in the thread that ran the above code? ... or crashing in the newly created thread?