Skip to content

Commit 4c3acd9

Browse files
author
Olivier Poitrey
committed
Fix SDURLCache with iOS 5+ (fix #30, #29)
- Disable SDURLCache at runtime if iOS version is >5 as this version now includes disk caching support - Exchange category with a wrapper to add NSCoder protocol support to NSCachedURLResponse
1 parent 9fdb79a commit 4c3acd9

File tree

5 files changed

+142
-32
lines changed

5 files changed

+142
-32
lines changed

SDCachedURLResponse.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//
2+
// SDCachedURLResponse.h
3+
// SDURLCache
4+
//
5+
// Created by Olivier Poitrey on 12/05/12.
6+
// Copyright (c) 2012 Dailymotion. All rights reserved.
7+
//
8+
9+
#import <Foundation/Foundation.h>
10+
11+
@interface SDCachedURLResponse : NSObject <NSCoding, NSCopying>
12+
13+
@property (nonatomic, retain) NSCachedURLResponse *response;
14+
15+
+ (id)cachedURLResponseWithNSCachedURLResponse:(NSCachedURLResponse *)response;
16+
17+
@end

SDCachedURLResponse.m

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//
2+
// SDCachedURLResponse.m
3+
// SDURLCache
4+
//
5+
// Created by Olivier Poitrey on 12/05/12.
6+
// Copyright (c) 2012 Dailymotion. All rights reserved.
7+
//
8+
9+
#import "SDCachedURLResponse.h"
10+
11+
@implementation SDCachedURLResponse
12+
13+
@synthesize response;
14+
15+
+ (id)cachedURLResponseWithNSCachedURLResponse:(NSCachedURLResponse *)response
16+
{
17+
SDCachedURLResponse *wrappedResponse = [[SDCachedURLResponse alloc] init];
18+
wrappedResponse.response = response;
19+
return [wrappedResponse autorelease];
20+
}
21+
22+
#pragma mark NSCopying Methods
23+
24+
- (id)copyWithZone:(NSZone *)zone
25+
{
26+
SDCachedURLResponse *newResponse = [[[self class] allocWithZone:zone] init];
27+
28+
if (newResponse)
29+
{
30+
newResponse.response = [[self.response copyWithZone:zone] autorelease];
31+
}
32+
33+
return newResponse;
34+
}
35+
36+
#pragma mark NSCoding Methods
37+
- (void)encodeWithCoder:(NSCoder *)coder
38+
{
39+
// force write the data of underlying cached response
40+
[coder encodeDataObject:self.response.data];
41+
[coder encodeObject:self.response.response forKey:@"response"];
42+
[coder encodeObject:self.response.userInfo forKey:@"userInfo"];
43+
[coder encodeInt:self.response.storagePolicy forKey:@"storagePolicy"];
44+
}
45+
46+
- (id)initWithCoder:(NSCoder *)coder
47+
{
48+
if ((self = [super init]))
49+
{
50+
self.response = [[[NSCachedURLResponse alloc] initWithResponse:[coder decodeObjectForKey:@"response"]
51+
data:[coder decodeDataObject]
52+
userInfo:[coder decodeObjectForKey:@"userInfo"]
53+
storagePolicy:[coder decodeIntForKey:@"storagePolicy"]] autorelease];
54+
}
55+
56+
return self;
57+
}
58+
59+
- (void)dealloc
60+
{
61+
[super dealloc];
62+
[response release], response = nil;
63+
}
64+
65+
@end

SDURLCache.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
@private
1414
NSString *diskCachePath;
1515
NSMutableDictionary *diskCacheInfo;
16-
BOOL diskCacheInfoDirty, ignoreMemoryOnlyStoragePolicy;
16+
BOOL diskCacheInfoDirty, ignoreMemoryOnlyStoragePolicy, disabled;
1717
NSUInteger diskCacheUsage;
1818
NSTimeInterval minCacheInterval;
1919
NSOperationQueue *ioQueue;

SDURLCache.m

Lines changed: 51 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,18 @@
77
//
88

99
#import "SDURLCache.h"
10+
#import "SDCachedURLResponse.h"
1011
#import <CommonCrypto/CommonDigest.h>
12+
#import <UIKit/UIDevice.h>
13+
14+
// The removal of the NSCachedURLResponse category means that NSKeyedArchiver
15+
// will throw an EXC_BAD_ACCESS when attempting to load NSCachedURLResponse
16+
// data.
17+
// This means that this change requires a cache refresh, and a new cache key
18+
// namespace that will prevent this from happening.
19+
// Old cache keys will eventually be evicted from the system as new keys are
20+
// populated.
21+
static NSString *const kSDURLCacheVersion = @"V2";
1122

1223
static NSTimeInterval const kSDURLCacheInfoDefaultMinCacheInterval = 5 * 60; // 5 minute
1324
static NSString *const kSDURLCacheInfoFileName = @"cacheInfo.plist";
@@ -30,31 +41,6 @@
3041
return [dateFormatter autorelease];
3142
}
3243

