I did develop a technique to keep the network activity indicator spinning when
multiple sources may request to have it spinning.
In my app delegate, I have those methods:
- (void)startNetworkIndicatorForInstance:(id)anInstance
{
NSString *addressAsString = [NSString stringWithFormat:@"%p",
anInstance];
if (addressAsString == nil) {
return;
}
NSNumber *numberOfInvocations = [self.networkIndicatorClients
objectForKey:addressAsString];
if (numberOfInvocations == nil) {
numberOfInvocations = [NSNumber numberWithInt:1];
[self.networkIndicatorClients setObject:numberOfInvocations
forKey:addressAsString];
}
else {
[self.networkIndicatorClients setObject:[NSNumber
numberWithInt:[numberOfInvocations intValue] + 1] forKey:addressAsString];
}
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
_networkIndicatorStack++;
if (_networkIndicatorStack > 0 && ! [UIApplication
sharedApplication].networkActivityIndicatorVisible)
[UIApplication
sharedApplication].networkActivityIndicatorVisible = YES;
}];
}
- (void)reallyStopNetworkIndicatorFromTimer:(NSTimer *)aTimer
{
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[aTimer invalidate];
[UIApplication
sharedApplication].networkActivityIndicatorVisible = NO;
}];
}
- (void)stopNetworkIndicatorOnMainThread
{
// In case of very short start and stop, we don't want to have the
indicator flickering so when we get a stop, we delay
// the hiding by one second
[[self class] cancelPreviousPerformRequestsWithTarget:self
selector:@selector(reallyStopNetworkIndicatorFromTimer:) object:NULL];
[self performSelector:@selector(reallyStopNetworkIndicatorFromTimer:)
withObject:NULL afterDelay:PERFORM_DELAY];
}
- (void)stopNetworkIndicatorForInstance:(id)anInstance
{
NSString *addressAsString = [[NSString stringWithFormat:@"%p",
anInstance] copy];
NSNumber *numberOfInvocations = [self.networkIndicatorClients
objectForKey:addressAsString];
if (numberOfInvocations != nil) {
if ([numberOfInvocations intValue] > 1) {
[self.networkIndicatorClients setObject:[NSNumber
numberWithInt:[numberOfInvocations intValue] - 1] forKey:addressAsString];
}
else {
[self.networkIndicatorClients
removeObjectForKey:addressAsString];
}
}
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
_networkIndicatorStack--;
if (_networkIndicatorStack < 0) {
_networkIndicatorStack = 0;
}
if (_networkIndicatorStack == 0) {
[self stopNetworkIndicatorOnMainThread];
}
}];
}
I have a setup with multiple UITableViewCells that download content from a
server. They each send the app delegate startNetworkIndicatorForInstance: and
they give their own pointer as the caller. As you can see in that method
implementation, the app delegate object converts the address into a string and
then check to see if that instance has already sent a
startNetworkIndicatorForInstance:. If it hasn't, I create an NSNumber and set
this object for the pointer address as a string as the key. I think it's fairly
obvious what is happening. When a UITableViewCell is finished with its
downloads (can have more than one), it sends the delegate
stopNetworkIndicatorForInstance:.
If the UITableViewCell is about to be deallocated and had a download executing,
it cancels the download and send the app delegate
stopNetworkIndicatorForInstance:
This all works pretty well except that from time to time, the app will crash
with a SIGABRT:
• 0 erodr 0x0011cfe6 testflight_backtrace + 238
• 1 erodr 0x0011dcd0 TFSignalHandler + 264
• 2 libsystem_c.dylib 0x3901de92 _sigtramp + 42
• 3 libsystem_c.dylib 0x39014122 pthread_kill + 58
• 4 libsystem_c.dylib 0x39050972 abort + 94
• 5 libsystem_c.dylib 0x3901af18 <redacted> + 0
• 6 libsystem_c.dylib 0x3901b234 <redacted> + 84
• 7 libsystem_c.dylib 0x38fe92de free + 170
• 8 CoreFoundation 0x35262cae <redacted> + 206
• 9 CoreFoundation 0x351c5560 <redacted> + 732
• 10 erodr 0x000d7b06 -[ERAppDelegate
startNetworkIndicatorForInstance:] (ERAppDelegate.m:747)
• 11 erodr 0x000f531c __68-[StreamerImageTextCell
downloadPictureFromURL:forKey:inDictionary:]_block_invoke_0
(StreamerImageTextCell.m:81)
• 12 Foundation 0x32ca5538 <redacted> + 200
• 13 Foundation 0x32c9cdb8 <redacted> + 840
• 14 Foundation 0x32d153da <redacted> + 102
• 15 libdispatch.dylib 0x33f5411e <redacted> + 10
• 16 libdispatch.dylib 0x33f58960 <redacted> + 252
• 17 libdispatch.dylib 0x33f58ac0 <redacted> + 84
• 18 libsystem_c.dylib 0x38feba10 <redacted> + 360
• 19 libsystem_c.dylib 0x38feb8a3 start_wqthread + 7
Line 747 of ERAppDelegate.m is:
[self.networkIndicatorClients setObject:numberOfInvocations
forKey:addressAsString];
I have no idea why this crash is happening. I use the same technique in another
app and never had a crash in the startNetworkIndicatorForInstance:.
Anybody has any idea of what is going on?
Thanks!
-Laurent.
--
Laurent Daudelin
AIM/iChat/Skype:LaurentDaudelin
http://www.nemesys-soft.com/
Logiciels Nemesys Software
[email protected]
_______________________________________________
Cocoa-dev mailing list ([email protected])
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com
This email sent to [email protected]