Is #autoreleasepool pool required in secondary thread? - ios

I read the Apple doc about the function of #autoreleasepool. It says in a new thread, the programmer should declare a new autorelease pool using the following code in ARC.
#autoreleasepool{
//etc
}
But as my test, I did not add the #autoreleasepool block in my code, it still run well.My code is like below.
[NSThread detachNewThreadSelector:#selector(threadTest) toTarget:self withObject:nil];
- (void)threadTest
{
// #autoreleasepool {
for (int i=0; i<1000000; i++) {
NSNumber *num = [NSNumber numberWithInt:i];
[NSString stringWithFormat:#"%#",num];
}
// }
}
I used the Xcode Leaks instruments to see whether it has any memory leaks, but I did not find any memory leaks. So the result is that "It seems it is not required to declare the #autoreleasepool block in a secondary thread", is my word correct, can someone clarify this?

Related

Read and Write Circular buffer with Thread synchronisation in iOS Objective c

I've some data which is accumulated in a buffer and I need to read the data when buffer is having data. This i need to do with thread synchronisation. I've worked little with GCD, which I'm failing to do. please help how to do a circular buffer with read and write threads in synchronization.
My Code:
- (void)viewDidLoad {
[super viewDidLoad];
readPtr = 0;
writePtr = 0;
currentPtr = 0;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{
while(YES){
[self writeToBuffer:buffer[0] withBufferSize:bufferSize];
}
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{
while(YES){
float* newBuffer;
if(currentPtr>512){
newBuffer = [self readBuffer];
}else{
continue;
}
[self UseBuffer: newBuffer];
}
});
}
-(void)writeToBuffer:(float*)Values withBufferSize:(int)bSize{
[_lock lock];
for(int i=0;i<bSize;i++){
if(writePtr>1859){
writePtr = 0;
}
globalBuffer[writePtr] = Values[i];
writePtr++;
currentPtr++;
}
NSLog(#"Writing");
[_lock unlock];
}
-(float*)readBuffer{
[_lock lock];
float rBuffer[512];
for(int i=0;i<512;i++){
if(readPtr>1859){
readPtr = 0;
}
rBuffer[i] = globalBuffer[readPtr];
readPtr++;
currentPtr--;
}
NSLog(#"Reading");
[_lock unlock]
return rBuffer;
}
One of the key points of GCD is that it completely replaces the need for locks. So, if you are mixing GCD and mutex locks it is typically a sign that you're doing things wrong or sub-optimally.
A serial queue is, effectively, an exclusive lock on whatever is associated with the serial queue.
There a bunch of problems in your code.
while (YES) {...} is going to spin, burning CPU cycles ad infinitum.
The readBuffer method is returning a pointer to a stack based buffer. That won't work.
It isn't really clear what the goal of the code is, but those are some specific issues.

Why does block object not dealloc the same way NSObject does?

Here's a code snippet that illustrates the problem
__weak id ptr = nil;
__weak id ptr2 = nil;
#autoreleasepool {
void (^block)(void) = [^{
NSLog(#"hahaha");
} copy];
block();
[[(id)block rac_willDeallocSignal] subscribeCompleted:^{
NSLog(#"block will dealloc");
}];
ptr = block;
int blockRetainCount = CFGetRetainCount((__bridge CFTypeRef)block);
NSObject *obj = [[NSObject alloc] init];
[obj.rac_willDeallocSignal subscribeCompleted:^{
NSLog(#"Obj dealloc");
}];
ptr2 = obj;
int objRetainCount = CFGetRetainCount((__bridge CFTypeRef)obj);
NSLog(#"oK");
}
NSLog(#"after pool %# %#" , ptr, ptr2);
When I run this snippet, I will see Obj dealloc printed to the console but not block will dealloc. After the autorelease pool also I will see ptr still containing a valid refence to the block while ptr2 has been correctly reset to nil as expected. Why is there this difference? When do blocks get dealloc'ed at all?
Multiple problems that should convince you to not ever try such a thing:
One, CFGetRetainCount isn't guaranteed to return anything meaningful in the presence of ARC.
Two, there is no guarantee that a block uses retain counts at all. Blocks that don't capture any variables are allocated as static variables; copy, retain and so on do nothing at all to them, and they are never allocated or deallocated.
Third, there is no guarantee that any block will ever call dealloc when it is deallocated. And no guarantee that it will happen in the next OS or iOS version.

NSMutableArray can not release objects after invoke removeAllObjects

I'm a new guy for developing ios app.
#property (copy, nonatomic) NSMutableArray* dataBufferArray;
Following code run in a callback function.which invoked frequently.
[analyzer.dataBufferArray addObject:[NSNumber numberWithInteger:thisFrame]];
[analyzer.dataBufferArray removeAllObjects];
Code run in ARC.
I found the memory always growing! Finally IOS exit my application cause by huge memory consume.
My question is: why removeAllObjects can not release the memory?
How to resolve it?
post more code
static int analyze(SAMPLE *inputBuffer,
unsigned long framesPerBuffer,
AudioSignalAnalyzer *analyzer) {
SAMPLE *pSample = inputBuffer;
for (long i = 0; i SAMPLE_RATE){
NSArray* unitSampleArray = [analyzer.dataBufferArray subarrayWithRange:NSMakeRange(0, SAMPLE_RATE - 1)];
[analyzer.dataBufferArray removeObjectsInRange:NSMakeRange(0, SAMPLE_RATE - 1)];
//use thread to process
NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:analyzer
selector:#selector(decodeSound:)
object:unitSampleArray];
[analyzer.queue addOperation: operation];
}
// for protect
if (analyzer.dataBufferArray.count > SAMPLE_RATE * 12){
NSLog(#"sample in data buffer so big, need clear");
[analyzer.dataBufferArray removeAllObjects];
}
return 0;
}
As you can see, analyze function is callback by AudioQueueNewInput.
I used NSMutableArray add object NSNumber, and I always 'removeObjectsInRange:' them. I use instruments to check the memory, it always growing!
You are using ARC based project there is no need to release memory, it will automatically managed. Your problem is somewhere else please post some more code.

iOS memory leak in thread

After running my thread for a while, Instruments shows that __NSDate has steadily incementing its # living value.
My conclusion is that this tread does not dealocate objects. This line, however, causes compilation error NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
How can I force this thread to retain all its objects or how shall I create a proper thread with working ARC.
- (void) start {
NSThread* myThread = [[NSThread alloc] initWithTarget:self
selector:#selector(myThreadMainRoutine)
object:nil];
[myThread start]; // Actually create the thread
}
- (void)myThreadMainRoutine {
// stuff inits here ...
// Do thread work here.
while (_live) {
// do some stuff ...
[runLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.05]];
[NSThread sleepForTimeInterval:0.05f];
}
// clean stuff here ...
}
The autoreleased objects are probably the reason for the increasing memory usage,
but you cannot use NSAutoreleasePool with ARC. Replace
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// ...
[pool drain];
with
#autoreleasepool {
// ...
}
Update: You actually need two autorelease pools in your case. First of all, the
Threading Programming Guide states:
If your application uses the managed memory model, creating an
autorelease pool should be the first thing you do in your thread entry
routine. Similarly, destroying this autorelease pool should be the
last thing you do in your thread. This pool ensures that autoreleased
objects are caught, although it does not release them until the thread
itself exits.
And the last sentence gives the clue why you need another autorelease pool: Otherwise
all autoreleased objects created in the long-running loop would only be released when
the thread exits. So you have
- (void)myThreadMainRoutine {
#autoreleasepool {
// stuff inits here ...
while (_live) {
#autoreleasepool {
// do some stuff ...
[runLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.05]];
[NSThread sleepForTimeInterval:0.05f];
}
}
// clean stuff here ...
}
}
- (void)myThreadMainRoutine {
#autoreleasepool {
// stuff inits here ...
// Do thread work here.
while (_live) {
// do some stuff ...
[runLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.05]];
[NSThread sleepForTimeInterval:0.05f];
}
// clean stuff here ...
}
}

Why I get error EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0) when I use GCD