33-
@implementation NSCachedURLResponse(NSCoder)
34-
#pragma clang diagnostic push
35-
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
36-
37-
- (void)encodeWithCoder:(NSCoder *)coder
38-
{
39-
[coder encodeDataObject:self.data];
40-
[coder encodeObject:self.response forKey:@"response"];
41-
[coder encodeObject:self.userInfo forKey:@"userInfo"];
42-
[coder encodeInt:self.storagePolicy forKey:@"storagePolicy"];
43-
}
44-
45-
- (id)initWithCoder:(NSCoder *)coder
46-
{
47-
return [self initWithResponse:[coder decodeObjectForKey:@"response"]
48-
data:[coder decodeDataObject]
49-
userInfo:[coder decodeObjectForKey:@"userInfo"]
50-
storagePolicy:[coder decodeIntForKey:@"storagePolicy"]];
51-
}
52-
53-
#pragma clang diagnostic pop
54-
55-
@end
56-
57-
5844
@interface SDURLCache ()
5945
@property (nonatomic, retain) NSString *diskCachePath;
6046
@property (nonatomic, readonly) NSMutableDictionary *diskCacheInfo;
@@ -87,8 +73,8 @@ + (NSString *)cacheKeyForURL:(NSURL *)url
8773
const char *str = [url.absoluteString UTF8String];
8874
unsigned char r[CC_MD5_DIGEST_LENGTH];
8975
CC_MD5(str, strlen(str), r);
90-
return [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
91-
r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10], r[11], r[12], r[13], r[14], r[15]];
76+
return [NSString stringWithFormat:@"%@_%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
77+
kSDURLCacheVersion, r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10], r[11], r[12], r[13], r[14], r[15]];
9278
}
9379

