I am working on multi-threading using the following codes in XCode4:
#import <Foundation/Foundation.h>
bool trigger = false;
NSLock *theLock=[NSLock new];
#interface Metronome : NSObject
+(void)tick:(id)param;
#end
#implementation Metronome
+(void)tick:(id)param{
while(1)
{
NSLog(#"TICK\n");
usleep(1000000);
[theLock lock];
trigger = true;
[theLock unlock];
}
}
#end
int main()
{
[NSThread detachNewThreadSelector:#selector(tick:)
toTarget:[Metronome class] withObject:nil];
}
There is no compiling error, but during execution the console pops up the following warning:
objc[688]: Object 0x100300ff0 of class NSThread autoreleased with no pool
in place - just leaking - break on objc_autoreleaseNoPool() to debug
I'm not familiar with the memory management of obj-C. Can someone explain this to me? Thanks a lot!
you need a thread pool.
-(void)someMethod {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//code that should be run in the new thread goes here
[pool release];
}
You could also considering using arc.
http://longweekendmobile.com/2011/09/07/objc-automatic-reference-counting-in-xcode-explained/
you have to create a NSAutoreleasePool for every thread that need invoke autorelease include main thread
int main()
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[NSThread detachNewThreadSelector:#selector(tick:)
toTarget:[Metronome class] withObject:nil];
[pool release];
}
Related
I have tested following code.
// Employee.h
#interface Employee : NSObject
#property(nonatomatic, copy) void (^print)(void);
#end
// Employee.m
#implementation Employee
#synthesize print = _print;
- (void)dealloc {
[_print release];
[super dealloc];
}
#end
// main.m
int main() {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
Employee* john = [[[Employee alloc] init] autorelease];
john.print = ^{
NSLog(#"block test %#", john);
};
[pool drain];
}
In this case, variable "john"'s dealloc is not called.
But if I don't log john variable(Just like as NSLog(#"block test")), then it's dealloc is called.
What would be wrong?
It's circular reference, which will prevent the affected instance to be deallocated in the system of reference-count memory management.
According to the documentation
In a manually reference-counted environment, local variables used
within the block are retained when the block is copied.
http://developer.apple.com/library/ios/documentation/cocoa/Conceptual/Blocks/Articles/bxVariables.html#//apple_ref/doc/uid/TP40007502-CH6-SW3
john got retained when the block is copied to print, so it's like that john has retained itself via print variable. Even john will be released by the pool's draining, the reference count would never reach zero and dealloc would never get called.
As tia said, you have a retain cycle here.
Here is the solution to get rid of it:
int main() {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
Employee* john = [[[Employee alloc] init] autorelease];
Employee* __weak weakjohn = john; // weak reference to john, to be used in the block.
john.print = ^{
NSLog(#"block test %#", weakjohn);
};
[pool drain];
}
In my code i am working with audio buffers, when i have a callback function that is being called many times per second. this callback is in a class that handle audio, and not in the main class of the app .
At the start i was getting this warning that is being log many times during callbacks:
Object 0x93cd5e0 of class __NSCFNumber autoreleased with no pool in place - just leaking - break on objc_autoreleaseNoPool()
Then i was told to put this line in the callback func :
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
then this error disappeared .
But i cant understand how is that possible that i alloc the pool so many times in 1 second- and maybe i have a memory issues.
I saw than that i have to put this at the end :
[pool drain];
so i have this :
OSStatus status;
status = AudioUnitRender(audioUnit,
ioActionFlags,
inTimeStamp,
inBusNumber,
inNumberFrames,
&bufferList);
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // that line added
//run on evert sample
int16_t *q = (int16_t *)(&bufferList)->mBuffers[0].mData;
for(int i=0; i < inNumberFrames; i++)
{
static int stateMachineSelector=1;
static int sampelsCounter=0;
// CODE TO HANDLE THE SAMPLES ...
}
[pool drain]; // issue here
what exactly i did here?
why is that ?
is that ok from memory aspect ?
thanks a lot .
When starting a Autorelease pool with [[NSAutoreleasePool alloc] init] all further objects receiving a autorelease or where created with the convenience allocator (like NSArray *ary = [NSArray array];, or UIView *view = [[[UIView alloc] init] autorelease]; are in that pool.
So:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *ary = [NSArray array];
[pool release]; // this will also release and dealoc the *ary from memory
If you have a callback running NOT no the main thread, you SHOULD do a new pool. If not, your objects may leek to nirvana. :)
If you process a lot of data with autorelease objects and you'd like to free the memory, then create a pool, process, release the pool.
The following code compiles and executes as expected.
#import <objc/objc.h>
#import <Foundation/Foundation.h>
BOOL loopValue = YES;
#interface myThread:NSObject
-(void) enterThread: (NSArray *) elemt count: (NSString *) x;
#end
#implementation myThread
-(void) enterThread : (NSArray *) elemt
{
NSLog (#" Inside mythread ");
NSAutoreleasePool *pool = [[ NSAutoreleasePool alloc] init];
int i;
int cnt =10;
for(i=0; i<cnt; i++) {
NSLog (#"Number of elemennts in array %i ", [elemt count]);
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
}
loopValue = NO;
[pool drain];
}
#end
int main ( int argc, char ** argv)
{
NSAutoreleasePool *pool = [[ NSAutoreleasePool alloc] init];
// id tobj = [[myThread alloc] init];
id tobj = [ myThread new ];
NSLog (#"Starting New Thread ");
[NSThread detachNewThreadSelector:#selector(enterThread:) toTarget:tobj withObject:[NSArray arrayWithObjects:#"ram",#"20",nil]];
while(1)
if ( loopValue )
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]];
else
break;
NSLog (#".. Exiting.. \n");
[pool drain];
return 0;
}
MY Question:
While compilation i do get the following warnings..
mythread.m:24:1: warning: incomplete implementation of class ‘myThread’ [enabled by default]
mythread.m:24:1: warning: method definition for ‘-enterThread:count:’ not found [enabled by default]
While Execution
WARNING your program is becoming multi-threaded, but you are using an ObjectiveC runtime library .... Removed due to redability]hich does not have a thread-safe implementation of the +initialize method. ......
What am i dong wrong ? how to avoid both warning/runtime errors.
The method you declared is enterThread:count: but the method you implement is enterThread:. Also, that warning you are getting, I'm sure I've only seen that from the old GNUstep runtime… but I guess not.
In objective-c manual it is written that something like
return [[[SomeClass alloc] init] autorelease];
can be done and then release is not necessary at any point, even not in the function that received this object. Who owns this object then? when will it be released?
The current NSAutoreleasePool does and will take care of the releasing when drained.
IBAction calls get wrapped into an NSAutoreleasePool that gets drained after the call.
For all non-IBAction calls the following would apply:
Say, you have these methods:
- (id)foo {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
SomeClass *bar = [self bar]; //bar still exists here
//do other stuff
//bar still exists here
//do other stuff
//bar still exists here
[pool drain]; //bar gets released right here!
//bar has been released
}
- (id)bar {
return [[[SomeClass alloc] init] autorelease]; //bar will still be around after the return
}
Consider another scenario:
- (void)foo {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//do other stuff
[self bar];
//do other stuff
[pool drain]; //the string from blee would be released right here;
}
- (void)bar {
[self baz];
}
- (void)baz {
NSString *string = [self blee];
}
- (id)blee {
return [NSString string]; //autoreleased
}
As you can see the autoreleased string object did not even have to be used in or get returned to the scope in which the pool was created.
NSAutoreleasePools exist on a stack (one per thread) and autoreleased objects get owned by the pool that's topmost at the time of the call to autorelease.
Update:
If you are dealing with a tight loop and want to keep the memory moderately low while not slowing down your loop, consider doing something like this:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
for (NSUInteger i = 0; i < 1000000000; i++) {
[NSString stringWithString:#"foo"];
if (i % 1000 == 0) {
[pool drain];
pool = [[NSAutoreleasePool alloc] init];
}
}
[pool drain];
However NSAutoreleasePool is highly optimized, so one pool per iteration usually isn't much of a problem.
To fully understand how NSAutoreleasePools work read this excellent article by Mike Ash
I need to call a method that starts some asynchronous code
MyClass* myClass = [[MyClass alloc] init];
[myClass startAsynchronousCode];
Now I can't simply release it as this would cause an error since the code is still running:
[myClass release]; // causes an error
What is the best way to deal with the memory?
You could have -[MyClass startAsynchronousCode] invoke a callback:
typedef void(*DoneCallback)(void *);
-(void) startAsynchronousCode {
// Lots of stuff
if (finishedCallback) {
finishedCallback(self);
}
}
and then instantiate a MyClass like this:
MyClass *myClass = [[MyClass alloc] initWith: myCallback];
myCallback might look like this:
void myCallback(void *userInfo) {
MyClass *thing = (MyClass *)userInfo;
// Do stuff
[thing release];
}
How are you invoking the asynchronous code? If you use NSThread +detachNewThreadSelector:toTarget:withObject:, you'll find that the target object is retained by the thread until it terminates and then it is released. So you can release the object immediately after the asynchronous message.
e.g.
#implementation MyClass
-(void) startAsynchronousCode
{
[NSThread detachNewThreadSelector: #selector(threadMain:) toTarget: self withObject: nil];
}
-(void) threadMain: (id) anObject
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
// do stuff
[pool drain];
}
#end
With the above, the following code is perfectly safe:
MyClass* myClass = [[MyClass alloc] init];
[myClass startAsynchronousCode];
[myClass release];
You must retain your myClass object internally in startAsynchronousCode method. And release it internally too after it finished.
This behavior used in NSURLConnection, UIAlertView and other async objects.
What I've always done is maintained an instance variable that points to the asynchronous object.
- (id)init {
myClass = [[MyClass alloc] init];
[myClass startAsynchronousCode];
}
- (void)myClassDidFinish:(MyClass *)myClass {
[myClass release];
}