├── JWNetAutoCache.podspec ├── JWNetAutoCache ├── JWCacheURLProtocol.h └── JWCacheURLProtocol.m ├── JWNetCache.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── junwen.deng.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── junwen.deng.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── JWNetAutoCache.xcscheme │ ├── JWNetCache.xcscheme │ └── xcschememanagement.plist ├── JWNetCache ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── ViewController.h ├── ViewController.m └── main.m ├── LICENSE └── README.md /JWNetAutoCache.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "JWNetAutoCache" 3 | s.version = "0.0.2" 4 | s.summary = "iOS web资源缓存解决方案、异步后台更新。离线缓存" 5 | 6 | s.description = <<-DESC 7 |  JWNetAutoCache 离线加载,自动缓存已经加载过的资源、对已经加载过的资源进行异步更新。有缓存时加载缓存,并且策略性的去更新加载的资源。达到既能最快显示,又能后台异步更新资源的目的。同时还可以在有缓存的情况下防止白屏 8 | DESC 9 | s.homepage = "https://github.com/dengjunwen/JWNetAutoCache" 10 | s.license = { :type => 'MIT', :file => 'LICENSE' } 11 | s.author = { "dengjunwen" => "dengjunwen1992@163.com" } 12 | s.ios.deployment_target = '6.0' 13 | s.source = { :git => "https://github.com/dengjunwen/JWNetAutoCache.git", :tag => s.version.to_s} 14 | s.source_files = 'JWNetAutoCache/*.{h,m}' 15 | s.requires_arc = true 16 | end 17 | -------------------------------------------------------------------------------- /JWNetAutoCache/JWCacheURLProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWCacheURLProtocol.h 3 | // JWNetCache 4 | // 5 | // Created by junwen.deng on 16/8/19. 6 | // Copyright © 2016年 junwen.deng. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface JWCacheURLProtocol : NSURLProtocol 12 | 13 | 14 | @property (readwrite, nonatomic, strong) NSURLSessionConfiguration *config;//config是全局的,所有的网络请求都用这个config 15 | 16 | @property (readwrite, nonatomic, assign) NSInteger updateInterval;//相同的url地址请求,相隔大于等于updateInterval才会发出后台更新的网络请求,小于的话不发出请求。 17 | 18 | + (void)startListeningNetWorking; 19 | + (void)cancelListeningNetWorking; 20 | 21 | + (void)setConfig:(NSURLSessionConfiguration *)config;//config是全局的,所有的网络请求都用这个config,参见NSURLSession使用的NSURLSessionConfiguration 22 | + (void)setUpdateInterval:(NSInteger)updateInterval;//相同的url地址请求,相隔大于等于updateInterval才会发出后台更新的网络请求,小于的话不发出请求。默认是3600秒,1个小时 23 | + (void)clearUrlDict;//收到内存警告的时候可以调用这个方法清空内存中的url记录 24 | @end 25 | -------------------------------------------------------------------------------- /JWNetAutoCache/JWCacheURLProtocol.m: -------------------------------------------------------------------------------- 1 | // 2 | // JWCacheURLProtocol.m 3 | // JWNetCache 4 | // 5 | // Created by junwen.deng on 16/8/19. 6 | // Copyright © 2016年 junwen.deng. All rights reserved. 7 | // 8 | 9 | 10 | 11 | #import "JWCacheURLProtocol.h" 12 | 13 | @interface JWUrlCacheConfig: NSObject 14 | 15 | @property (readwrite, nonatomic, strong) NSMutableDictionary *urlDict;//记录上一次url请求时间 16 | @property (readwrite, nonatomic, assign) NSInteger updateInterval;//相同的url地址请求,相隔大于等于updateInterval才会发出后台更新的网络请求,小于的话不发出请求。 17 | @property (readwrite, nonatomic, strong) NSURLSessionConfiguration *config;//config是全局的,所有的网络请求都用这个config 18 | @property (readwrite, nonatomic, strong) NSOperationQueue *forgeroundNetQueue; 19 | @property (readwrite, nonatomic, strong) NSOperationQueue *backgroundNetQueue; 20 | 21 | @end 22 | 23 | #define DefaultUpdateInterval 3600 24 | @implementation JWUrlCacheConfig 25 | 26 | 27 | - (NSInteger)updateInterval{ 28 | if (_updateInterval == 0) { 29 | //默认后台更新的时间为3600秒 30 | _updateInterval = DefaultUpdateInterval; 31 | } 32 | return _updateInterval; 33 | } 34 | 35 | - (NSURLSessionConfiguration *)config{ 36 | if (!_config) { 37 | _config = [NSURLSessionConfiguration defaultSessionConfiguration]; 38 | } 39 | return _config; 40 | } 41 | 42 | - (NSMutableDictionary *)urlDict{ 43 | if (!_urlDict) { 44 | _urlDict = [NSMutableDictionary dictionary]; 45 | } 46 | return _urlDict; 47 | } 48 | 49 | - (NSOperationQueue *)forgeroundNetQueue{ 50 | if (!_forgeroundNetQueue) { 51 | _forgeroundNetQueue = [[NSOperationQueue alloc] init]; 52 | _forgeroundNetQueue.maxConcurrentOperationCount = 10; 53 | } 54 | return _forgeroundNetQueue; 55 | } 56 | 57 | - (NSOperationQueue *)backgroundNetQueue{ 58 | if (!_backgroundNetQueue) { 59 | _backgroundNetQueue = [[NSOperationQueue alloc] init]; 60 | _backgroundNetQueue.maxConcurrentOperationCount = 6; 61 | } 62 | return _backgroundNetQueue; 63 | } 64 | 65 | + (instancetype)instance{ 66 | static JWUrlCacheConfig *urlCacheConfig = nil; 67 | static dispatch_once_t onceToken; 68 | dispatch_once(&onceToken, ^{ 69 | urlCacheConfig = [[JWUrlCacheConfig alloc] init]; 70 | }); 71 | return urlCacheConfig; 72 | } 73 | 74 | - (void)clearUrlDict{ 75 | [JWUrlCacheConfig instance].urlDict = nil; 76 | } 77 | 78 | @end 79 | 80 | static NSString * const URLProtocolAlreadyHandleKey = @"alreadyHandle"; 81 | static NSString * const checkUpdateInBgKey = @"checkUpdateInBg"; 82 | 83 | @interface JWCacheURLProtocol() 84 | 85 | @property (readwrite, nonatomic, strong) NSURLSession *session; 86 | @property (readwrite, nonatomic, strong) NSMutableData *data; 87 | @property (readwrite, nonatomic, strong) NSURLResponse *response; 88 | 89 | @end 90 | 91 | @implementation JWCacheURLProtocol 92 | 93 | + (void)startListeningNetWorking{ 94 | [NSURLProtocol registerClass:[JWCacheURLProtocol class]]; 95 | } 96 | 97 | + (void)cancelListeningNetWorking{ 98 | [NSURLProtocol unregisterClass:[JWCacheURLProtocol class]]; 99 | } 100 | 101 | + (void)setConfig:(NSURLSessionConfiguration *)config{ 102 | [[JWUrlCacheConfig instance] setConfig:config]; 103 | } 104 | 105 | + (void)setUpdateInterval:(NSInteger)updateInterval{ 106 | [[JWUrlCacheConfig instance] setUpdateInterval:updateInterval]; 107 | } 108 | 109 | + (void)clearUrlDict{ 110 | [[JWUrlCacheConfig instance] clearUrlDict]; 111 | } 112 | 113 | + (BOOL)canInitWithRequest:(NSURLRequest *)request{ 114 | NSString *urlScheme = [[request URL] scheme]; 115 | if ([urlScheme caseInsensitiveCompare:@"http"] == NSOrderedSame || [urlScheme caseInsensitiveCompare:@"https"] == NSOrderedSame){ 116 | //判断是否标记过使用缓存来处理,或者是否有标记后台更新 117 | if ([NSURLProtocol propertyForKey:URLProtocolAlreadyHandleKey inRequest:request] || [NSURLProtocol propertyForKey:checkUpdateInBgKey inRequest:request]) { 118 | return NO; 119 | } 120 | } 121 | return YES; 122 | } 123 | 124 | + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request{ 125 | return request; 126 | } 127 | 128 | - (void)backgroundCheckUpdate{ 129 | __weak typeof(self) weakSelf = self; 130 | [[[JWUrlCacheConfig instance] backgroundNetQueue] addOperationWithBlock:^{ 131 | NSDate *updateDate = [[JWUrlCacheConfig instance].urlDict objectForKey:weakSelf.request.URL.absoluteString]; 132 | if (updateDate) { 133 | //判读两次相同的url地址发出请求相隔的时间,如果相隔的时间小于给定的时间,不发出请求。否则发出网络请求 134 | NSDate *currentDate = [NSDate date]; 135 | NSInteger interval = [currentDate timeIntervalSinceDate:updateDate]; 136 | if (interval < [JWUrlCacheConfig instance].updateInterval) { 137 | return; 138 | } 139 | } 140 | NSMutableURLRequest *mutableRequest = [[weakSelf request] mutableCopy]; 141 | [NSURLProtocol setProperty:@YES forKey:checkUpdateInBgKey inRequest:mutableRequest]; 142 | [weakSelf netRequestWithRequest:mutableRequest]; 143 | 144 | }]; 145 | } 146 | 147 | - (void)netRequestWithRequest:(NSURLRequest *)request{ 148 | NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; 149 | self.session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[JWUrlCacheConfig instance].forgeroundNetQueue]; 150 | NSURLSessionDataTask * sessionTask = [self.session dataTaskWithRequest:request]; 151 | [[JWUrlCacheConfig instance].urlDict setValue:[NSDate date] forKey:self.request.URL.absoluteString]; 152 | [sessionTask resume]; 153 | } 154 | 155 | 156 | - (void)startLoading{ 157 | NSCachedURLResponse *urlResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:[self request]]; 158 | if (urlResponse) { 159 | //如果缓存存在,则使用缓存。并且开启异步线程去更新缓存 160 | [self.client URLProtocol:self didReceiveResponse:urlResponse.response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; 161 | [self.client URLProtocol:self didLoadData:urlResponse.data]; 162 | [self.client URLProtocolDidFinishLoading:self]; 163 | [self backgroundCheckUpdate]; 164 | return; 165 | } 166 | NSMutableURLRequest *mutableRequest = [[self request] mutableCopy]; 167 | 168 | [NSURLProtocol setProperty:@YES forKey:URLProtocolAlreadyHandleKey inRequest:mutableRequest]; 169 | 170 | [self netRequestWithRequest:mutableRequest]; 171 | } 172 | 173 | - (void)stopLoading{ 174 | [self.session invalidateAndCancel]; 175 | self.session = nil; 176 | } 177 | 178 | - (BOOL)isUseCache{ 179 | //如果有缓存则使用缓存,没有缓存则发出请求 180 | NSCachedURLResponse *urlResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:[self request]]; 181 | if (urlResponse) { 182 | return YES; 183 | } 184 | return NO; 185 | } 186 | 187 | - (void)appendData:(NSData *)newData 188 | { 189 | if ([self data] == nil) { 190 | [self setData:[newData mutableCopy]]; 191 | } 192 | else { 193 | [[self data] appendData:newData]; 194 | } 195 | } 196 | #pragma mark -NSURLSessionDataDelegate 197 | 198 | - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{ 199 | [self.client URLProtocol:self didLoadData:data]; 200 | 201 | [self appendData:data]; 202 | } 203 | 204 | - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler{ 205 | [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; 206 | self.response = response; 207 | completionHandler(NSURLSessionResponseAllow); 208 | } 209 | 210 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{ 211 | if (error) { 212 | [self.client URLProtocol:self didFailWithError:error]; 213 | } else { 214 | [self.client URLProtocolDidFinishLoading:self]; 215 | if (!self.data) { 216 | return; 217 | } 218 | NSCachedURLResponse *cacheUrlResponse = [[NSCachedURLResponse alloc] initWithResponse:task.response data:self.data]; 219 | [[NSURLCache sharedURLCache] storeCachedResponse:cacheUrlResponse forRequest:self.request]; 220 | self.data = nil; 221 | } 222 | } 223 | 224 | 225 | @end 226 | -------------------------------------------------------------------------------- /JWNetCache.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 8CF089D91D66EDB900C8AC2D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF089D81D66EDB900C8AC2D /* main.m */; }; 11 | 8CF089DC1D66EDB900C8AC2D /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF089DB1D66EDB900C8AC2D /* AppDelegate.m */; }; 12 | 8CF089DF1D66EDB900C8AC2D /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF089DE1D66EDB900C8AC2D /* ViewController.m */; }; 13 | 8CF089E21D66EDB900C8AC2D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8CF089E01D66EDB900C8AC2D /* Main.storyboard */; }; 14 | 8CF089E41D66EDB900C8AC2D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8CF089E31D66EDB900C8AC2D /* Assets.xcassets */; }; 15 | 8CF089E71D66EDB900C8AC2D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8CF089E51D66EDB900C8AC2D /* LaunchScreen.storyboard */; }; 16 | 8CF08A061D66F0EE00C8AC2D /* JWCacheURLProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CF08A051D66F0EE00C8AC2D /* JWCacheURLProtocol.m */; }; 17 | 8CF08A071D66F4E100C8AC2D /* libJWNetAutoCache.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8CF089F21D66EF0E00C8AC2D /* libJWNetAutoCache.a */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXCopyFilesBuildPhase section */ 21 | 8CF089F01D66EF0E00C8AC2D /* Copy Files */ = { 22 | isa = PBXCopyFilesBuildPhase; 23 | buildActionMask = 2147483647; 24 | dstPath = "include/$(PRODUCT_NAME)"; 25 | dstSubfolderSpec = 16; 26 | files = ( 27 | ); 28 | name = "Copy Files"; 29 | runOnlyForDeploymentPostprocessing = 0; 30 | }; 31 | /* End PBXCopyFilesBuildPhase section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | 8CF089D41D66EDB900C8AC2D /* JWNetCache.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = JWNetCache.app; sourceTree = BUILT_PRODUCTS_DIR; }; 35 | 8CF089D81D66EDB900C8AC2D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 36 | 8CF089DA1D66EDB900C8AC2D /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 37 | 8CF089DB1D66EDB900C8AC2D /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 38 | 8CF089DD1D66EDB900C8AC2D /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 39 | 8CF089DE1D66EDB900C8AC2D /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 40 | 8CF089E11D66EDB900C8AC2D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 41 | 8CF089E31D66EDB900C8AC2D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 42 | 8CF089E61D66EDB900C8AC2D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 43 | 8CF089E81D66EDB900C8AC2D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 44 | 8CF089F21D66EF0E00C8AC2D /* libJWNetAutoCache.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libJWNetAutoCache.a; sourceTree = BUILT_PRODUCTS_DIR; }; 45 | 8CF08A041D66F0EE00C8AC2D /* JWCacheURLProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JWCacheURLProtocol.h; sourceTree = ""; }; 46 | 8CF08A051D66F0EE00C8AC2D /* JWCacheURLProtocol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JWCacheURLProtocol.m; sourceTree = ""; }; 47 | /* End PBXFileReference section */ 48 | 49 | /* Begin PBXFrameworksBuildPhase section */ 50 | 8CF089D11D66EDB900C8AC2D /* Frameworks */ = { 51 | isa = PBXFrameworksBuildPhase; 52 | buildActionMask = 2147483647; 53 | files = ( 54 | 8CF08A071D66F4E100C8AC2D /* libJWNetAutoCache.a in Frameworks */, 55 | ); 56 | runOnlyForDeploymentPostprocessing = 0; 57 | }; 58 | 8CF089EF1D66EF0E00C8AC2D /* Frameworks */ = { 59 | isa = PBXFrameworksBuildPhase; 60 | buildActionMask = 2147483647; 61 | files = ( 62 | ); 63 | runOnlyForDeploymentPostprocessing = 0; 64 | }; 65 | /* End PBXFrameworksBuildPhase section */ 66 | 67 | /* Begin PBXGroup section */ 68 | 8CF089CB1D66EDB900C8AC2D = { 69 | isa = PBXGroup; 70 | children = ( 71 | 8CF089D61D66EDB900C8AC2D /* JWNetCache */, 72 | 8CF089F31D66EF0E00C8AC2D /* JWNetAutoCache */, 73 | 8CF089D51D66EDB900C8AC2D /* Products */, 74 | ); 75 | sourceTree = ""; 76 | }; 77 | 8CF089D51D66EDB900C8AC2D /* Products */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | 8CF089D41D66EDB900C8AC2D /* JWNetCache.app */, 81 | 8CF089F21D66EF0E00C8AC2D /* libJWNetAutoCache.a */, 82 | ); 83 | name = Products; 84 | sourceTree = ""; 85 | }; 86 | 8CF089D61D66EDB900C8AC2D /* JWNetCache */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 8CF089DA1D66EDB900C8AC2D /* AppDelegate.h */, 90 | 8CF089DB1D66EDB900C8AC2D /* AppDelegate.m */, 91 | 8CF089DD1D66EDB900C8AC2D /* ViewController.h */, 92 | 8CF089DE1D66EDB900C8AC2D /* ViewController.m */, 93 | 8CF089E01D66EDB900C8AC2D /* Main.storyboard */, 94 | 8CF089E31D66EDB900C8AC2D /* Assets.xcassets */, 95 | 8CF089E51D66EDB900C8AC2D /* LaunchScreen.storyboard */, 96 | 8CF089E81D66EDB900C8AC2D /* Info.plist */, 97 | 8CF089D71D66EDB900C8AC2D /* Supporting Files */, 98 | ); 99 | path = JWNetCache; 100 | sourceTree = ""; 101 | }; 102 | 8CF089D71D66EDB900C8AC2D /* Supporting Files */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | 8CF089D81D66EDB900C8AC2D /* main.m */, 106 | ); 107 | name = "Supporting Files"; 108 | sourceTree = ""; 109 | }; 110 | 8CF089F31D66EF0E00C8AC2D /* JWNetAutoCache */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | 8CF08A041D66F0EE00C8AC2D /* JWCacheURLProtocol.h */, 114 | 8CF08A051D66F0EE00C8AC2D /* JWCacheURLProtocol.m */, 115 | ); 116 | path = JWNetAutoCache; 117 | sourceTree = ""; 118 | }; 119 | /* End PBXGroup section */ 120 | 121 | /* Begin PBXNativeTarget section */ 122 | 8CF089D31D66EDB900C8AC2D /* JWNetCache */ = { 123 | isa = PBXNativeTarget; 124 | buildConfigurationList = 8CF089EB1D66EDB900C8AC2D /* Build configuration list for PBXNativeTarget "JWNetCache" */; 125 | buildPhases = ( 126 | 8CF089D01D66EDB900C8AC2D /* Sources */, 127 | 8CF089D11D66EDB900C8AC2D /* Frameworks */, 128 | 8CF089D21D66EDB900C8AC2D /* Resources */, 129 | ); 130 | buildRules = ( 131 | ); 132 | dependencies = ( 133 | ); 134 | name = JWNetCache; 135 | productName = JWNetCache; 136 | productReference = 8CF089D41D66EDB900C8AC2D /* JWNetCache.app */; 137 | productType = "com.apple.product-type.application"; 138 | }; 139 | 8CF089F11D66EF0E00C8AC2D /* JWNetAutoCache */ = { 140 | isa = PBXNativeTarget; 141 | buildConfigurationList = 8CF089F81D66EF0E00C8AC2D /* Build configuration list for PBXNativeTarget "JWNetAutoCache" */; 142 | buildPhases = ( 143 | 8CF089EE1D66EF0E00C8AC2D /* Sources */, 144 | 8CF089EF1D66EF0E00C8AC2D /* Frameworks */, 145 | 8CF089F01D66EF0E00C8AC2D /* Copy Files */, 146 | ); 147 | buildRules = ( 148 | ); 149 | dependencies = ( 150 | ); 151 | name = JWNetAutoCache; 152 | productName = JWNetAutoCache; 153 | productReference = 8CF089F21D66EF0E00C8AC2D /* libJWNetAutoCache.a */; 154 | productType = "com.apple.product-type.library.static"; 155 | }; 156 | /* End PBXNativeTarget section */ 157 | 158 | /* Begin PBXProject section */ 159 | 8CF089CC1D66EDB900C8AC2D /* Project object */ = { 160 | isa = PBXProject; 161 | attributes = { 162 | LastUpgradeCheck = 0720; 163 | ORGANIZATIONNAME = junwen.deng; 164 | TargetAttributes = { 165 | 8CF089D31D66EDB900C8AC2D = { 166 | CreatedOnToolsVersion = 7.2.1; 167 | }; 168 | 8CF089F11D66EF0E00C8AC2D = { 169 | CreatedOnToolsVersion = 7.2.1; 170 | }; 171 | }; 172 | }; 173 | buildConfigurationList = 8CF089CF1D66EDB900C8AC2D /* Build configuration list for PBXProject "JWNetCache" */; 174 | compatibilityVersion = "Xcode 3.2"; 175 | developmentRegion = English; 176 | hasScannedForEncodings = 0; 177 | knownRegions = ( 178 | en, 179 | Base, 180 | ); 181 | mainGroup = 8CF089CB1D66EDB900C8AC2D; 182 | productRefGroup = 8CF089D51D66EDB900C8AC2D /* Products */; 183 | projectDirPath = ""; 184 | projectRoot = ""; 185 | targets = ( 186 | 8CF089D31D66EDB900C8AC2D /* JWNetCache */, 187 | 8CF089F11D66EF0E00C8AC2D /* JWNetAutoCache */, 188 | ); 189 | }; 190 | /* End PBXProject section */ 191 | 192 | /* Begin PBXResourcesBuildPhase section */ 193 | 8CF089D21D66EDB900C8AC2D /* Resources */ = { 194 | isa = PBXResourcesBuildPhase; 195 | buildActionMask = 2147483647; 196 | files = ( 197 | 8CF089E71D66EDB900C8AC2D /* LaunchScreen.storyboard in Resources */, 198 | 8CF089E41D66EDB900C8AC2D /* Assets.xcassets in Resources */, 199 | 8CF089E21D66EDB900C8AC2D /* Main.storyboard in Resources */, 200 | ); 201 | runOnlyForDeploymentPostprocessing = 0; 202 | }; 203 | /* End PBXResourcesBuildPhase section */ 204 | 205 | /* Begin PBXSourcesBuildPhase section */ 206 | 8CF089D01D66EDB900C8AC2D /* Sources */ = { 207 | isa = PBXSourcesBuildPhase; 208 | buildActionMask = 2147483647; 209 | files = ( 210 | 8CF089DF1D66EDB900C8AC2D /* ViewController.m in Sources */, 211 | 8CF089DC1D66EDB900C8AC2D /* AppDelegate.m in Sources */, 212 | 8CF089D91D66EDB900C8AC2D /* main.m in Sources */, 213 | ); 214 | runOnlyForDeploymentPostprocessing = 0; 215 | }; 216 | 8CF089EE1D66EF0E00C8AC2D /* Sources */ = { 217 | isa = PBXSourcesBuildPhase; 218 | buildActionMask = 2147483647; 219 | files = ( 220 | 8CF08A061D66F0EE00C8AC2D /* JWCacheURLProtocol.m in Sources */, 221 | ); 222 | runOnlyForDeploymentPostprocessing = 0; 223 | }; 224 | /* End PBXSourcesBuildPhase section */ 225 | 226 | /* Begin PBXVariantGroup section */ 227 | 8CF089E01D66EDB900C8AC2D /* Main.storyboard */ = { 228 | isa = PBXVariantGroup; 229 | children = ( 230 | 8CF089E11D66EDB900C8AC2D /* Base */, 231 | ); 232 | name = Main.storyboard; 233 | sourceTree = ""; 234 | }; 235 | 8CF089E51D66EDB900C8AC2D /* LaunchScreen.storyboard */ = { 236 | isa = PBXVariantGroup; 237 | children = ( 238 | 8CF089E61D66EDB900C8AC2D /* Base */, 239 | ); 240 | name = LaunchScreen.storyboard; 241 | sourceTree = ""; 242 | }; 243 | /* End PBXVariantGroup section */ 244 | 245 | /* Begin XCBuildConfiguration section */ 246 | 8CF089E91D66EDB900C8AC2D /* Debug */ = { 247 | isa = XCBuildConfiguration; 248 | buildSettings = { 249 | ALWAYS_SEARCH_USER_PATHS = NO; 250 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 251 | CLANG_CXX_LIBRARY = "libc++"; 252 | CLANG_ENABLE_MODULES = YES; 253 | CLANG_ENABLE_OBJC_ARC = YES; 254 | CLANG_WARN_BOOL_CONVERSION = YES; 255 | CLANG_WARN_CONSTANT_CONVERSION = YES; 256 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 257 | CLANG_WARN_EMPTY_BODY = YES; 258 | CLANG_WARN_ENUM_CONVERSION = YES; 259 | CLANG_WARN_INT_CONVERSION = YES; 260 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 261 | CLANG_WARN_UNREACHABLE_CODE = YES; 262 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 263 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 264 | COPY_PHASE_STRIP = NO; 265 | DEBUG_INFORMATION_FORMAT = dwarf; 266 | ENABLE_STRICT_OBJC_MSGSEND = YES; 267 | ENABLE_TESTABILITY = YES; 268 | GCC_C_LANGUAGE_STANDARD = gnu99; 269 | GCC_DYNAMIC_NO_PIC = NO; 270 | GCC_NO_COMMON_BLOCKS = YES; 271 | GCC_OPTIMIZATION_LEVEL = 0; 272 | GCC_PREPROCESSOR_DEFINITIONS = ( 273 | "DEBUG=1", 274 | "$(inherited)", 275 | ); 276 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 277 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 278 | GCC_WARN_UNDECLARED_SELECTOR = YES; 279 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 280 | GCC_WARN_UNUSED_FUNCTION = YES; 281 | GCC_WARN_UNUSED_VARIABLE = YES; 282 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 283 | MTL_ENABLE_DEBUG_INFO = YES; 284 | ONLY_ACTIVE_ARCH = YES; 285 | SDKROOT = iphoneos; 286 | TARGETED_DEVICE_FAMILY = "1,2"; 287 | }; 288 | name = Debug; 289 | }; 290 | 8CF089EA1D66EDB900C8AC2D /* Release */ = { 291 | isa = XCBuildConfiguration; 292 | buildSettings = { 293 | ALWAYS_SEARCH_USER_PATHS = NO; 294 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 295 | CLANG_CXX_LIBRARY = "libc++"; 296 | CLANG_ENABLE_MODULES = YES; 297 | CLANG_ENABLE_OBJC_ARC = YES; 298 | CLANG_WARN_BOOL_CONVERSION = YES; 299 | CLANG_WARN_CONSTANT_CONVERSION = YES; 300 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 301 | CLANG_WARN_EMPTY_BODY = YES; 302 | CLANG_WARN_ENUM_CONVERSION = YES; 303 | CLANG_WARN_INT_CONVERSION = YES; 304 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 305 | CLANG_WARN_UNREACHABLE_CODE = YES; 306 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 307 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 308 | COPY_PHASE_STRIP = NO; 309 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 310 | ENABLE_NS_ASSERTIONS = NO; 311 | ENABLE_STRICT_OBJC_MSGSEND = YES; 312 | GCC_C_LANGUAGE_STANDARD = gnu99; 313 | GCC_NO_COMMON_BLOCKS = YES; 314 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 315 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 316 | GCC_WARN_UNDECLARED_SELECTOR = YES; 317 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 318 | GCC_WARN_UNUSED_FUNCTION = YES; 319 | GCC_WARN_UNUSED_VARIABLE = YES; 320 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 321 | MTL_ENABLE_DEBUG_INFO = NO; 322 | SDKROOT = iphoneos; 323 | TARGETED_DEVICE_FAMILY = "1,2"; 324 | VALIDATE_PRODUCT = YES; 325 | }; 326 | name = Release; 327 | }; 328 | 8CF089EC1D66EDB900C8AC2D /* Debug */ = { 329 | isa = XCBuildConfiguration; 330 | buildSettings = { 331 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 332 | INFOPLIST_FILE = JWNetCache/Info.plist; 333 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 334 | PRODUCT_BUNDLE_IDENTIFIER = JW.JWNetCache; 335 | PRODUCT_NAME = "$(TARGET_NAME)"; 336 | }; 337 | name = Debug; 338 | }; 339 | 8CF089ED1D66EDB900C8AC2D /* Release */ = { 340 | isa = XCBuildConfiguration; 341 | buildSettings = { 342 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 343 | INFOPLIST_FILE = JWNetCache/Info.plist; 344 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 345 | PRODUCT_BUNDLE_IDENTIFIER = JW.JWNetCache; 346 | PRODUCT_NAME = "$(TARGET_NAME)"; 347 | }; 348 | name = Release; 349 | }; 350 | 8CF089F91D66EF0E00C8AC2D /* Debug */ = { 351 | isa = XCBuildConfiguration; 352 | buildSettings = { 353 | OTHER_LDFLAGS = "-ObjC"; 354 | PRODUCT_NAME = "$(TARGET_NAME)"; 355 | SKIP_INSTALL = YES; 356 | }; 357 | name = Debug; 358 | }; 359 | 8CF089FA1D66EF0E00C8AC2D /* Release */ = { 360 | isa = XCBuildConfiguration; 361 | buildSettings = { 362 | OTHER_LDFLAGS = "-ObjC"; 363 | PRODUCT_NAME = "$(TARGET_NAME)"; 364 | SKIP_INSTALL = YES; 365 | }; 366 | name = Release; 367 | }; 368 | /* End XCBuildConfiguration section */ 369 | 370 | /* Begin XCConfigurationList section */ 371 | 8CF089CF1D66EDB900C8AC2D /* Build configuration list for PBXProject "JWNetCache" */ = { 372 | isa = XCConfigurationList; 373 | buildConfigurations = ( 374 | 8CF089E91D66EDB900C8AC2D /* Debug */, 375 | 8CF089EA1D66EDB900C8AC2D /* Release */, 376 | ); 377 | defaultConfigurationIsVisible = 0; 378 | defaultConfigurationName = Release; 379 | }; 380 | 8CF089EB1D66EDB900C8AC2D /* Build configuration list for PBXNativeTarget "JWNetCache" */ = { 381 | isa = XCConfigurationList; 382 | buildConfigurations = ( 383 | 8CF089EC1D66EDB900C8AC2D /* Debug */, 384 | 8CF089ED1D66EDB900C8AC2D /* Release */, 385 | ); 386 | defaultConfigurationIsVisible = 0; 387 | defaultConfigurationName = Release; 388 | }; 389 | 8CF089F81D66EF0E00C8AC2D /* Build configuration list for PBXNativeTarget "JWNetAutoCache" */ = { 390 | isa = XCConfigurationList; 391 | buildConfigurations = ( 392 | 8CF089F91D66EF0E00C8AC2D /* Debug */, 393 | 8CF089FA1D66EF0E00C8AC2D /* Release */, 394 | ); 395 | defaultConfigurationIsVisible = 0; 396 | defaultConfigurationName = Release; 397 | }; 398 | /* End XCConfigurationList section */ 399 | }; 400 | rootObject = 8CF089CC1D66EDB900C8AC2D /* Project object */; 401 | } 402 | -------------------------------------------------------------------------------- /JWNetCache.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /JWNetCache.xcodeproj/project.xcworkspace/xcuserdata/junwen.deng.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengjunwen/JWNetAutoCache/5b44871e60301260df60cd31cc22eb0cd24be855/JWNetCache.xcodeproj/project.xcworkspace/xcuserdata/junwen.deng.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /JWNetCache.xcodeproj/xcuserdata/junwen.deng.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /JWNetCache.xcodeproj/xcuserdata/junwen.deng.xcuserdatad/xcschemes/JWNetAutoCache.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /JWNetCache.xcodeproj/xcuserdata/junwen.deng.xcuserdatad/xcschemes/JWNetCache.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /JWNetCache.xcodeproj/xcuserdata/junwen.deng.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | JWNetAutoCache.xcscheme 8 | 9 | orderHint 10 | 1 11 | 12 | JWNetCache.xcscheme 13 | 14 | orderHint 15 | 0 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | 8CF089D31D66EDB900C8AC2D 21 | 22 | primary 23 | 24 | 25 | 8CF089F11D66EF0E00C8AC2D 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /JWNetCache/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // JWNetCache 4 | // 5 | // Created by junwen.deng on 16/8/19. 6 | // Copyright © 2016年 junwen.deng. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /JWNetCache/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // JWNetCache 4 | // 5 | // Created by junwen.deng on 16/8/19. 6 | // Copyright © 2016年 junwen.deng. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /JWNetCache/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /JWNetCache/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /JWNetCache/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /JWNetCache/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | NSAppTransportSecurity 8 | 9 | NSAllowsArbitraryLoads 10 | 11 | 12 | CFBundleExecutable 13 | $(EXECUTABLE_NAME) 14 | CFBundleIdentifier 15 | $(PRODUCT_BUNDLE_IDENTIFIER) 16 | CFBundleInfoDictionaryVersion 17 | 6.0 18 | CFBundleName 19 | $(PRODUCT_NAME) 20 | CFBundlePackageType 21 | APPL 22 | CFBundleShortVersionString 23 | 1.0 24 | CFBundleSignature 25 | ???? 26 | CFBundleVersion 27 | 1 28 | LSRequiresIPhoneOS 29 | 30 | UILaunchStoryboardName 31 | LaunchScreen 32 | UIMainStoryboardFile 33 | Main 34 | UIRequiredDeviceCapabilities 35 | 36 | armv7 37 | 38 | UISupportedInterfaceOrientations 39 | 40 | UIInterfaceOrientationPortrait 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UISupportedInterfaceOrientations~ipad 45 | 46 | UIInterfaceOrientationPortrait 47 | UIInterfaceOrientationPortraitUpsideDown 48 | UIInterfaceOrientationLandscapeLeft 49 | UIInterfaceOrientationLandscapeRight 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /JWNetCache/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // JWNetCache 4 | // 5 | // Created by junwen.deng on 16/8/19. 6 | // Copyright © 2016年 junwen.deng. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /JWNetCache/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // JWNetCache 4 | // 5 | // Created by junwen.deng on 16/8/19. 6 | // Copyright © 2016年 junwen.deng. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "JWCacheURLProtocol.h" 11 | 12 | @interface ViewController () 13 | 14 | @end 15 | 16 | @implementation ViewController 17 | 18 | - (void)viewDidLoad { 19 | [super viewDidLoad]; 20 | //使用方法,在开启webview的时候开启监听,,销毁weibview的时候取消监听,否则监听还在继续。将会监听所有的网络请求 21 | [JWCacheURLProtocol startListeningNetWorking]; 22 | UIWebView *webview = [[UIWebView alloc] initWithFrame:self.view.bounds]; 23 | [self.view addSubview:webview]; 24 | NSURL *URL = [NSURL URLWithString:@"https://m.jd.com"]; 25 | NSURLRequest *request = [[NSURLRequest alloc] initWithURL:URL]; 26 | [webview loadRequest:request]; 27 | 28 | NSLog(@"cache directory---%@", NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]); 29 | } 30 | 31 | - (void)dealloc{ 32 | [JWCacheURLProtocol cancelListeningNetWorking];//在不需要用到webview的时候即使的取消监听 33 | } 34 | 35 | - (void)didReceiveMemoryWarning { 36 | [super didReceiveMemoryWarning]; 37 | // Dispose of any resources that can be recreated. 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /JWNetCache/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // JWNetCache 4 | // 5 | // Created by junwen.deng on 16/8/19. 6 | // Copyright © 2016年 junwen.deng. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2009-2016 Matej Bukovinski 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UIWebview使用缓存并且保证实时性`此处输入代码` 2 | 3 | 标签(空格分隔): iOS 缓存 UIWebview 实时更新 4 | 5 | --- 6 | ## webview缓存策略的介绍 ## 7 | 使用webview加载页面的时候,最理想的情况是: 资源文件没有更新,就只加载缓存文件。如果有更新,则第一时间使用新的文件。 8 | 9 | **UIWebview中提供的缓存策略** 10 | 11 | - NSURLRequestUseProtocolCachePolicy 缓存策略定义在 web 12 | 协议实现中,用于请求特定的URL。是默认的URL缓存策略。 13 | - NSURLRequestReloadIgnoringLocalCacheData 从服务端获取数据,忽略本地缓存 14 | - NSURLRequestReloadIgnoringLocalAndRemoteCacheData //源文件注释中写到没有实现 15 | - NSURLRequestReloadIgnoringCacheData 16 | 被NSURLRequestReloadIgnoringLocalCacheData替换了 17 | - NSURLRequestReturnCacheDataElseLoad 18 | 已经存在的缓存数据用于请求返回,不管它的过期日期和已经存在了多久。如果没有请求对应的缓存数据,从数据源读取 19 | - NSURLRequestReturnCacheDataDontLoad 20 | 已经存在的缓存数据用于请求返回,不管它的过期日期和已经存在了多久。如果没有请求对应的缓存数据,不要去数据源读取,该请求被设置为失败,这种情况多用于离线模式 21 | - NSURLRequestReloadRevalidatingCacheData //源文件中写到没有实现 22 | 23 | 24 | > 其中我觉得最接近理想状态的就是默认的缓存策略了-NSURLRequestUseProtocolCachePolicy。 这个缓存策略的缓存模式,经过探究,如下图所示: 25 | ![默认缓存策略的流程图][1] 26 | 我们会遇到两个问题: 27 |     1.在“是否过期”这个判断的地方,倘若后端开发人员没有设置过期时间,那么将会导致立马过期,即使有缓存的情况下,都会无法加载资源。尽管服务器资源没有更新。 28 |     2.当网络差的时候,缓存已经过期,则向服务器发出询问是否有更新,通常返回304的状态码的情况比较多,返回304则又会使用缓存。而这种情况在网络差的时候,发出请求这个部分时间非常的耗时。也就容易造成白屏了。 29 | 30 | **application cache** 31 | 32 | > 这个是h5中用到的一个缓存方式。使用manifest配置文件,告知客户端哪些需要更新,哪些不需要更新。使用这种方式,需要枚举出所有需要缓存的配置文件,每次打开页面都要去请求配置文件是否有更新,如果配置文件有更新,则更新配置文件中列出的资源文件。 33 | > 缺点:多个页面引用同一个js,将会缓存两份js文件。需要服务端配合 34 | > 优点:快速的展现缓存的内容,后台去更新缓存,下次再打开该资源时将使用最新的。 35 | 36 | ## 自定义一种较为优的缓存策略 ## 37 | 38 | > 既可以快速的加载缓存的内容(可以离线加载),又可以实时的更新。 39 | 40 | 跟application cache的方式差不多,快速加载缓存,后台去异步更新,但是没有它的缺点。不会缓存多份文件,不需要服务端配合,意味着不需要改变服务端的东西。客户端开发人员就可以搞定。成本很小。快速又节约流量。 41 | ![自定义cache策略图][2] 42 | 43 | > 如上图所示,发出网络请求后,会优先加载本地的缓存内容。使得资源得以实施的加载,杜绝白屏现象。然后异步去更新缓存。 44 | 这里的缓存策略依赖于自己的设定,使用webview自带的缓存策略即可。然后根据 `当前时间 - 上一次更新时间 > 设定更新间隔时间按` 这个条件来选择是否更新。 45 |     这样做的好处是防止白屏,又能实时更新。 46 | 47 | 48 | 附上github demo的地址:[demo地址][3] 49 | 使用方法: 50 | 51 | pod 'JWNetAutoCache' 52 | 53 | 54 | 或者直接下载文件,将JWNetAutoCache目录下的两个文件引入。 55 | 在需要开启的时候调用 56 | 57 | `[JWCacheURLProtocol startListeningNetWorking];` 58 | 59 | 使用结束后调用 60 | 61 | `[JWCacheURLProtocol cancelListeningNetWorking];` 62 | 63 | 64 | > 代码侵入性小。可以随时的移除。缓存还是使用原来系统的方式来进行存储。移除后无感知。 65 | 66 | 67 | 68 | [1]: http://7xrj9d.com1.z0.glb.clouddn.com/cache%E5%9B%BE.png 69 | [2]: http://7xrj9d.com1.z0.glb.clouddn.com/%E8%87%AA%E5%AE%9A%E4%B9%89cache%E7%AD%96%E7%95%A5.png 70 | [3]: https://github.com/dengjunwen/JWNetAutoCache --------------------------------------------------------------------------------