I'm trying to build an array of dictionaries in a background thread while keeping access to the current array until the background operation is done. Here's a simplified version of my code:
#property (nonatomic, strong) NSMutableArray *data;
#property (nonatomic, strong) NSMutableArray *dataInProgress;
- (void)loadData {
self.dataInProgress = [NSMutableArray array];
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
[self loadDataWorker];
});
}
- (void)loadDataWorker {
for (int i=0; i<10000; i++) {
[self addDataItem];
}
dispatch_async(dispatch_get_main_queue(), ^{
[self loadDataFinish]; // the crash occurs before we get to this point
});
}
- (void)addDataItem {
// first check some previously added data
int currentCount = (int)[self.dataInProgress count];
if (currentCount > 0) {
NSDictionary *lastItem = [self.dataInProgress objectAtIndex:(currentCount - 1)];
NSDictionary *checkValue = [lastItem objectForKey:#"key3"]; // this line crashes with EXC_BAD_ACCESS
}
// then add another item
NSDictionary *dictionaryValue = [NSDictionary dictionaryWithObjectsAndKeys:#"bar", #"foo", nil];
NSDictionary *item = [NSDictionary dictionaryWithObjectsAndKeys:#"value1", #"key1", #"value2", #"key2", dictionaryValue, #"key3", nil];
// as described in UPDATE, I think this is the problem
dispatch_async(dispatch_get_main_queue(), ^{
[dictionaryValue setObject:[self makeCustomView] forKey:#"customView"];
});
[self.dataInProgress addObject:item];
}
- (UIView *)makeCustomView {
return [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
}
- (void)loadDataFinish {
self.data = [NSMutableArray arrayWithArray:self.dataInProgress];
}
This works fine in most cases, but when the dataset is large, I start to get crashes on the line indicated above. The likelihood of a crash is greater with more data or a device with less memory. On an iPhone 6 with 10,000 items, it happens about one in five times. So it looks like when memory gets tight, the dictionaries inside the data array are destroyed before I access them.
If I do everything in the main thread there are no crashes. I originally had this problem with non-ARC code, then I converted my project to ARC and the same problem remains.
Is there a way to ensure the objects added earlier in the build process are retained until I'm done? Or is there a better way to do what I'm doing?
Here's a stack trace:
thread #17: tid = 0x9c586, 0x00000001802d1b90 libobjc.A.dylib`objc_msgSend + 16, queue = 'com.apple.root.background-qos', stop reason = EXC_BAD_ACCESS (code=1, address=0x10)
frame #0: 0x00000001802d1b90 libobjc.A.dylib`objc_msgSend + 16
frame #1: 0x0000000180b42384 CoreFoundation`-[__NSDictionaryM objectForKey:] + 148
frame #2: 0x00000001002edd58 MyApp`-[Table addDataItem](self=0x000000014fd44600, _cmd="addDataItem", id=0x00000001527650d0, section=3, cellData=0x0000000152765050) + 1232 at Table.m:392
frame #4: 0x00000001002eca28 MyApp`__25-[Table loadData]_block_invoke(.block_descriptor=0x000000015229efd0) + 52 at Table.m:265
frame #5: 0x0000000100705a7c libdispatch.dylib`_dispatch_call_block_and_release + 24
frame #6: 0x0000000100705a3c libdispatch.dylib`_dispatch_client_callout + 16
frame #7: 0x0000000100714c9c libdispatch.dylib`_dispatch_root_queue_drain + 2344
frame #8: 0x0000000100714364 libdispatch.dylib`_dispatch_worker_thread3 + 132
frame #9: 0x00000001808bd470 libsystem_pthread.dylib`_pthread_wqthread + 1092
frame #10: 0x00000001808bd020 libsystem_pthread.dylib`start_wqthread + 4
UPDATE
I traced through my full code with the answers below in mind, particularly those about locking while multithreading, and realized that part of the data I'm adding to my data array is a UIView that I'm creating during the build process. Since it's bad to build views in a background thread, and I did see problems when doing that, I'm jumping back to the main thread for makeCustomView. See the lines of code I added above with "UPDATE" in the comment. This must be the problem now; when I skip adding the custom view, I have no more crashes.
I could rework the build workflow so that all the data except the custom views are added on the background thread, then I could make a second pass and add the custom views on the main thread. But is there a way to manage the threads in this workflow? I tried locking with NSLock before and after calling makeCustomView, but that made no difference. I also found an SO answer saying NSLock is basically outdated, so I didn't go further with that.
If I understood you correctly, concurrent accesses to the dataInProgress array causes the problem, because the array is filled in a background thread and used in the main thread. But NSMutableArray is not thread safe. This fits to my intention that the array itself is corrupted.
You could solve that with NSLock to serialize the accesses to the array, but this is akin of outdated and does not fit to the rest of your code, which uses the more modern (and better) GCD.
A. Situation
What you have:
a builder control flow, which has to run in background
a creation of views control flow, which has to run in the main queue (thread). (I'm not completely sure, whether the pure creation of a view has to be done in main thread, but i would do.)
both control flows accesses the same resource (dataInProgress)
B. GCD
With classical thread/lock approach you start async control flows and serialize them with locks, when they access concurrently a shared resource.
With GCD you start control flows concurrently to each other, but serialized for a given shared resource. (Basically, there are more features, more complexity, but this is, what we need here.)
C. Serializing
It is correct to start the builder in a background queue ("thread") to run it without blocking the main thread. Done.
It is correct to switch back to main thread, if you want to do something with UI elements, esp. creating a view.
Since both control flows accesses the same resource, you have to serialize the accesses. You do this by creating a (serial) queue for that resource:
…
#property dispatch_queue_t dataInProgressAccessQ;
…
// In init or whatever
self. dataInProgressAccessQ = dispatch_queue_create("com.yourcompany.dataInProgressQ", NULL);
After doing that, you put every access to the dataInProgress array in that queue. There is a simple example for that:
// [self.dataInProgress addObject:item];
dispatch_async( self.dataInProgressAccessQ,
^{
[self.dataInProgress addObject:item];
});
In this case it is very easy, because you have to switch the queue at the and of the code. If it is in the middle, you have two options:
a) Use the queue similar to a lock. Let's have an example:
// NSInteger currentCount = [self.dataInProgress count]; // Why int?
NSInteger currentCount;
dispatch_sync( self.dataInProgressAccessQ,
^{
currentCount = [self.dataInProgress count];
});
// More code using currentCount
Using dispatch_sync() will let the code execution wait, until accesses from other control flows are finished. (It is like a lock.)
Edit: As with locks, the access is guaranteed to be serialized. But there might be the problem, that another thread removes objects from the array. Let's have a look to such a situation:
// NSInteger currentCount = [self.dataInProgress count]; // Why int?
NSInteger currentCount;
dispatch_sync( self.dataInProgressAccessQ,
^{
currentCount = [self.dataInProgress count];
});
// More code using currentCount
// Imagine that the execution is stopped here
// Imagine that -makeCustomView removes the last item in meanwhile
// Imagine that the execution continues here
// -> currentCount is not valid anymore.
id lastItem = [self.dataInProgress objectAtIndex:currentCount]; // crash: index out of bounds
To prevent this, you really have to isolate your concurrent code. This highly depends on your code. However, in my example:
id lastItem;
dispatch_sync( self.dataInProgressAccessQ,
^{
NSInteger currentCount;
currentCount = [self.dataInProgress count];
lastItem = [self.dataInProgress objectAtIndex:currentCount]; // don't crash: bounds are not changed
});
// Continue with lastItem
As you can imagine, when getting the last item, if can be removed from the array in the very next moment after you read it. Maybe this causes problems of inconsistency in your code. It really depends on your code.
End of edit
b) Maybe you get performance problems, because is works like a lock (synch). If so, you have to analyze your code and extract parts, that can run concurrently again. The pattern looks like this:
// NSInteger currentCount = [self.dataInProgress count]; // Why int?
dispatch_async( self.dataInProgressAccessQ, // <-- becomes asynch
^{
NSInteger currentCount = [self.dataInProgress count];
// go back to the background queue to leave the access queue fastly
dispatch_async( dispatch_get_global_queue(),
^{
// use current count here.
});
});
dispatch_async( self.dataInProgressAccessQ,
^{
// Another task, that can run concurrently to the above
});
What you can do there, is a matter of your concrete code. Maybe it is a help for you, to have your own private builder queue instead of using the global queue.
But this is the basic approach: Move a task into a queue and do not wait, until it is finished, but add code at the end, that completes the task in another control flow.
Instead of
Code
--lock--
var = Access code
--unlock--
More Code using var
it is
Code
asynch {
var Access Code
asynch {
More code using var
}
}
Of course, you have to do the same inside -makeCustomView.
I agree with Phillip Mills. This looks like a thread safety issue around your self.dataInProgress object.
From Apple docs https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html :
Mutable objects are generally not thread-safe. To use mutable objects in a threaded application, the application must synchronize access to them using locks. (For more information, see Atomic Operations). In general, the collection classes (for example, NSMutableArray, NSMutableDictionary) are not thread-safe when mutations are concerned. That is, if one or more threads are changing the same array, problems can occur. You must lock around spots where reads and writes occur to assure thread safety.
If addDataItem is being called from various background threads, you need to lock around reading and writing to self.dataInProgress.
I don't think you need deep copies. If the dictionaries aren't mutable, all you need is for them to not be released...and a copy of the array they're in will do that for you.
What I believe you need is synchronization around any access to self.data. I suggest creating a NSLock object for your class and wrapping each of the following two lines with lock/unlock method calls:
self.data = [NSMutableArray arrayWithArray:self.dataInProgress];
//...
NSDictionary *item = [self.data objectAtIndex:index];
Also, why does self.data need to be mutable? If it doesn't, self.data = [self.dataInProgress copy]; is simpler...and quite possibly more efficient for memory and performance.
The one thing that's worrying me is, what about the caller of getData. It may not know that the self.data array has changed. If the array becomes shorter, you're headed for an "index out of bounds" crash.
It would be good to only call getData when you know the array is going to be stable. (In other words, synchronize the data gets at a higher level.)
I would attempt at passing in a weak reference to self. I bet if you might have a strong retain cycle happening there somewhere. If I remember correctly, __weak doesn't up the retain count, and __block allows you to change the variable
- (void)loadData {
self.dataInProgress = [NSMutableArray array];
__weak __block SelfClassName *weakSelf = self;
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
[weakSelf loadDataWorker];
});
}
- (void)loadDataWorker {
for (int i=0; i<10000; i++) {
[self addDataItem];
}
__weak __block SelfClassName *weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf loadDataFinish];
});
}
Related
My goal is to initialize a large amount of data on a different (non-main) thread, and then refresh a UIProgressView on the main thread corresponding to the large data load's progress. To do this, I use performSelectorInBackground to load the data, and update the progress throughout the way using performSelectorOnMainThread.
I am currently getting a EXE_BAD_ACCESS error from one of the dictionaries that I am initializing, specifically the line where I am setting self.someDictionary = #{...}. self.someDictionary takes on strong and nonatomic properties, and is initialized on the non-main thread.
As a total newbie to multithreading, I am beginning to see that I shouldn't be setting a strong and nonatomic property (however, changing it to atomic still caused the crash). What else am I doing incorrectly to cause the EXE_BAD_ACCESS error, and how do I set a large amount of data to an NSDictionary on a non-main thread and still be able to update the progress on the main thread?
Thanks!
Edit 1:
Code:
//In viewWillAppear, from the main thread
[self performSelectorInBackground:#selector(populateDictionaries) withObject:nil];
//In populateDictionaries method
Dictionary *someDictionary = [[Dictionary alloc] init];
//the methods inside the Dictionary class
- (id) init{
self = [super init];
if (self){
[self makeDictionaries];
}
return self;
}
- (void)makeDictionaries{
self.insiderDictionary = #{ ...} //this line is causing the crash
}
If you want to perform the process in background try Dispatch queue
ref: https://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html
Thank you.
The code below will crash inside of NSKVOUnionSetAndNotify calling CFDictionaryGetValue with what appears to be a bogus dictionary.
It seems to be a race between the swizzled addFoos / NSKVOUnionSetAndNotify code and the act of adding and removing KVO observers.
#import <Foundation/Foundation.h>
#interface TestObject : NSObject
#property (readonly) NSSet *foos;
#end
#implementation TestObject {
NSMutableSet *_internalFoos;
dispatch_queue_t queue;
BOOL observed;
}
- (id)init {
self = [super init];
_internalFoos = [NSMutableSet set];
queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
return self;
}
- (void)start {
// Start a bunch of work hitting the unordered collection mutator
for (int i = 0; i < 10; i++) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (YES) {
#autoreleasepool {
[self addFoos:[NSSet setWithObject:#(rand() % 100)]];
}
}
});
}
// Start work that will constantly observe and unobserve the unordered collection
[self observe];
}
- (void)observe {
dispatch_async(dispatch_get_main_queue(), ^{
observed = YES;
[self addObserver:self forKeyPath:#"foos" options:0 context:NULL];
});
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
dispatch_async(dispatch_get_main_queue(), ^{
if (observed) {
observed = NO;
[self removeObserver:self forKeyPath:#"foos"];
[self observe];
}
});
}
// Public unordered collection property
- (NSSet *)foos {
__block NSSet *result;
dispatch_sync(queue, ^{
result = [_internalFoos copy];
});
return result;
}
// KVO compliant mutators for unordered collection
- (void)addFoos:(NSSet *)objects {
dispatch_barrier_sync(queue, ^{
[_internalFoos unionSet:objects];
});
}
- (void)removeFoos:(NSSet *)objects {
dispatch_barrier_sync(queue, ^{
[_internalFoos minusSet:objects];
});
}
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
TestObject *t = [[TestObject alloc] init];
[t start];
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10000, false);
}
return 0;
}
The actual crash you get is an EXC_BAD_ACCESS when the key value observing dictionary is accesed. The stack trace is as follows:
* thread #2: tid = 0x1ade39, 0x00007fff92f8e097 libobjc.A.dylib`objc_msgSend + 23, queue = 'com.apple.root.default-priority', stop reason = EXC_BAD_ACCESS (code=1, address=0x18)
frame #0: 0x00007fff92f8e097 libobjc.A.dylib`objc_msgSend + 23
frame #1: 0x00007fff8ffe2b11 CoreFoundation`CFDictionaryGetValue + 145
frame #2: 0x00007fff8dc55750 Foundation`NSKVOUnionSetAndNotify + 147
* frame #3: 0x0000000100000f85 TestApp`__19-[TestObject start]_block_invoke(.block_descriptor=<unavailable>) + 165 at main.m:34
frame #4: 0x000000010001832d libdispatch.dylib`_dispatch_call_block_and_release + 12
frame #5: 0x0000000100014925 libdispatch.dylib`_dispatch_client_callout + 8
frame #6: 0x0000000100016c3d libdispatch.dylib`_dispatch_root_queue_drain + 601
frame #7: 0x00000001000182e6 libdispatch.dylib`_dispatch_worker_thread2 + 52
frame #8: 0x00007fff9291eef8 libsystem_pthread.dylib`_pthread_wqthread + 314
frame #9: 0x00007fff92921fb9 libsystem_pthread.dylib`start_wqthread + 13
If you set a symbolic breakpoint with the symbol NSKVOUnionSetAndNotify the debugger will stop where this method is being invoked.
The crash you are seeing is because automatic key-value notifications are being sent from one thread when you invoke your [addFoos:] method, but then the change dictionary is being accessed from another thread. This is stimulated by your use of the global dispatch queue when calling this method, as that will execute the block in many different threads.
There are mulitple ways to fix this crash, and I will try to walk you through this to give you a more thourough understanding of what is going on.
In the simplest case, you can fix the crash by using the key-value coding mutable proxy object for this key:
NSMutableSet *someSet = [self mutableSetValueForKey:#"foos"];
[someSet unionSet:[NSSet setWithObject:#(rand() % 100)]];
That will stop this particular crash. What's happening here? When mutableSetValueForKey: is called, the result is a proxy object that forwards messages to your KVC-compliant accessor methods for the key "foos". The author's object does not actually fully conform to the required pattern for a KVC compliant property of this type. If other KVC accessor methods are messaged for this key, they may go through non-thread safe accessors provided by Foundation, which can result in this crash all over again. We'll get to how to fix that in a moment.
The crash is being triggered by automatic KVO change notifications crossing threads. Automatic KVO notifications work by swizzling classes and methods at runtime. You can read a more in-depth explanation here and here. KVC accessor methods are essentially wrapped at runtime with KVO-supplied methods. This is in fact where the crash in the original application is happening. This is the KVO inserted code disassembled from Foundation:
int _NSKVOUnionSetAndNotify(int arg0, int arg1, int arg2) {
r4 = object_getIndexedIvars(object_getClass(arg0));
OSSpinLockLock(_NSKVONotifyingInfoPropertyKeysSpinLock);
r6 = CFDictionaryGetValue(*(r4 + 0xc), arg1);
OSSpinLockUnlock(_NSKVONotifyingInfoPropertyKeysSpinLock);
var_0 = arg2;
[arg0 willChangeValueForKey:r6 withSetMutation:0x1 usingObjects:STK-1];
r0 = *r4;
r0 = class_getInstanceMethod(r0, arg1);
method_invoke(arg0, r0);
var_0 = arg2;
r0 = [arg0 didChangeValueForKey:r6 withSetMutation:0x1 usingObjects:STK-1];
Pop();
Pop();
Pop();
return r0;
}
As you can see, this is wrapping a KVC compliant accessor method with willChangeValueForKey:withSetMutation:usingObjects: and didChangeValueForKey: withSetMutation:usingObjects:. These are the methods that send out KVO notifications. KVO will insert this wrapper at runtime if the object has opted into automatic key value observer notification. In between these calls you can see class_getInstanceMethod. This is getting a reference to the KVC compliant accessor being wrapped, and then calling it. In the case of the original code, this is being triggered from inside NSSet's unionSet:, which was happening across threads and causing the crash when it accessed the change dictionary.
Automatic notifications are sent by the thread where the change occured, and are intended to be received on the same thread. This being Teh IntarWebs, there is a lot of bad or misleading information out there about KVO. Not all objects and not all properties emit automatic KVO notifications, and in your classes you can control which do and don't. From the Key Value Observing Programming Guide: Automatic Change Notification :
NSObject provides a basic implementation of automatic key-value change notification. Automatic key-value change notification informs observers of changes made using key-value compliant accessors, as well as the key-value coding methods. Automatic notification is also supported by the collection proxy objects returned by, for example, mutableArrayValueForKey:
This may lead one to believe that all descendants of NSObject emit automatic notifications by default. This is not the case - may framework classes do not, or implement special behavior. Core Data is an example. From the Core Data Programming Guide :
NSManagedObject disables automatic key-value observing (KVO) change notifications for modeled properties, and the primitive accessor methods do not invoke the access and change notification methods. For unmodeled properties, on OS X v10.4 Core Data also disables automatic KVO; on OS X v10.5 and later, Core Data adopts to NSObject’s behavior.
As a developer, you can ensure that automatic key value observer notifications are on or off for a particular property by implementing a method with the correct naming convention, +automaticallyNotifiesObserversOf<Key>. When this method returns NO, automatic key value notifications are not emitted for this property. When automatic change notifications are disabled KVO also does not have to swizzle the accessor method at runtime, as this is done primarily to support automatic change notifications. For example:
+ (BOOL) automaticallyNotifiesObserversOfFoos {
return NO;
}
In a comment the author stated that the reason he was using dispatch_barrier_sync for his accessor methods is that if he did not, KVO notifications would arrive before changes occured. With automatic notifications disabled for a property, you still have the option of sending these notifications manually. This is done by using the methods willChangeValueForKey: and didChangeValueForKey:. Not only does this give you control of when these notifications are sent (if at all), but on what thread. Automatic change notifications, as you recall, are sent from and received on the thread where the change occured.
For example, if you wanted change notifications to happen only on the main queue, you could do so using recursive decomposition:
- (void)addFoos:(NSSet *)objects {
dispatch_async(dispatch_get_main_queue(), ^{
[self willChangeValueForKey:#"foos"];
dispatch_barrier_sync(queue, ^{
[_internalFoos unionSet:objects];
dispatch_async(dispatch_get_main_queue(), ^{
[self didChangeValueForKey:#"foos"];
});
});
});
}
The original class in the author's question was forcing KVO observation to start and stop on the main queue, which seems have been an attempt to emit notifications on the main queue. The above example demonstrates a solution that not only addresses that concern, but ensures that the KVO notifications are correctly sent before and after the data changes.
In the example above I modified the author's original method as an illustrative example - this class is still not correctly KVC compliant for the key "foos". To be Key-Value Observing compliant, an object must first be Key-Value Coding compliant. To address this, first create the correct Key-value coding compliant accessors for an unordered mutable collection :
Immutable:
countOfFoos
enumeratorOfFoos
memberOfFoos:
Mutable:
addFoosObject:
removeFoosObject:
These are just the minimum, there are additional methods that can be implemented for performance or data integrity reasons.
The original application was using a concurrent queue and dispatch_barrier_sync. This was dangerous, for many reasons. The approach recommended by the Concurrency Programming Guide is to instead use a serial queue. This ensures that only one thing can be touching the protected resource at a time, and it is from a consistent context. For example, two of the above methods would look like this:
- (NSUInteger)countOfFoos {
__block NSUInteger result = 0;
dispatch_sync([self serialQueue], ^{
result = [[self internalFoos] count];
});
return result;
}
- (void) addFoosObject:(id)object {
id addedObject = [object copy];
dispatch_async([self serialQueue], ^{
[[self internalFoos] addObject:addedObject];
});
}
Note that in this example and the next, I am not including manual KVO change notifications for brevity and clarity. If you want manual change notifications to be sent, that code should be added to these methods like what you saw in the previous example.
Unlike using dispatch_barrier_sync with a concurrent queue, this will not allow a deadlock.
The WWDC 2011 Session 210 Mastering Grand Central Dispatch showed the correct use of the dispatch barrier API for implementing a reader/writer lock for a collection using a concurrent queue. This would be implemented like this:
- (id) memberOfFoos:(id)object {
__block id result = nil;
dispatch_sync([self concurrentQueue], ^{
result = [[self internalFoos] member:object];
});
return result;
}
- (void) addFoosObject:(id)object {
id addedObject = [object copy];
dispatch_barrier_async([self concurrentQueue], ^{
[[self internalFoos] addObject:addedObject];
});
}
Note that the dispatch barrier is accessed asynchronously for the write operation, while the read operation uses dispatch_sync. The original application used dispatch_barrier_sync for both reads and writes, which the author stated was done to control when automatic change notifications were sent. Using manual change notifications would address that concern (again, not shown in this example for brevity and clarity).
There are still issues with the KVO implementation in the original. It does not use the context pointer to determine ownership of an observation. This is a recommended practice, and can use a pointer to self as a value. The value should have the same address as the objected used to add and remove the observer:
[self addObserver:self forKeyPath:#"foos" options:NSKeyValueObservingOptionNew context:(void *)self];
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (context == (__bridge void *)self){
// check the key path, etc.
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
From the NSKeyValueObserving.h header:
You should use -removeObserver:forKeyPath:context: instead of -removeObserver:forKeyPath: whenever possible because it allows you to more precisely specify your intent. When the same observer is registered for the same key path multiple times, but with different context pointers each time, -removeObserver:forKeyPath: has to guess at the context pointer when deciding what exactly to remove, and it can guess wrong.
If you are interested in a further understanding of applying and implementing Key Value Observing, I suggest the video KVO Considered Awesome
In summary:
• Implement the required key-value coding accessor pattern (unordered mutable collection)
• Make those accessors thread safe (using a serial queue with dispatch_sync/dispatch_async, or a concurrent queue with dispatch_sync/dispatch_barrier_async)
• Decide wether you want automatic KVO notifications or not, implement automaticallyNotifiesObserversOfFoos accordingly
• Add manual change notifications appropriately to accessor methods
• Make sure that code which accesses your property does so through the correct KVC accessor methods (i.e. mutableSetValueForKey:)
I am a newbie. I am using Grand Central Dispatch to populate an array (student_temp) on another thread. That part is working fine. The problem is I cannot pass the array to a class property (student_Array) where it is used throughout the class. I can't get the array back on the main thread.
it works fine until I get back tot he main thread and I can't pass student_temp into student_Array (the property) either inside or outside of GCD.
What am I doing wrong, or is there a better to populate the array property using GCD?
Thank you for your help. And please try to explain in non-technical language if possible I am new at this.
- (void)viewDidLoad
{
[super viewDidLoad];
R2LFetcher *studentFetch = [[R2LFetcher alloc] init];
__block NSMutableArray *student_temp = [[NSMutableArray alloc] init];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
//long-running code goes here…
student_temp = [studentFetch fetchToStudentArray];
dispatch_async(dispatch_get_main_queue(), ^{
// code the updates the main thread (UI) here...
student_Array = student_temp;
});
});
student_Array = student_temp;
A couple of reactions:
In the last line of your code, you're setting student_Array to student_temp. Clearly that line makes no sense because you're populating student_temp asynchronously. And you're opening yourself up to synchronization issues if you're trying to simultaneously access the save variable in two queues. Don't bother to assign student_Array to student_temp at the end of viewDidLoad, but rather just do it inside the nested dispatch_async calls.
Inside the block, you're populating and setting student_temp. It probably makes more sense to make that variable scoped within that block, avoiding temptation to access it from outside that block as well as simplifying your code because the __block qualifier is no longer needed.
This block is running asynchronously, so when you update student_Array in the main queue, you might want to update your UI at the same time (e.g. reload the tableview or whatever). Perhaps you're doing that already and just removed it for the sake of brevity, but I just wanted to make sure.
Thus:
- (void)viewDidLoad
{
[super viewDidLoad];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);
dispatch_async(queue, ^{
R2LFetcher *studentFetch = [[R2LFetcher alloc] init];
// long-running code goes here, for example ...
NSMutableArray *student_temp = [studentFetch fetchToStudentArray];
dispatch_async(dispatch_get_main_queue(), ^{
student_Array = student_temp;
// code the updates the main thread (UI) here, for example...
[self.tableView reloadData];
});
});
}
You should be able to add objects to student_Array directly from your block. Unlike stack variables, properties and ivars don't get copied when used inside a block. Instead, self gets retained in the block, and the property is referenced through it.
Of course, you need to be aware of concurrency issues, e.g. if you need to access the data from the main thread as well. For that, you probably still want to have this at the end of your async GCD block:
// done populating the data
dispatch_async(dispatch_get_main_queue(), ^{
// update the UI
}
I want to synchronize some data with a web service. For each item I have to make a asynchronous call.
I want to have a completion block witch is called, when each item was synchronized. For each item I am able to perform a completion block. Now, I don't know a good way how to do it.
This is the interface:
-(void) synchronizeItemsOnComplete:(CompleteBlock) block {
NSArray* items = // get items
for (int i = 0, n = [items count]; i < n; i++) {
[self synchronizeItem:[items objectAtIndex:i] onComplete:^{
// What do do here?
}];
}
// And/or here?
}
-(void) synchronizeItemOnComplete:(CompleteBlock) block {
// do something
block();
}
How can I wait for the synchronization and then perform the block?
I tried something like this:
NSArray* items = // get items
__block int countOfItemsUntilDone = [items count];
for (int i = 0, n = countOfItemsUntilDone; i < n; i++) {
[self synchronizeItem:[items objectAtIndex:i] onComplete:^{
countOfItemsUntilDone--;
}];
}
dispatch_queue_t queue = dispatch_queue_create("wait for syncing", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
while (countOfItemsUntilDone > 0) {
usleep(1000); // wait a little bit
}
block();
});
dispatch_release(queue);
But I think this is a quite bad way. Any ideas?
Instead of spinning in a loop waiting for the counter to equal zero, check the counter value each time you decrement it, then fire an event when it reaches zero.
-(void) synchronizeItemsOnComplete:(CompleteBlock) block {
NSArray* items = // get items
__block NSUInteger remaining = [items count];
for (ItemClass* item in items) {
[self synchronizeItemImage:item onComplete:^{
--remaining;
if (remaining == 0) {
block();
}
}];
}
}
To explain why it feels wrong, there are two things you're doing here that you should do either never or rarely:
Using background queues. This is difficult and bug-prone. Don't do it without reading up a lot about writing concurrent code. You also only really need to do this if an operation blocks for a substantial amount of time (eg., to read a file from disk, or perform an intensive calculation). Don't assume you need to do it unless you have a good reason (eg., a measurable performance problem).
Spinning in a loop, checking a variable for changes and calling sleep. You should never do this.
Also, if you're looping over the elements in an array, the for ... in syntax is much nicer (and potentially more efficient) calling objectAtIndex: on each index.
Never check or decrement shared memory in different threads like this, it can cause races. Use a dispatch group to do what you're doing.
dispatch_queue_t myBGQueue;
dispatch_group_t itemsGroup = dispatch_group_create();
for (ItemClass *item in items) {
dispatch_group_async(itemsGroup, myBGQueue, ^{
[self synchronizeItemImage:item];
});
}
/* execution will sleep here until all the blocks added in the `for` complete */
dispatch_group_wait(itemsGroup, DISPATCH_TIME_FOREVER);
dispatch_release(itemsGroup);
You can use these to use synchronously.
GCD and this
performSelector:waitUntilDone:YES
If you are an advanced user of drawRect, you will know that of course drawRect will not actually run until "all processing is finished."
setNeedsDisplay flags a view as invalidated and the OS, and basically waits until all processing is done. This can be infuriating in the common situation where you want to have:
a view controller 1
starts some function 2
which incrementally 3
creates a more and more complicated artwork and 4
at each step, you setNeedsDisplay (wrong!) 5
until all the work is done 6
Of course, when you do the above 1-6, all that happens is that drawRect is run once only after step 6.
Your goal is for the view to be refreshed at point 5. What to do?
If I understand your question correctly, there is a simple solution to this. During your long-running routine you need to tell the current runloop to process for a single iteration (or more, of the runloop) at certain points in your own processing. e.g, when you want to update the display. Any views with dirty update regions will have their drawRect: methods called when you run the runloop.
To tell the current runloop to process for one iteration (and then return to you...):
[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate: [NSDate date]];
Here's an example of an (inefficient) long running routine with a corresponding drawRect - each in the context of a custom UIView:
- (void) longRunningRoutine:(id)sender
{
srand( time( NULL ) );
CGFloat x = 0;
CGFloat y = 0;
[_path moveToPoint: CGPointMake(0, 0)];
for ( int j = 0 ; j < 1000 ; j++ )
{
x = 0;
y = (CGFloat)(rand() % (int)self.bounds.size.height);
[_path addLineToPoint: CGPointMake( x, y)];
y = 0;
x = (CGFloat)(rand() % (int)self.bounds.size.width);
[_path addLineToPoint: CGPointMake( x, y)];
x = self.bounds.size.width;
y = (CGFloat)(rand() % (int)self.bounds.size.height);
[_path addLineToPoint: CGPointMake( x, y)];
y = self.bounds.size.height;
x = (CGFloat)(rand() % (int)self.bounds.size.width);
[_path addLineToPoint: CGPointMake( x, y)];
[self setNeedsDisplay];
[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate: [NSDate date]];
}
[_path removeAllPoints];
}
- (void) drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor( ctx, [UIColor blueColor].CGColor );
CGContextFillRect( ctx, rect);
CGContextSetStrokeColorWithColor( ctx, [UIColor whiteColor].CGColor );
[_path stroke];
}
And here is a fully working sample demonstrating this technique.
With some tweaking you can probably adjust this to make the rest of the UI (i.e. user-input) responsive as well.
Update (caveat for using this technique)
I just want to say that I agree with much of the feedback from others here saying this solution (calling runMode: to force a call to drawRect:) isn't necessarily a great idea. I've answered this question with what I feel is a factual "here's how" answer to the stated question, and I am not intending to promote this as "correct" architecture. Also, I'm not saying there might not be other (better?) ways to achieve the same effect - certainly there may be other approaches that I wasn't aware of.
Update (response to the Joe's sample code and performance question)
The performance slowdown you're seeing is the overhead of running the runloop on each iteration of your drawing code, which includes rendering the layer to the screen as well as all of the other processing the runloop does such as input gathering and processing.
One option might be to invoke the runloop less frequently.
Another option might be to optimize your drawing code. As it stands (and I don't know if this is your actual app, or just your sample...) there are a handful of things you could do to make it faster. The first thing I would do is move all the UIGraphicsGet/Save/Restore code outside the loop.
From an architectural standpoint however, I would highly recommend considering some of the other approaches mentioned here. I see no reason why you can't structure your drawing to happen on a background thread (algorithm unchanged), and use a timer or other mechanism to signal the main thread to update it's UI on some frequency until the drawing is complete. I think most of the folks who've participated in the discussion would agree that this would be the "correct" approach.
Updates to the user interface happen at the end of the current pass through the run loop. These updates are performed on the main thread, so anything that runs for a long time in the main thread (lengthy calculations, etc.) will prevent the interface updates from being started. Additionally, anything that runs for a while on the main thread will also cause your touch handling to be unresponsive.
This means that there is no way to "force" a UI refresh to occur from some other point in a process running on the main thread. The previous statement is not entirely correct, as Tom's answer shows. You can allow the run loop to come to completion in the middle of operations performed on the main thread. However, this still may reduce the responsiveness of your application.
In general, it is recommended that you move anything that takes a while to perform to a background thread so that the user interface can remain responsive. However, any updates you wish to perform to the UI need to be done back on the main thread.
Perhaps the easiest way to do this under Snow Leopard and iOS 4.0+ is to use blocks, like in the following rudimentary sample:
dispatch_queue_t main_queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
// Do some work
dispatch_async(main_queue, ^{
// Update the UI
});
});
The Do some work part of the above could be a lengthy calculation, or an operation that loops over multiple values. In this example, the UI is only updated at the end of the operation, but if you wanted continuous progress tracking in your UI, you could place the dispatch to the main queue where ever you needed a UI update to be performed.
For older OS versions, you can break off a background thread manually or through an NSOperation. For manual background threading, you can use
[NSThread detachNewThreadSelector:#selector(doWork) toTarget:self withObject:nil];
or
[self performSelectorInBackground:#selector(doWork) withObject:nil];
and then to update the UI you can use
[self performSelectorOnMainThread:#selector(updateProgress) withObject:nil waitUntilDone:NO];
Note that I've found the NO argument in the previous method to be needed to get constant UI updates while dealing with a continuous progress bar.
This sample application I created for my class illustrates how to use both NSOperations and queues for performing background work and then updating the UI when done. Also, my Molecules application uses background threads for processing new structures, with a status bar that is updated as this progresses. You can download the source code to see how I achieved this.
You can do this repeatedly in a loop and it'll work fine, no threads, no messing with the runloop, etc.
[CATransaction begin];
// modify view or views
[view setNeedsDisplay];
[CATransaction commit];
If there is an implicit transaction already in place prior to the loop you need to commit that with [CATransaction commit] before this will work.
In order to get drawRect called the soonest (which is not necessarily immediately, as the OS may still wait until, for instance, the next hardware display refresh, etc.), an app should idle it's UI run loop as soon as possible, by exiting any and all methods in the UI thread, and for a non-zero amount of time.
You can either do this in the main thread by chopping any processing that takes more than an animation frame time into shorter chunks and scheduling continuing work only after a short delay (so drawRect might run in the gaps), or by doing the processing in a background thread, with a periodic call to performSelectorOnMainThread to do a setNeedsDisplay at some reasonable animation frame rate.
A non-OpenGL method to update the display near immediately (which means at the very next hardware display refresh or three) is by swapping visible CALayer contents with an image or CGBitmap that you have drawn into. An app can do Quartz drawing into a Core Graphics bitmap at pretty much at any time.
New added answer:
Please see Brad Larson's comments below and Christopher Lloyd's comment on another answer here as the hint leading towards this solution.
[ CATransaction flush ];
will cause drawRect to be called on views on which a setNeedsDisplay request has been done, even if the flush is done from inside a method that is blocking the UI run loop.
Note that, when blocking the UI thread, a Core Animation flush is required to update changing CALayer contents as well. So, for animating graphic content to show progress, these may both end up being forms of the same thing.
New added note to new added answer above:
Do not flush faster than your drawRect or animation drawing can complete, as this might queue up flushes, causing weird animation effects.
Without questioning the wisdom of this (which you ought to do), you can do:
[myView setNeedsDisplay];
[[myView layer] displayIfNeeded];
-setNeedsDisplay will mark the view as needing to be redrawn.
-displayIfNeeded will force the view's backing layer to redraw, but only if it has been marked as needing to be displayed.
I will emphasize, however, that your question is indicative of an architecture that could use some re-working. In all but exceptionally rare cases, you should never need to or want to force a view to redraw immediately. UIKit with not built with that use-case in mind, and if it works, consider yourself lucky.
Have you tried doing the heavy processing on a secondary thread and calling back to the main thread to schedule view updates? NSOperationQueue makes this sort of thing pretty easy.
Sample code that takes an array of NSURLs as input and asynchronously downloads them all, notifying the main thread as each of them is finished and saved.
- (void)fetchImageWithURLs:(NSArray *)urlArray {
[self.retriveAvatarQueue cancelAllOperations];
self.retriveAvatarQueue = nil;
NSOperationQueue *opQueue = [[NSOperationQueue alloc] init];
for (NSUInteger i=0; i<[urlArray count]; i++) {
NSURL *url = [urlArray objectAtIndex:i];
NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:#selector(cacheImageWithIndex:andURL:)]];
[inv setTarget:self];
[inv setSelector:#selector(cacheImageWithIndex:andURL:)];
[inv setArgument:&i atIndex:2];
[inv setArgument:&url atIndex:3];
NSInvocationOperation *invOp = [[NSInvocationOperation alloc] initWithInvocation:inv];
[opQueue addOperation:invOp];
[invOp release];
}
self.retriveAvatarQueue = opQueue;
[opQueue release];
}
- (void)cacheImageWithIndex:(NSUInteger)index andURL:(NSURL *)url {
NSData *imageData = [NSData dataWithContentsOfURL:url];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *filePath = PATH_FOR_IMG_AT_INDEX(index);
NSError *error = nil;
// Save the file
if (![fileManager createFileAtPath:filePath contents:imageData attributes:nil]) {
DLog(#"Error saving file at %#", filePath);
}
// Notifiy the main thread that our file is saved.
[self performSelectorOnMainThread:#selector(imageLoadedAtPath:) withObject:filePath waitUntilDone:NO];
}
I think, the most complete answer comes from the Jeffrey Sambell's blog post 'Asynchronous Operations in iOS with Grand Central Dispatch' and it worked for me!
It's basically the same solution as proposed by Brad above but fully explained in terms of OSX/IOS concurrency model.
The dispatch_get_current_queue function will return the current queue
from which the block is dispatched and the dispatch_get_main_queue
function will return the main queue where your UI is running.
The dispatch_get_main_queue function is very useful for updating the
iOS app’s UI as UIKit methods are not thread safe (with a few
exceptions) so any calls you make to update UI elements must always be
done from the main queue.
A typical GCD call would look something like this:
// Doing something on the main thread
dispatch_queue_t myQueue = dispatch_queue_create("My Queue",NULL);
dispatch_async(myQueue, ^{
// Perform long running process
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI
});
});
// Continue doing other stuff on the
// main thread while process is running.
And here goes my working example (iOS 6+). It displays frames of a stored video using the AVAssetReader class:
//...prepare the AVAssetReader* asset_reader earlier and start reading frames now:
[asset_reader startReading];
dispatch_queue_t readerQueue = dispatch_queue_create("Reader Queue", NULL);
dispatch_async(readerQueue, ^{
CMSampleBufferRef buffer;
while ( [asset_reader status]==AVAssetReaderStatusReading )
{
buffer = [asset_reader_output copyNextSampleBuffer];
if (buffer!=nil)
{
//The point is here: to use the main queue for actual UI operations
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI using the AVCaptureVideoDataOutputSampleBufferDelegate style function
[self captureOutput:nil didOutputSampleBuffer:buffer fromConnection:nil];
CFRelease (buffer);
});
}
}
});
The first part of this sample may be found here in Damian's answer.
I'd like to offer a clean solution to the given problem.
I agree with other posters that in an ideal situation all the heavy lifting should be done in a background thread, however there are times when this simply isn't possible because the time consuming part requires lots of accessing to non thread-safe methods such as those offered by UIKit. In my case, initialising my UI is time consuming and there's nothing I can run in the background, so my best option is to update a progress bar during the init.
However, once we think in terms of the ideal GCD approach, the solution is actually a simple. We do all the work in a background thread, dividing it into chucks that are called synchronously on the main thread. The run loop will be run for each chuck, updating the UI and any progress bars etc.
- (void)myInit
{
// Start the work in a background thread.
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// Back to the main thread for a chunk of code
dispatch_sync(dispatch_get_main_queue(), ^{
...
// Update progress bar
self.progressIndicator.progress = ...:
});
// Next chunk
dispatch_sync(dispatch_get_main_queue(), ^{
...
// Update progress bar
self.progressIndicator.progress = ...:
});
...
});
}
Of course, this is essentially the same as Brad's technique, but his answer doesn't quite address the issue at hand - that of running a lot of non thread safe code while updating the UI periodically.
Joe -- if you are willing to set it up so that your lengthy processing all happens inside of drawRect, you can make it work. I just wrote a test project. It works. See code below.
LengthyComputationTestAppDelegate.h:
#import <UIKit/UIKit.h>
#interface LengthyComputationTestAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#end
LengthComputationTestAppDelegate.m:
#import "LengthyComputationTestAppDelegate.h"
#import "Incrementer.h"
#import "IncrementerProgressView.h"
#implementation LengthyComputationTestAppDelegate
#synthesize window;
#pragma mark -
#pragma mark Application lifecycle
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
IncrementerProgressView *ipv = [[IncrementerProgressView alloc]initWithFrame:self.window.bounds];
[self.window addSubview:ipv];
[ipv release];
[self.window makeKeyAndVisible];
return YES;
}
Incrementer.h:
#import <Foundation/Foundation.h>
//singleton object
#interface Incrementer : NSObject {
NSUInteger theInteger_;
}
#property (nonatomic) NSUInteger theInteger;
+(Incrementer *) sharedIncrementer;
-(NSUInteger) incrementForTimeInterval: (NSTimeInterval) timeInterval;
-(BOOL) finishedIncrementing;
incrementer.m:
#import "Incrementer.h"
#implementation Incrementer
#synthesize theInteger = theInteger_;
static Incrementer *inc = nil;
-(void) increment {
theInteger_++;
}
-(BOOL) finishedIncrementing {
return (theInteger_>=100000000);
}
-(NSUInteger) incrementForTimeInterval: (NSTimeInterval) timeInterval {
NSTimeInterval negativeTimeInterval = -1*timeInterval;
NSDate *startDate = [NSDate date];
while (!([self finishedIncrementing]) && [startDate timeIntervalSinceNow] > negativeTimeInterval)
[self increment];
return self.theInteger;
}
-(id) init {
if (self = [super init]) {
self.theInteger = 0;
}
return self;
}
#pragma mark --
#pragma mark singleton object methods
+ (Incrementer *) sharedIncrementer {
#synchronized(self) {
if (inc == nil) {
inc = [[Incrementer alloc]init];
}
}
return inc;
}
+ (id)allocWithZone:(NSZone *)zone {
#synchronized(self) {
if (inc == nil) {
inc = [super allocWithZone:zone];
return inc; // assignment and return on first allocation
}
}
return nil; // on subsequent allocation attempts return nil
}
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
- (id)retain {
return self;
}
- (unsigned)retainCount {
return UINT_MAX; // denotes an object that cannot be released
}
- (void)release {
//do nothing
}
- (id)autorelease {
return self;
}
#end
IncrementerProgressView.m:
#import "IncrementerProgressView.h"
#implementation IncrementerProgressView
#synthesize progressLabel = progressLabel_;
#synthesize nextUpdateTimer = nextUpdateTimer_;
-(id) initWithFrame:(CGRect)frame {
if (self = [super initWithFrame: frame]) {
progressLabel_ = [[UILabel alloc]initWithFrame:CGRectMake(20, 40, 300, 30)];
progressLabel_.font = [UIFont systemFontOfSize:26];
progressLabel_.adjustsFontSizeToFitWidth = YES;
progressLabel_.textColor = [UIColor blackColor];
[self addSubview:progressLabel_];
}
return self;
}
-(void) drawRect:(CGRect)rect {
[self.nextUpdateTimer invalidate];
Incrementer *shared = [Incrementer sharedIncrementer];
NSUInteger progress = [shared incrementForTimeInterval: 0.1];
self.progressLabel.text = [NSString stringWithFormat:#"Increments performed: %d", progress];
if (![shared finishedIncrementing])
self.nextUpdateTimer = [NSTimer scheduledTimerWithTimeInterval:0. target:self selector:(#selector(setNeedsDisplay)) userInfo:nil repeats:NO];
}
- (void)dealloc {
[super dealloc];
}
#end
Regarding the original issue:
In a word, you can (A) background the large painting, and call to the foreground for UI updates or (B) arguably controversially there are four 'immediate' methods suggested that do not use a background process. For the result of what works, run the demo program. It has #defines for all five methods.
Alternately per Tom Swift
Tom Swift has explained the amazing idea of quite simply manipulating the run loop. Here's how you trigger the run loop:
[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate: [NSDate date]];
This is a truly amazing piece of engineering. Of course one should be extremely careful when manipulating the run loop and as many pointed out this approach is strictly for experts.
However, a bizarre problem arises ...
Even though a number of the methods work, they don't actually "work" because there is a bizarre progressive-slow-down artifact you will see clearly in the demo.
Scroll to the 'answer' I pasted in below, showing the console output - you can see how it progressively slows.
Here's the new SO question:
Mysterious "progressive slowing" problem in run loop / drawRect
Here is V2 of the demo app...
http://www.fileswap.com/dl/p8lU3gAi/stepwiseDrawingV2.zip.html
You will see it tests all five methods,
#ifdef TOMSWIFTMETHOD
[self setNeedsDisplay];
[[NSRunLoop currentRunLoop]
runMode:NSDefaultRunLoopMode beforeDate:[NSDate date]];
#endif
#ifdef HOTPAW
[self setNeedsDisplay];
[CATransaction flush];
#endif
#ifdef LLOYDMETHOD
[CATransaction begin];
[self setNeedsDisplay];
[CATransaction commit];
#endif
#ifdef DDLONG
[self setNeedsDisplay];
[[self layer] displayIfNeeded];
#endif
#ifdef BACKGROUNDMETHOD
// here, the painting is being done in the bg, we have been
// called here in the foreground to inval
[self setNeedsDisplay];
#endif
You can see for yourself which methods work and which do not.
you can see the bizarre "progressive-slow-down". Why does it happen?
you can see with the controversial TOMSWIFT method, there is actually no problem at all with responsiveness. tap for response at any time (but still the bizarre "progressive-slow-down" problem)
So the overwhelming thing is this weird "progressive-slow-down": on each iteration, for unknown reasons, the time taken for a loop decreases. Note that this applies to both doing it "properly" (background look) or using one of the 'immediate' methods.
Practical solutions?
For anyone reading in the future, if you are actually unable to get this to work in production code because of the "mystery progressive slowdown", Felz and Void have each presented astounding solutions in the other specific question.