I have an NSOperation where inside its -main method I use [NSThread detachNewThreadSelector:#selector(aMethod:) toTarget:self withObject:anArgument];
aObject (instance variable of my NSOperation subclass) is a weak reference to an object of an autoreleased array returned inside the -main method...
-(void)main {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *clients = [group produceClients]; // clients array is an autorelease instance
self->aObject = [clients objectAtIndex:3]; // a weak reference, Lets say at index three!
[NSThread detachNewThreadSelector:#selector(aMethod:)
toTarget:self
withObject:#"I use this for another thing"];
// Do other things here that may take some time
[pool release];
}
-(void)aMethod:(NSString*)aStr {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// aStr object is reserved for another functionality
// I know that I could pass a NSDictionary with a number of entries, which would be
// retained by `detachNewThreadSelector` but then ...
// I wouldn't have to ask this question ! :)
// Do several things with aObject, which is a weak reference as described above.
NSLog(#"%#", [self->aObject.id]);
// Is it safe ?
[pool release];
}
I know that NSThread's detachNewThreadSelector method retains self and withObject:anArgument, but what happens to aObject ?? Is it sure that will exist during the execution of the detached thread (aMethod:) ? Self is retained by detachNewThreadSelector, does this mean that the pool of the -main thread will be delayed released since it is retained and thus the clients will exist and thus the aObject will exist ?
Or the -main (NSOperation) thread will finish execution and released before -aMethod (NSThread) finishes so it's unsafe to use aObject there ?
The real question is: When calling [NSThread detachNewThreadSelector:#selector(aMethod:) ...toTarget:self ...] from inside a thread, does the last thread being retained in a way that its autoreleased instances (clients array) are safe to be used in aMethod (self->aObject) (lets say via weak references) ?
Your approach seems highly unstable, but I'm not an expert on multithreading so I could be wrong. Your clients array is in the main autorelease pool, which you cannot be assured will wait till your aMethod thread is completed to drain. What about this:
-(void)main {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[NSThread detachNewThreadSelector:#selector(aMethod:)
toTarget:self
withObject:#"I use this for another thing"];
[pool release];
}
-(void)aMethod:(NSString*)aStr {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *clients = [group produceClients]; // clients array is an autorelease instance
self->aObject = [clients objectAtIndex:3]; // a weak reference, Lets say at index three!
[pool release];
}
With this approach, your clients array is in the thread's autorelease pool.
Related
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
I'm making a great deal of NSString manipulations within an autorelease pool. Problem is my program will sometimes crash before the pool drains. I'm wondering if there is a way to circumvent this problem by assigning nil to NSString. The assignment to userLetters happens a lot. See code below
Before
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
usersLetters = [usersLetters stringByReplacingCharactersInRange:NSMakeRange(indexUser, 1) withString:#"*"];
[pool drain];
After
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *temp = [usersLetters stringByReplacingCharactersInRange:NSMakeRange(indexUser, 1) withString:#"*"]; //remove that found character so it can't be reused again
usersLetters = nil;
usersLetters = temp;
temp = nil;
[pool drain];
I doubt what assigning to nil will help in what you want to achieve.
(I assume you mean that your program crashes because the memory is exhausted, otherwise, it is much more likely that you released to often somewhere, you should also run the Status Analyzer over your code.)
What you can do is to send a retain message to all objects that you still need (in your case usersLetters) and drain the pool afterwards. The objects that you still need should then have a retain count of 1, all other autoreleased objects should have been deallocated.
In your case, this would be
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
usersLetters = [[usersLetters stringByReplacingCharactersInRange:NSMakeRange(indexUser, 1) withString:#"*"] retain]; //sending retain to an object increases its retain count by 1 and returns the object itself
// some more stuff that needs memory
[pool drain];
// ...
[usersLetters release];
[pool release];
You don't mention if your project targets iOS or Mac OS X. If the latter, the best way to resolve this issue may be to simply use garbage collection.
If GC is not an option (which it is not on iOS), the idiomatic way to deal with this is to wrap a nested autorelease pool around your inner, fast-allocating operations. In this case you must retain any objects which need to outlive the nested pool, as mrueg has explained.
What is the best way to write a thread safe method?
I have the following method, sometimes i want to call it asynchronously (call it through a thread)
And sometimes i want to call it directly on the main thread.
Is there a problem with keeping the pool (alloc, release) even if I am not calling the method on a separate thread?
- (void)doStuff
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
//do some stuff here?
[pool release];
}
That's perfectly safe to call on the main thread. NSAutoreleasePool maintains a stack, so in this case you're just putting a new pool on top of the stack, then popping it off when you're done.
No, there is no problem with always using your own, method-local pool. You should be using [pool drain] instead of [pool release], though.
Regardless of what it would mean for the release pool, I would not recommend writing it this way. If you absolutely have to be able to call the method from the main and from other threads, code it like this:
- (void) doSomething {
}
- (void) doSomethingInBackground {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
[self doSomething];
[pool release]
}
This way you wrap the method in a method that takes care of the release pool.
I will also prefer the method mentioned by Ivo Jansch. Creating/draining a pool is valid but it may be a bit headache when you call the method doStuff: recursively or many times. But by following Ivo's method you can do stuff without a memory headache.
And also for a method to be thread safe you must use locks or #synchronized() whenever its needed, usually when accessing a array/buffer/dictionary. Your method should be something like this
- (void)doStuff{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
#synchronized(lock)
{
NSLog(#"Hello World");
}
[pool release];}
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.
I am allocating myMDD in main which contains an NSMutableArray instance variable (alloc/init-ed in init). When I add items to the NSMutableArray (frameList) I release after adding. The array and the objects it now contains are released at the bottom of main.
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
MDD *myMDD = [[MDD alloc] init];
Frame *myFrame = [[Frame alloc] init];
[myMDD addFrame:myFrame];
[myMDD release];
[pool drain];
return 0;
}
// METHOD_ mdd addFrame:
-(void)addFrame:(Frame*) inFrame {
[frameList addObject:inFrame];
[inFrame release];
}
// METHOD_ mdd dealloc
-(void)dealloc {
NSLog(#"_deal...: %#", self);
[frameList release];
[super dealloc];
}
My question is that the "static analyser" reports a potential memory leak, prefering to have the release for frame added main. (i.e)
int main (int argc, const char * argv[]) {
...
[myFrame release]; // Added
[myMDD release];
[pool drain];
return 0;
}
// METHOD_ mdd addFrame:
-(void)addFrame:(Frame*) inFrame {
[frameList addObject:inFrame];
// [inFrame release];
}
I can see why this is, if I alloc myMDD and never call addFrame then I need to release it. Maybe its just a case of adding a autorelease to myMDD, but would that work in the situation where I call addFrame and the NSMutableArray is releasing the object?
EDIT_001
Changed to ...
int main (int argc, const char * argv[]) {
...
[myMDD addFrame:myFrame];
[myFrame release];
myFrame = nil;
[myMDD release];
[pool drain];
return 0;
}
// METHOD_ mdd addFrame:
-(void)addFrame:(Frame*) inFrame {
[frameList addObject:inFrame];
}
gary
The reason you got that warning is because an NSMutableDArray retains any object put into it; likewise, when an NSMutableArray is released, it also releases any object contained within it. So let's look at your code.
This line:
Frame *myFrame = [[Frame alloc] init];
creates a new instance of Frame called myFrame. myFrame has a retain count of 1, because you used alloc/init to create it.
You then pass this to addFrame::
[myMDD addFrame:myFrame];
Which in turn puts it into an instance of an NSMutableArray:
[frameList addObject:inFrame];
At this point, inFrame and myFrame point to the same object. When added to the array, this object's retain count is incremented, so now it is 2.
Later on, back in main, you release myMDD, which releases frameList. Assuming frameList now has a retain count of 0, it is deallocated -- and, as an NSMutableArray, it releases any object it contains, which includes the object pointed to my myFrame.
So now myFrame's retain count is 1...so it doesn't get released, and you have a memory leak.
One Cocoa-y way to solve the problem is by autorelease myFrame:
Frame *myFrame = [[[Frame alloc] init] autorelease];
Which means it won't leak. Then, use the -[MDD dealloc] method in your second example (Edit_001). You're right that you shouldn't release inFrame in your addFrame method, since you're not retaining it.
As per convention, an add method should just retain the object if needed, not release it. And as a general rule, you should not release object that you did not retain, in your example the scope where you retained (created) the frame is not the same as in the addFrame method.
By scope I mean logic scope, not language scope.
In that particular example, you must call release just after addFrame. But the release should not be in the addFrame method.
In most cases, Cocoa provides class methods that initialize and return an autoreleased version of an object. i.e. [NSMutableDictionary dictionary] vs [[NSMutableDictionary alloc] init].
I advise to always use the class methods where possible if you're creating an object that you won't need to keep around or if you going to store it in a collection (NSArray, NSDictionary, NSSet, etc).
The general rule then is to only alloc objects your class owns directly (i.e. an instace or class variable, not inside a collection) and to use the class methods for all other cases.