LembergSun
LembergSun

Reputation: 661

Performing loop iterations serially in Objective-C

I need to create a loop which iterations should be executed in one thread and one after another serially. I have tried to add every operation to queue in a loop with dispatch_sync and my custom serial queue myQueue

dispatch_queue_t myQueue = dispatch_queue_create("samplequeue", NULL);
void (^myBlock)() = ^{
  //a few seconds long operation
};

for (int i = 0; i < 10; ++i) {
    dispatch_sync(myQueue, myBlock);
}

But is doesn't work. I also have tried dispatch_apply but is doesn't work to.

I also tried to add operations to my queue without loop.

dispatch_sync(myQueue, myBlock);
dispatch_sync(myQueue, myBlock);
dispatch_sync(myQueue, myBlock);

But nothing works... So, why can't I do it?

I need it for memory economy. Every operation takes some memory and after completion saves the result. So, the next operation can reuse this memory. When I run them manually (tapping button on the screen every time when previous operation is finished) my app takes a little bit of memory, but when I do it with loop, they run all together and take a lot of memory.

Can anyone help me with this case? Maybe I should use something like @synchronize(), or NSOperation & NSOperationQueue, or NSLock?

Upvotes: 0

Views: 942

Answers (3)

Sunil_Vaishnav
Sunil_Vaishnav

Reputation: 425

It is too late to answer this question but recently I faced exactly same problem and I create on category(NSArray+TaskLoop) over NSArray to perform iteration serially as well as parallely

you can download same from here https://github.com/SunilSpaceo/DemoTaskLoop

To perform iteration serially you should use

[array enumerateTaskSequentially:^(.... ];

put your iteration in block and call completion(nil) when you done with that iteration

Do not forgot to call completion block otherwise it will not go to next iteration

Upvotes: 0

rob mayoff
rob mayoff

Reputation: 385760

This is apparently your real problem:

I need it for memory economy. Every operation takes some memory and after completion saves the result. So, the next operation can reuse this memory. When I run them manually (tapping button on the screen every time when previous operation is finished) my app takes a little bit of memory, but when I do it with loop, they run all together and take a lot of memory.

The problem you describe here sounds like an autorelease pool problem. Each operation allocates some objects and autoreleases them. By default, the autorelease pool is drained (and the objects can be deallocated) at the “end” of the run loop (before the run loop looks for the next event to dispatch). So if you do a lot of operations during a single pass through the run loop, then each operation will allocate and autorelease objects, and none of those objects will be deallocated until all the operations have finished.

You can explicitly drain the run loop like this:

for (int i = 0; i < 10; ++i) {
    @autoreleasepool {
        // a few seconds long operation
    }
};

You attempted to use dispatch_sync, but a queue doesn't necessarily run a block inside a new autorelease pool. In fact, dispatch_sync tries to run the block immediately on the calling thread when possible. That's what's happening in your case. (A queue is not a thread! Only the “main” queue cares what thread it uses; other queue will run their blocks on any thread.)

If the operation is really a few seconds long, then you should definitely run it on a background thread, not the main thread. You run a block on a background thread by using dispatch_async. If you want to do something after all the operations complete, queue one last block to do the extra something:

dispatch_queue_t myQueue = dispatch_queue_create("samplequeue", NULL);

for (int i = 0; i < 10; ++i) {
    dispatch_async(myQueue, ^{
        @autoreleasepool {
            //a few seconds long operation
        }
    });
}

dispatch_async(myQueue, ^{
    // code to run after all long operations complete
});

dispatch_queue_release();

// Execution continues here on calling thread IMMEDIATELY, while operations
// run on a background thread.

Upvotes: 2

Jeffery Thomas
Jeffery Thomas

Reputation: 42598

I had a much more complicated answer using barriers, but then I realized.

dispatch_queue_t myQueue = dispatch_queue_create("samplequeue", NULL);
void (^myBlock)() = ^{
    for (int i = 0; i < 10; ++i) {
        //a few seconds long operation
    }
};

dispatch_async(myQueue, myBlock);

Upvotes: 2

Related Questions