Could somebody, please, explain me why I get error EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0) in dispatch_semaphore_wait in the following code:
-(void) initialize {
dispatch_queue_t queue = dispatch_queue_create("My queue", NULL);
dispatch_semaphore_t sem = dispatch_semaphore_create(1);
self.queue = queue;
self.sem = sem;
self.myarray = [[NSMutableArray alloc]init];
[self.myarray addObject: [[MyObject alloc] init]];
}
-(MyObject *) method1 {
//do something
dispatch_semaphore_wait(self.sem, DISPATCH_TIME_FOREVER);
MyObject *obj = [self.myarray objectAtIndex:0];
dispatch_barrier_sync(self.queue, ^{
[self.myarray removeObjectAtIndex:0];
});
return obj;
}
-(void) method2:(MyObject *)object {
//do something
dispatch_barrier_async(self.queue, ^{
[self.myarray addObject:object];
dispatch_semaphore_signal(self.sem);
});
}
I found similar question Why does this code cause "EXC_BAD_INSTRUCTION"?, but in my case I am using ARC and I do not write explicitly nowhere dispatch_release(sem);
The sem you created in your initialize method is locally scoped to that method. It needs to be accessible to the other methods. If you have an iVar named sem that you're attempting to assign, you're shadowing it by declaring a local variable in initialize. (Same thing with queue, by the way.)
Also, you appear to have a typo here, in that you call dispatch_semaphore_wait(sen, DISPATCH_TIME_FOREVER); (i.e. se n vs se m)
You are allowing simultaneous access to the array self.myarray without sufficient protection. You modify the array with -addObject: and -removeObjectAtIndex: on the serial queue self.queue but you read from it using -objectAtIndex: without any protection. That means you may be reading from it at the same time you're writing to it, which is not safe. You need to also put the -objectAtIndex: call on the serial queue.
Also, you are using barrier functions with a serial queue, which doesn't make any sense.
-(MyObject *) method1 {
//do something
dispatch_semaphore_wait(self.sem, DISPATCH_TIME_FOREVER);
__block MyObject *obj;
dispatch_sync(self.queue, ^{
obj = [self.myarray objectAtIndex:0];
[self.myarray removeObjectAtIndex:0];
});
return obj;
}
-(void) method2:(MyObject *)object {
//do something
dispatch_async(self.queue, ^{
[self.myarray addObject:object];
dispatch_semaphore_signal(self.sem);
});
}
This kind of crash will happen when you are running a (vector)extension which is not supported on your CPU.
For example, in xcode 5 under "project-settings / build-settings / Code Generation, set the
"Enable Additional Vector extensions" to "AVX2". Build your executable.
Now run it on an:
Intel Core i5: it's going to crash (wherever the compiler decided to use avx2) with 'exc_i386_invop subcode=0x0'.
Intel Core i7: it will work.

Resources