9480
/*
@@ -368,7 +354,8 @@ - (void)balanceDiskUsage
368354
- (void)storeToDisk:(NSDictionary *)context
369355
{
370356
NSURLRequest *request = [context objectForKey:@"request"];
371-
NSCachedURLResponse *cachedResponse = [context objectForKey:@"cachedResponse"];
357+
// use wrapper to ensure we save appropriate fields
358+
SDCachedURLResponse *cachedResponse = [SDCachedURLResponse cachedURLResponseWithNSCachedURLResponse:[context objectForKey:@"cachedResponse"]];
372359

373360
NSString *cacheKey = [SDURLCache cacheKeyForURL:request.URL];
374361
NSString *cacheFilePath = [diskCachePath stringByAppendingPathComponent:cacheKey];
@@ -436,7 +423,12 @@ + (NSString *)defaultCachePath
436423

437424
- (id)initWithMemoryCapacity:(NSUInteger)memoryCapacity diskCapacity:(NSUInteger)diskCapacity diskPath:(NSString *)path
438425
{
439-
if ((self = [super initWithMemoryCapacity:memoryCapacity diskCapacity:diskCapacity diskPath:path]))
426+
// iOS 5 implements disk caching. SDURLCache then disables itself at runtime if the current device OS
427+
// version is 5 or greater
428+
NSArray *version = [[UIDevice currentDevice].systemVersion componentsSeparatedByString:@"."];
429+
disabled = [[version objectAtIndex:0] intValue] >= 5;
430+
431+
if ((self = [super initWithMemoryCapacity:memoryCapacity diskCapacity:diskCapacity diskPath:path]) && !disabled)
440432
{
441433
self.minCacheInterval = kSDURLCacheInfoDefaultMinCacheInterval;
442434
self.diskCachePath = path;
@@ -455,6 +447,12 @@ - (id)initWithMemoryCapacity:(NSUInteger)memoryCapacity diskCapacity:(NSUInteger
455447

456448
- (void)storeCachedResponse:(NSCachedURLResponse *)cachedResponse forRequest:(NSURLRequest *)request
457449
{
450+
if (disabled)
451+
{
452+
[super storeCachedResponse:cachedResponse forRequest:request];
453+
return;
454+
}
455+
458456
request = [SDURLCache canonicalRequestForRequest:request];
459457

460458
if (request.cachePolicy == NSURLRequestReloadIgnoringLocalCacheData
@@ -500,6 +498,8 @@ - (void)storeCachedResponse:(NSCachedURLResponse *)cachedResponse forRequest:(NS
500498

501499
- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request
502500
{
501+
if (disabled) return [super cachedResponseForRequest:request];
502+
503503
request = [SDURLCache canonicalRequestForRequest:request];
504504

505505
NSCachedURLResponse *memoryResponse = [super cachedResponseForRequest:request];
@@ -517,7 +517,10 @@ - (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request
517517
NSMutableDictionary *accesses = [self.diskCacheInfo objectForKey:kSDURLCacheInfoAccessesKey];
518518
if ([accesses objectForKey:cacheKey]) // OPTI: Check for cache-hit in a in-memory dictionary before hitting the file system
519519
{
520-
NSCachedURLResponse *diskResponse = [NSKeyedUnarchiver unarchiveObjectWithFile:[diskCachePath stringByAppendingPathComponent:cacheKey]];
520+
// load wrapper
521+
SDCachedURLResponse *diskResponseWrapper = [NSKeyedUnarchiver unarchiveObjectWithFile:[diskCachePath stringByAppendingPathComponent:cacheKey]];
522+
NSCachedURLResponse *diskResponse = diskResponseWrapper.response;
523+
521524
if (diskResponse)
522525
{
523526
// OPTI: Log the entry last access time for LRU cache eviction algorithm but don't save the dictionary
@@ -547,6 +550,8 @@ - (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request
547550

548551
- (NSUInteger)currentDiskUsage
549552
{
553+
if (disabled) return [super currentDiskUsage];
554+
550555
if (!diskCacheInfo)
551556
{
552557
[self diskCacheInfo];
@@ -556,6 +561,12 @@ - (NSUInteger)currentDiskUsage
556561

557562
- (void)removeCachedResponseForRequest:(NSURLRequest *)request
558563
{
564+
if (disabled)
565+
{
566+
[super removeCachedResponseForRequest:request];
567+
return;
568+
}
569+
559570
request = [SDURLCache canonicalRequestForRequest:request];
560571

561572
[super removeCachedResponseForRequest:request];
@@ -567,6 +578,7 @@ - (void)removeAllCachedResponses
567578
{
568579
[super removeAllCachedResponses];
569580

581+
if (disabled) return;
570582
NSFileManager *fileManager = [[NSFileManager alloc] init];
571583
[fileManager removeItemAtPath:diskCachePath error:NULL];
572584
[fileManager release];
@@ -586,7 +598,9 @@ - (BOOL)isCached:(NSURL *)url
586598
{
587599
return YES;
588600
}
589-
601+
602+
if (disabled) return NO;
603+
590604
NSString *cacheKey = [SDURLCache cacheKeyForURL:url];
591605
NSString *cacheFile = [diskCachePath stringByAppendingPathComponent:cacheKey];
592606
NSFileManager *manager = [[NSFileManager alloc] init];
@@ -605,6 +619,12 @@ - (BOOL)isCached:(NSURL *)url
605619

606620
- (void)dealloc
607621
{
622+
if (disabled)
623+
{
624+
[super dealloc];
625+
return;
626+
}
627+
608628
[periodicMaintenanceTimer invalidate];
609629
[periodicMaintenanceTimer release], periodicMaintenanceTimer = nil;
610630
[periodicMaintenanceOperation release], periodicMaintenanceOperation = nil;

SDURLCache.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10+
5318A9AB155DD7B500AB6767 /* SDCachedURLResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 5318A9A9155DD7B500AB6767 /* SDCachedURLResponse.h */; };
11+
5318A9AC155DD7B500AB6767 /* SDCachedURLResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 5318A9AA155DD7B500AB6767 /* SDCachedURLResponse.m */; };
1012
53F557F4114EA63600A3DA4B /* SDURLCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 53F557F2114EA63600A3DA4B /* SDURLCache.h */; };
1113
53F557F5114EA63600A3DA4B /* SDURLCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 53F557F3114EA63600A3DA4B /* SDURLCache.m */; };
1214
53F5592C114F1ED800A3DA4B /* SDURLCacheTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 53F5592B114F1ED800A3DA4B /* SDURLCacheTests.m */; };
@@ -25,6 +27,8 @@
2527
/* End PBXContainerItemProxy section */
2628

