iOS crash using dispatch_sync - ios

In my iOS app I read a large CSV file and load it (via inserts) in a SQLite database.
This is my code:
- (void) parseCSVFile:(NSString *)path
{
__block BOOL isFileOk = NO;
__block BOOL atLeastOneRowProcessed = NO;
NSFileManager *fileMgr = [[NSFileManager alloc] init];
unsigned long totalSize = [[[fileMgr attributesOfItemAtPath:path error:nil] objectForKey:#"NSFileSize"] intValue];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:#"mydatabase.sqlite"];
const char *dbpath = [writableDBPath UTF8String];
dispatch_queue_t databaseQueue = [(AppDelegate *)[[UIApplication sharedApplication] delegate] databaseQueue];
dispatch_sync(databaseQueue, ^{
sqlite3 *ddbb;
NSLog(#"INIT DATABASE");
if (sqlite3_open(dbpath, &ddbb) == SQLITE_OK)
{
char* errorMessage;
sqlite3_exec(ddbb, "DROP TABLE IF EXISTS datainfile", NULL, NULL, &errorMessage);
sqlite3_exec(ddbb, "CREATE TABLE \"datainfile\" (\"field1\" TEXT PRIMARY KEY NOT NULL ,\"field2\" TEXT DEFAULT (null) ,\"field3\" TEXT DEFAULT (null) ,\"field4\" TEXT)", NULL, NULL, &errorMessage);
sqlite3_exec(ddbb, "BEGIN TRANSACTION", NULL, NULL, &errorMessage);
char buffer[] = "INSERT OR IGNORE INTO datainfile VALUES (?1, ?2, ?3, ?4)";
sqlite3_stmt* stmt;
sqlite3_prepare_v2(ddbb, buffer, (int)strlen(buffer), &stmt, NULL);
char *pathUTF8 = (char*)[path UTF8String];
FILE* stream = fopen(pathUTF8, "r");
char *field1 = "";
char *field2 = "";
char *field3 = "";
char *field4 = "";
NSLog(#"READING FILE");
unsigned long processedSize = 0;
char line[1024];
while(fgets(line, 1024, stream))
{
processedSize = processedSize + strlen(line) * sizeof(char);
char* tmp = strdup(line);
field1 = strtok (tmp,", \t");
field2 = strtok (NULL, ", \t");
field3 = strtok (NULL, ", \t");
field4 = strtok (NULL, "\r\n");
atLeastOneRowProcessed = YES;
if(!isFileOk)
{
isFileOk = [self checkIfFileIsACorrectCSVWithField1:field1 field2:field2 field3:field3 andField4:field4];
if(!isFileOk)
{
if([_delegate respondsToSelector:#selector(fileParserError:)])
[_delegate fileParserError:_reportErrorMessage];
free(tmp);
break;
}
}
sqlite3_bind_text(stmt, 1, field1, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 2, field2, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 3, field3, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 4, field4, -1, SQLITE_STATIC);
if (sqlite3_step(stmt) != SQLITE_DONE)
{
NSLog(#"ERROR: Commit Failed in line %s!\n", line);
// If there was an error we try to repeat the commit
sqlite3_reset(stmt);
sqlite3_bind_text(stmt, 1, field1, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 2, field2, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 3, field3, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 4, field4, -1, SQLITE_STATIC);
if (sqlite3_step(stmt) != SQLITE_DONE)
{
NSLog(#"ERROR: Commit failed again");
}
else
{
NSLog(#"ERROR: Commit success");
}
}
sqlite3_reset(stmt);
free(tmp);
}
if(isFileOk)
{
NSLog(#"File read and now the commit starts");
sqlite3_exec(ddbb, "COMMIT TRANSACTION", NULL, NULL, &errorMessage);
_phase = PARSER_PHASE_CREATING_INDEX;
sqlite3_exec(ddbb, "CREATE INDEX field1 ON datainfile(field1);", NULL, NULL, &errorMessage);
sqlite3_finalize(stmt);
NSLog(#"Commit has ended");
}
}
sqlite3_close(ddbb);
});
if(isFileOk)
{
if([_delegate respondsToSelector:#selector(fileParserFinished)])
[_delegate fileParserFinished];
}
else
{
if(!atLeastOneRowProcessed)
{
_reportErrorMessage = NSLocalizedString(#"TXT_FILEERROR_NOPROCESSED", Nil);
if([_delegate respondsToSelector:#selector(fileParserError:)])
[_delegate fileParserError:_reportErrorMessage];
}
}
}
databaseQueue is an attribute of AppDelegate created this way:
_databaseQueue = dispatch_queue_create("com.mycompany.myapp.database", 0);
Well, this is working well almost always. But sometimes it happens a strange crash. This is the log:
Crashed: com.mycompany.myapp.database
0 libsystem_kernel.dylib 0x1865d1014 __pthread_kill + 8
1 libsystem_pthread.dylib 0x18669b264 pthread_kill + 112
2 libsystem_c.dylib 0x1865459c4 abort + 140
3 libsystem_c.dylib 0x186545ad8 _UTF2_init + 82
4 libsystem_c.dylib 0x186559c7c __chk_fail_overlap + 54
5 libsystem_c.dylib 0x186559c44 __chk_fail + 18
6 libsystem_c.dylib 0x18655a04c __vsprintf_chk + 134
7 MyApp 0x1000c151c __32-[FileParser parseCSVFile:]_block_invoke (FileParser.m:650)
8 libdispatch.dylib 0x18648e9a0 _dispatch_client_callout + 16
9 libdispatch.dylib 0x18649bee0 _dispatch_barrier_sync_f_invoke + 84
10 MyApp 0x1000c1084 -[FileParser parseCSVFile:] (FileParser.m:734)
11 MyApp 0x1000bed28 -[FileParser readFileWithPath:andDelegate:] (FileParser.m:147)
12 MyApp 0x100041a64 __57-[HomeViewController readFileWithPath:]_block_invoke_2 (HomeViewController.m:625)
13 libdispatch.dylib 0x18648e9e0 _dispatch_call_block_and_release + 24
14 libdispatch.dylib 0x18648e9a0 _dispatch_client_callout + 16
15 libdispatch.dylib 0x18649d0d4 _dispatch_queue_override_invoke + 644
16 libdispatch.dylib 0x18649ea50 _dispatch_root_queue_drain + 540
17 libdispatch.dylib 0x18649e7d0 _dispatch_worker_thread3 + 124
18 libsystem_pthread.dylib 0x186697100 _pthread_wqthread + 1096
19 libsystem_pthread.dylib 0x186696cac start_wqthread + 4
It happens in the lines 734 and 650 of my FileParser, and these lines are:
(line 734) the second if(isFileOk)
Just the first line after leaving the dispatch_sync.
(line 650) processedSize = processedSize + strlen(line) * sizeof(char);
Reading the first line of the CSV file.
It seems to me like the dispatch_sync leaves its code before it's executed, calling "simultaneously" a line outside the block and one line inside the block.
I must add that when this crash has happened the line that crashed outside the block is always if(isFileOk), but the line that crashed inside the block varies.
What could it be happening?

Related

SocketStream::read crashes

We use an in NSInputStream to receive data from an IMAP-server. We see a stange crash in Xcodes Crashes that we are not able to reproduce. The InputStream is initialized like this (i omitted some sanity-checks):
- (void)getStreamsToServer:(NSString *)hostName
andPort:(NSUInteger)port
andInputStream:(NSInputStream **)inputStream
andOutputStream:(NSOutputStream **)outputStream
{
CFReadStreamRef readStream = NULL;
CFWriteStreamRef writeStream = NULL;
CFStreamCreatePairWithSocketToHost(NULL,
(__bridge CFStringRef)(hostName),
(unsigned int)port,
&readStream,
&writeStream);
*inputStream = (__bridge_transfer NSInputStream *)readStream;
*outputStream = (__bridge_transfer NSOutputStream *)writeStream;
}
Later, we upgrade the streams to SSL:
[_inputStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL
forKey:NSStreamSocketSecurityLevelKey];
[_outputStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL
forKey:NSStreamSocketSecurityLevelKey];
And we try to read data from it like this:
NSUInteger READ_BUFFER_LENGTH = 16 * 1024;
uint8_t readBuffer[READ_BUFFER_LENGTH];
NSInteger readBytesCount = 0;
if([_inputStream hasBytesAvailable]) {
readBytesCount = [_inputStream read:readBuffer
maxLength:READ_BUFFER_LENGTH];
if (readBytesCount > 0) {
NSData *partialData = [NSData dataWithBytes:(const void *)readBuffer
length:readBytesCount];
}
This works in 99,99% of the time. But Xcodes crash reporter gives us some headache with this crash:
0 CoreFoundation 0x0000000184970620 CFHash + 360 (CFRuntime.c:1080)
1 CoreFoundation 0x00000001849718b8 CFBasicHashGetCountOfKey + 160 (CFBasicHash.c:455)
2 CoreFoundation 0x00000001849718b8 CFBasicHashGetCountOfKey + 160 (CFBasicHash.c:455)
3 CoreFoundation 0x0000000184971804 CFSetContainsValue + 208 (CFSet.c:405)
4 CoreFoundation 0x00000001849a0148 CFRunLoopRemoveSource + 164 (CFRunLoop.c:3542)
5 CFNetwork 0x0000000185113e78 SocketStream::read(__CFReadStream*, unsigned char*, long, CFStreamError*, unsigned char*) + 604 (SocketStream.cpp:2757)
6 CoreFoundation 0x000000018499c7c8 CFReadStreamRead + 480 (CFStream.c:1146)
This crash takes places in the line with [NSData dataWithBytes:length:].
Do you have any idea, how to provoke, debug or fix this error?
Try using SocketIO
socket.io-client-swift

Crash while using CFHostCreateWithName: "Attempt to start the Thread again"

help!
I met a crash when i try to get the addresses for the give hostname;
the crash log shows like this:
1 libobjc.A.dylib 0x0000000197d180e4 objc_exception_throw + 56
2 CoreFoundation 0x0000000186044218 -[NSException initWithCoder:]
3 Foundation 0x0000000186e9528c -[NSThread start] + 136
4 ZFirewall7.dylib 0x0000000105e4d768 -[FiPSettingsManager doHostStuff:] + 1124
5 ZFirewall7.dylib 0x0000000105e48424 _ZL23my_CFHostCreateWithNamePPK13__CFAllocatorPPK10__CFString + 164
6 Upload 0x000000010220d1c0 +[UploadUtil addressesForHostname:] (UploadUtil.m:277)
7 Upload 0x000000010220d120 +[UploadUtil addressForHostname:] (UploadUtil.m:267)
....
*** -[NSThread start]: attempt to start the thread again
and my code is:
+(NSArray *)addressesForHostname:(NSString *)hostname
{
// Get the addresses for the given hostname.
CFHostRef hostRef = CFHostCreateWithName(kCFAllocatorDefault, (__bridge CFStringRef)hostname);
BOOL isSuccess = CFHostStartInfoResolution(hostRef, kCFHostAddresses, nil);
if (!isSuccess)
{
CFRelease(hostRef);
return nil;
}
CFArrayRef addressesRef = CFHostGetAddressing(hostRef, nil);
if (addressesRef == nil)
{
CFRelease(hostRef);
return nil;
}
// Convert these addresses into strings.
char ipAddress[INET6_ADDRSTRLEN];
NSMutableArray *addresses = [NSMutableArray array];
CFIndex numAddresses = CFArrayGetCount(addressesRef);
for (CFIndex currentIndex = 0; currentIndex < numAddresses; currentIndex++) {
struct sockaddr *address = (struct sockaddr *)CFDataGetBytePtr(CFArrayGetValueAtIndex(addressesRef, currentIndex));
if (address == nil)
{
CFRelease(hostRef);
return nil;
}
getnameinfo(address, address->sa_len, ipAddress, INET6_ADDRSTRLEN, nil, 0, NI_NUMERICHOST);
[addresses addObject:[NSString stringWithCString:ipAddress encoding:NSASCIIStringEncoding]];
}
CFRelease(hostRef);
return addresses;
}
this function do called by several threads, and the doc of CFHostCreateWithName says that it is thread safe, now i have no idea about how to solve this problem.
help!

Getting IPV4 address as a String

Im using Bonjour to browse for available services from specific devices. I can successfully obtain and resolve the services returned from the browse, however I would like to take the service and retrieve it's IPV4 address in String form. To do this I am using the arpa/inet library to translate the NSdata object received by the NSNetService.addresses into a String. The code below works most of the time however occasionally the line below results in the crashing of the app.
NSString *ipString = [NSString stringWithFormat: #"%s",
inet_ntoa(socketAddress->sin_addr)];
The error: EXC_BAD_ACCESS
I am sure it has to do with the way I have declared this code, any ideas?
+ (NSString *)getStringFromAddressData:(NSData *)dataIn {
struct sockaddr_in *socketAddress = nil;
socketAddress = (struct sockaddr_in *)[dataIn bytes];
NSString *ipString = [NSString stringWithFormat: #"%s", inet_ntoa(socketAddress->sin_addr)];
return ipString;
}
Stack trace:
2015-08-13 08:23:45.860 Semiphores[4664:2119558] Stack trace : (
0 Semiphores 0x0000000100001c0b +[BonjourAddressHelper getStringFromAddressData:] + 107
1 Semiphores 0x0000000100007c8a _TFFC10Semiphores17BonjourController15resolveServicesFS0_FPSs9AnyObject_T_U_FT_T_ + 2682
2 Semiphores 0x0000000100007207 _TTRXFo__dT__XFdCb__dT__ + 39
3 libdispatch.dylib 0x00000001006852bb _dispatch_call_block_and_release + 12
4 libdispatch.dylib 0x000000010067fd43 _dispatch_client_callout + 8
5 libdispatch.dylib 0x0000000100683283 _dispatch_root_queue_drain + 1471
6 libdispatch.dylib 0x0000000100694cd0 _dispatch_worker_thread3 + 106
7 libsystem_pthread.dylib 0x00007fff86770637 _pthread_wqthread + 729
8 libsystem_pthread.dylib 0x00007fff8676e40d start_wqthread + 13
)
What would happen if someone passed in nil data?
+ (NSString *)getStringFromAddressData:(NSData *)dataIn {
if (dataIn != nil) {
struct sockaddr_in *socketAddress = nil;
socketAddress = (struct sockaddr_in *)[dataIn bytes];
NSString *ipString = [NSString stringWithFormat: #"%s", inet_ntoa(socketAddress->sin_addr)];
return ipString;
}
return #"";
}

Getting CocoaLibSpotify error in SPPlaylist.m

When I try to build my project i get the following error on line 827
/PATH_TO_PROJECT/Libs/CocoaLibSpotify/Model/SPPlaylist.m:827:25: Implicit conversion of Objective-C pointer type 'AVAssetTrack *' to C pointer type 'sp_track *' (aka 'struct sp_track *') requires a bridged cast
Here is the code where the error occurs:
805 -(void)addItems:(NSArray *)newItems atIndex:(NSUInteger)index callback:(SPErrorableOperationCallback)block {
806
807 SPDispatchAsync(^{
808
809 if (newItems.count == 0) {
810 dispatch_async(dispatch_get_main_queue(), ^{
811 if (block) block([NSError spotifyErrorWithCode:SP_ERROR_INVALID_INDATA]);
812 });
813 return;
814 }
815
816 sp_track **tracks = malloc(sizeof(sp_track *) * newItems.count);
817
818 // libSpotify iterates through the array and inserts each track at the given index,
819 // which ends up reversing the expected order. Defeat this by constructing a backwards
820 // array.
821 for (int currentTrack = (int)newItems.count - 1; currentTrack >= 0; currentTrack--) {
822
823 sp_track *track;
824 id item = [newItems objectAtIndex:currentTrack];
825
826 if ([item isKindOfClass:[SPTrack class]])
827 track = [item track];
828 else
829 track = [(SPTrack *)((SPPlaylistItem *)item).item track];
830
831 tracks[currentTrack] = track;
832 }
833 sp_track *const *trackPointer = tracks;
834
835 if (block)
836 [self.addCallbackStack addObject:block];
837
838 sp_error errorCode = sp_playlist_add_tracks(self.playlist, trackPointer, (int)newItems.count, (int)index, self.session.session);
839 free(tracks);
840 tracks = NULL;
841
842 NSError *error = nil;
843 if (errorCode != SP_ERROR_OK)
844 error = [NSError spotifyErrorWithCode:errorCode];
845
846 if (error && block) {
847 [self.addCallbackStack removeObject:block];
848 dispatch_async(dispatch_get_main_queue(), ^{ block(error); });
849 }
850 });
851 }
Thanks for your help!
The error is pretty self-explanatory - you need to make a bridged cast.
track = (__bridge sp_track *)[item track];
or
track = [(SPPlaylistItem *)item track];

SIGABRT crash because of SDWebImageCache

I am getting this crash log (getting it from users, cannot reproduce it):
Threads
_________________________________
Thread: Unknown Name (Crashed)
0 libsystem_kernel.dylib 0x382dd1fc __pthread_kill + 8
1 libsystem_c.dylib 0x3828dffd abort + 77
2 libc++abi.dylib 0x375bccd7 abort_message + 75
3 libc++abi.dylib 0x375d320d _ZSt11__terminatePFvvE + 149
4 libc++abi.dylib 0x375d2d2d __cxa_increment_exception_refcount + 1
5 libobjc.A.dylib 0x37d1e7f7 objc_exception_rethrow + 43
6 CoreFoundation 0x2d811c9d CFRunLoopRunSpecific + 641
7 CoreFoundation 0x2d811a0b CFRunLoopRunInMode + 107
8 GraphicsServices 0x32538283 GSEventRunModal + 139
9 UIKit 0x300b5049 UIApplicationMain + 1137
10 Paok FC 0x000c6723 main (main.m:12)
Thread: Unknown Name
0 libsystem_kernel.dylib 0x382ca83c kevent64 + 24
1 libdispatch.dylib 0x3820af9b _dispatch_mgr_thread$VARIANT$mp + 39
Thread: Unknown Name
0 libsystem_kernel.dylib 0x382ddc7c __workq_kernreturn + 8
Thread: Unknown Name
0 libsystem_kernel.dylib 0x382caadc semaphore_wait_trap + 8
1 libdispatch.dylib 0x38207a17 _dispatch_barrier_sync_f_slow + 139
2 Paok FC 0x0010c369 __42-[SDImageCache queryDiskCacheForKey:done:]_block_invoke (SDImageCache.m:308)
3 libdispatch.dylib 0x38201d1b _dispatch_call_block_and_release + 11
4 libdispatch.dylib 0x38208273 _dispatch_queue_drain$VARIANT$mp + 375
5 libdispatch.dylib 0x3820806b _dispatch_queue_invoke$VARIANT$mp + 43
6 libdispatch.dylib 0x38208ce1 _dispatch_root_queue_drain + 77
7 libdispatch.dylib 0x38208f59 _dispatch_worker_thread2 + 57
8 libsystem_pthread.dylib 0x38343dbf _pthread_wqthread + 299
Thread: Unknown Name
0 libsystem_kernel.dylib 0x382caa8c mach_msg_trap + 20
1 CoreFoundation 0x2d8a87c3 __CFRunLoopServiceMachPort + 155
2 CoreFoundation 0x2d8a6ee9 __CFRunLoopRun + 785
3 CoreFoundation 0x2d811c27 CFRunLoopRunSpecific + 523
4 CoreFoundation 0x2d811a0b CFRunLoopRunInMode + 107
5 Foundation 0x2e24c2f7 +[NSURLConnection(Loader) _resourceLoadLoop:] + 319
6 Foundation 0x2e2c1c87 __NSThread__main__ + 1063
7 libsystem_pthread.dylib 0x38345c1d _pthread_body + 141
8 libsystem_pthread.dylib 0x38345b8f _pthread_start + 103
Thread: Unknown Name
0 libsystem_kernel.dylib 0x382dd440 __select + 20
1 libsystem_pthread.dylib 0x38345c1d _pthread_body + 141
2 libsystem_pthread.dylib 0x38345b8f _pthread_start + 103
Thread: Unknown Name
0 libsystem_kernel.dylib 0x382ddc7c __workq_kernreturn + 8
Thread: Unknown Name
0 libsystem_kernel.dylib 0x382ddc7c __workq_kernreturn + 8
Crashed Registers
_________________________________
r12 0x148
r10 0x190
r11 0x1559ca80
cpsr 0x10
r4 0x6
r5 0x3a15318c
r6 0x0
r7 0x27d45a60
r0 0x0
r1 0x0
r2 0x0
r3 0x2060
sp 0x27d45a54
r8 0x1558f8e0
r9 0x1
pc 0x382dd1fc
lr 0x38346a33
The method crashing in SDWebImageCache.m is this:
- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(void (^)(UIImage *image, SDImageCacheType cacheType))doneBlock
{
NSOperation *operation = NSOperation.new;
if (!doneBlock) return nil;
if (!key)
{
doneBlock(nil, SDImageCacheTypeNone);
return nil;
}
// First check the in-memory cache...
UIImage *image = [self imageFromMemoryCacheForKey:key];
if (image)
{
doneBlock(image, SDImageCacheTypeMemory);
return nil;
}
dispatch_async(self.ioQueue, ^
{
if (operation.isCancelled)
{
return;
}
#autoreleasepool
{
UIImage *diskImage = [self diskImageForKey:key];
if (diskImage)
{
CGFloat cost = diskImage.size.height * diskImage.size.width * diskImage.scale;
[self.memCache setObject:diskImage forKey:key cost:cost];
}
dispatch_main_sync_safe(^
{
doneBlock(diskImage, SDImageCacheTypeDisk);
});
}
});
return operation;
}
Has anyone experience this crash? If yes, is there a way to fix this?
Line 308 where the crash occurs is this:
dispatch_main_sync_safe(^
{
doneBlock(diskImage, SDImageCacheTypeDisk);
});
This method is called in another class from this method:
- (id<SDWebImageOperation>)downloadWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletedWithFinishedBlock)completedBlock
{
// Invoking this method without a completedBlock is pointless
NSParameterAssert(completedBlock);
// Very common mistake is to send the URL using NSString object instead of NSURL. For some strange reason, XCode won't
// throw any warning for this type mismatch. Here we failsafe this error by allowing URLs to be passed as NSString.
if ([url isKindOfClass:NSString.class])
{
url = [NSURL URLWithString:(NSString *)url];
}
// Prevents app crashing on argument type error like sending NSNull instead of NSURL
if (![url isKindOfClass:NSURL.class])
{
url = nil;
}
__block SDWebImageCombinedOperation *operation = SDWebImageCombinedOperation.new;
__weak SDWebImageCombinedOperation *weakOperation = operation;
BOOL isFailedUrl = NO;
#synchronized(self.failedURLs)
{
isFailedUrl = [self.failedURLs containsObject:url];
}
if (!url || (!(options & SDWebImageRetryFailed) && isFailedUrl))
{
dispatch_main_sync_safe(^
{
NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil];
completedBlock(nil, error, SDImageCacheTypeNone, YES);
});
return operation;
}
#synchronized(self.runningOperations)
{
[self.runningOperations addObject:operation];
}
NSString *key = [self cacheKeyForURL:url];
operation.cacheOperation = [self.imageCache queryDiskCacheForKey:key done:^(UIImage *image, SDImageCacheType cacheType)
{
if (operation.isCancelled)
{
#synchronized(self.runningOperations)
{
[self.runningOperations removeObject:operation];
}
return;
}
if ((!image || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:#selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url]))
{
if (image && options & SDWebImageRefreshCached)
{
dispatch_main_sync_safe(^
{
// If image was found in the cache bug SDWebImageRefreshCached is provided, notify about the cached image
// AND try to re-download it in order to let a chance to NSURLCache to refresh it from server.
completedBlock(image, nil, cacheType, YES);
});
}
// download if no image or requested to refresh anyway, and download allowed by delegate
SDWebImageDownloaderOptions downloaderOptions = 0;
if (options & SDWebImageLowPriority) downloaderOptions |= SDWebImageDownloaderLowPriority;
if (options & SDWebImageProgressiveDownload) downloaderOptions |= SDWebImageDownloaderProgressiveDownload;
if (options & SDWebImageRefreshCached) downloaderOptions |= SDWebImageDownloaderUseNSURLCache;
if (options & SDWebImageContinueInBackground) downloaderOptions |= SDWebImageDownloaderContinueInBackground;
if (options & SDWebImageHandleCookies) downloaderOptions |= SDWebImageDownloaderHandleCookies;
if (image && options & SDWebImageRefreshCached)
{
// force progressive off if image already cached but forced refreshing
downloaderOptions &= ~SDWebImageDownloaderProgressiveDownload;
// ignore image read from NSURLCache if image if cached but force refreshing
downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse;
}
id<SDWebImageOperation> subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished)
{
if (weakOperation.isCancelled)
{
dispatch_main_sync_safe(^
{
completedBlock(nil, nil, SDImageCacheTypeNone, finished);
});
}
else if (error)
{
dispatch_main_sync_safe(^
{
completedBlock(nil, error, SDImageCacheTypeNone, finished);
});
if (error.code != NSURLErrorNotConnectedToInternet)
{
#synchronized(self.failedURLs)
{
[self.failedURLs addObject:url];
}
}
}
else
{
BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly);
if (options & SDWebImageRefreshCached && image && !downloadedImage)
{
// Image refresh hit the NSURLCache cache, do not call the completion block
}
// NOTE: We don't call transformDownloadedImage delegate method on animated images as most transformation code would mangle it
else if (downloadedImage && !downloadedImage.images && [self.delegate respondsToSelector:#selector(imageManager:transformDownloadedImage:withURL:)])
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^
{
UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url];
dispatch_main_sync_safe(^
{
completedBlock(transformedImage, nil, SDImageCacheTypeNone, finished);
});
if (transformedImage && finished)
{
NSData *dataToStore = [transformedImage isEqual:downloadedImage] ? data : nil;
[self.imageCache storeImage:transformedImage imageData:dataToStore forKey:key toDisk:cacheOnDisk];
}
});
}
else
{
dispatch_main_sync_safe(^
{
completedBlock(downloadedImage, nil, SDImageCacheTypeNone, finished);
});
if (downloadedImage && finished)
{
[self.imageCache storeImage:downloadedImage imageData:data forKey:key toDisk:cacheOnDisk];
}
}
}
if (finished)
{
#synchronized(self.runningOperations)
{
[self.runningOperations removeObject:operation];
}
}
}];
operation.cancelBlock = ^{[subOperation cancel];};
}
else if (image)
{
dispatch_main_sync_safe(^
{
completedBlock(image, nil, cacheType, YES);
});
#synchronized(self.runningOperations)
{
[self.runningOperations removeObject:operation];
}
}
else
{
// Image not in cache and download disallowed by delegate
dispatch_main_sync_safe(^
{
completedBlock(nil, nil, SDImageCacheTypeNone, YES);
});
#synchronized(self.runningOperations)
{
[self.runningOperations removeObject:operation];
}
}
}];
return operation;
}
As the report says, this is related to semaphores. I found the line in question you commented
enterdispatch_main_sync_safe(^ { doneBlock(diskImage, SDImageCacheTypeDisk); });
to be exactly the one that is reported causing deadlock on SDWebImage GitHub error report [1]. If I understand correctly, this issue is already fixed, newest patch commit is 2 hours ago.
Maybe you can get that in some future release, or if you can use git, there it is..
My source:
[1] https://github.com/rs/SDWebImage/issues/507

Resources