2729
/* Begin PBXFileReference section */
30+
5318A9A9155DD7B500AB6767 /* SDCachedURLResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDCachedURLResponse.h; sourceTree = "<group>"; };
31+
5318A9AA155DD7B500AB6767 /* SDCachedURLResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDCachedURLResponse.m; sourceTree = "<group>"; };
2832
53F557F2114EA63600A3DA4B /* SDURLCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDURLCache.h; sourceTree = "<group>"; };
2933
53F557F3114EA63600A3DA4B /* SDURLCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDURLCache.m; sourceTree = "<group>"; };
3034
53F5591C114F1D5E00A3DA4B /* SDURLCacheTestBundle.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SDURLCacheTestBundle.octest; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -86,6 +90,8 @@
8690
children = (
8791
53F557F2114EA63600A3DA4B /* SDURLCache.h */,
8892
53F557F3114EA63600A3DA4B /* SDURLCache.m */,
93+
5318A9A9155DD7B500AB6767 /* SDCachedURLResponse.h */,
94+
5318A9AA155DD7B500AB6767 /* SDCachedURLResponse.m */,
8995
);
9096
name = Classes;
9197
sourceTree = "<group>";
@@ -116,6 +122,7 @@
116122
files = (
117123
AA747D9F0F9514B9006C5449 /* SDURLCache_Prefix.pch in Headers */,
118124
53F557F4114EA63600A3DA4B /* SDURLCache.h in Headers */,
125+
5318A9AB155DD7B500AB6767 /* SDCachedURLResponse.h in Headers */,
119126
);
120127
runOnlyForDeploymentPostprocessing = 0;
121128
};
@@ -227,6 +234,7 @@
227234
buildActionMask = 2147483647;
228235
files = (
229236
53F557F5114EA63600A3DA4B /* SDURLCache.m in Sources */,
237+
5318A9AC155DD7B500AB6767 /* SDCachedURLResponse.m in Sources */,
230238
);
231239
runOnlyForDeploymentPostprocessing = 0;
232240
};

0 commit comments

Comments
 (0)