├── .gitattributes
├── Images
├── model.png
└── ShortMediaCache.png
├── ShortMediaCacheDemo
├── Assets.xcassets
│ ├── Contents.json
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── ViewController.h
├── AppDelegate.h
├── main.m
├── PlayerCell.h
├── Info.plist
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── AppDelegate.m
├── PlayerCell.m
└── ViewController.m
├── ShortMediaCacheDemo.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ └── dangercheng.xcuserdatad
│ │ └── IDEFindNavigatorScopes.plist
└── project.pbxproj
├── ShortMediaCache
├── AVAssetResourceLoadingDataRequest+ShortMediaCache.h
├── ShortMediaWeakProxy.h
├── ShortMediaCacheConfig.h
├── ShortMediaCacheConfig.m
├── ShortMediaResourceLoader.h
├── AVAssetResourceLoadingDataRequest+ShortMediaCache.m
├── ShortMediaDownloadOperation.h
├── ShortMediaManager.h
├── ShortMediaCache.h
├── ShortMediaDiskCache.h
├── ShortMediaDownloader.h
├── ShortMediaWeakProxy.m
├── ShortMediaManager.m
├── ShortMediaCache.m
├── ShortMediaResourceLoader.m
├── ShortMediaDownloader.m
├── ShortMediaDownloadOperation.m
└── ShortMediaDiskCache.m
├── ShortMediaCache.podspec
├── LICENSE
└── README.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/Images/model.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dangercheng/ShortMediaCache/HEAD/Images/model.png
--------------------------------------------------------------------------------
/Images/ShortMediaCache.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dangercheng/ShortMediaCache/HEAD/Images/ShortMediaCache.png
--------------------------------------------------------------------------------
/ShortMediaCacheDemo/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/ShortMediaCacheDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ShortMediaCacheDemo.xcodeproj/project.xcworkspace/xcuserdata/dangercheng.xcuserdatad/IDEFindNavigatorScopes.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/ShortMediaCacheDemo/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // ShortMediaCacheDemo
4 | //
5 | // Created by dangercheng on 2018/7/30.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ViewController : UIViewController
12 |
13 |
14 | @end
15 |
16 |
--------------------------------------------------------------------------------
/ShortMediaCacheDemo/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // ShortMediaCacheDemo
4 | //
5 | // Created by dangercheng on 2018/7/30.
6 | // Copyright © 2018年 DandJ. 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 |
--------------------------------------------------------------------------------
/ShortMediaCacheDemo/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // ShortMediaCacheDemo
4 | //
5 | // Created by dangercheng on 2018/7/30.
6 | // Copyright © 2018年 DandJ. 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 |
--------------------------------------------------------------------------------
/ShortMediaCache/AVAssetResourceLoadingDataRequest+ShortMediaCache.h:
--------------------------------------------------------------------------------
1 | //
2 | // AVAssetResourceLoadingDataRequest+ShortMediaCache.h
3 | // ShortMediaCacheDemo
4 | //
5 | // Created by dangercheng on 2018/8/9.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AVAssetResourceLoadingDataRequest (ShortMediaCache)
12 | @property (nonatomic, assign) NSInteger respondedSize;
13 | @end
14 |
--------------------------------------------------------------------------------
/ShortMediaCacheDemo/PlayerCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // PlayerCell.h
3 | // ShortMediaCacheDemo
4 | //
5 | // Created by dangercheng on 2018/8/9.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface PlayerCell : UICollectionViewCell
12 | @property (weak, nonatomic) IBOutlet UIView *playerView;
13 |
14 | - (void)stopPlayWithUrl:(NSURL *)videoUrl;
15 |
16 | - (void)playVideoWithUrl:(NSURL *)videoUrl;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/ShortMediaCache/ShortMediaWeakProxy.h:
--------------------------------------------------------------------------------
1 | //
2 | // ShortMediaWeakProxy.h
3 | // ShortMediaCacheDemo
4 | //
5 | // Created by dangercheng on 2018/8/7.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface ShortMediaWeakProxy : NSProxy
14 |
15 | @property (nullable, readonly, nonatomic, weak) id target;
16 |
17 | + (instancetype)proxyWithtTarget:(id)target;
18 |
19 | - (instancetype)initWithTarget:(id)target;
20 |
21 | @end
22 |
23 | NS_ASSUME_NONNULL_END
24 |
--------------------------------------------------------------------------------
/ShortMediaCache.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = "ShortMediaCache"
3 | s.version = "0.1.0"
4 | s.summary = "A cache for short video while playing."
5 | s.homepage = "https://github.com/dangercheng/ShortMediaCache"
6 | s.license = "MIT"
7 | s.author = { "DandJ" => "877478415@qq.com" }
8 | s.source = { :git => "https://github.com/dangercheng/ShortMediaCache.git", :tag => "#{s.version}" }
9 | s.requires_arc = true
10 | s.ios.deployment_target = "8.0"
11 | s.source_files = "ShortMediaCache/*.{h,m}"
12 | end
--------------------------------------------------------------------------------
/ShortMediaCache/ShortMediaCacheConfig.h:
--------------------------------------------------------------------------------
1 | //
2 | // ShortMediaCacheConfig.h
3 | // ShortMediaCacheDemo
4 | //
5 | // Created by dangercheng on 2018/8/16.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ShortMediaCacheConfig : NSObject
12 | @property (nonatomic, assign) NSInteger maxTempCacheSize;
13 | @property (nonatomic, assign) NSInteger maxFinalCacheSize;
14 | @property (nonatomic, assign) NSInteger maxTempCacheTimeInterval;
15 | @property (nonatomic, assign) NSInteger maxFinalCacheTimeInterval;
16 |
17 | + (instancetype)defaultConfig;
18 | @end
19 |
--------------------------------------------------------------------------------
/ShortMediaCache/ShortMediaCacheConfig.m:
--------------------------------------------------------------------------------
1 | //
2 | // ShortMediaCacheConfig.m
3 | // ShortMediaCacheDemo
4 | //
5 | // Created by dangercheng on 2018/8/16.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import "ShortMediaCacheConfig.h"
10 |
11 | @implementation ShortMediaCacheConfig
12 |
13 | + (instancetype)defaultConfig {
14 | ShortMediaCacheConfig *config = [ShortMediaCacheConfig new];
15 | config.maxTempCacheSize = 1024 * 1024 * 100;//100MB
16 | config.maxFinalCacheSize = 1024 * 1024 * 200;//200MB
17 | config.maxTempCacheTimeInterval = 1 * 24 * 60 * 60;//1days
18 | config.maxFinalCacheTimeInterval = 1 * 24 * 60 * 60;//2days
19 | return config;
20 | }
21 |
22 | @end
23 |
--------------------------------------------------------------------------------
/ShortMediaCache/ShortMediaResourceLoader.h:
--------------------------------------------------------------------------------
1 | //
2 | // ShortMediaResourceLoader.h
3 | // ShortMediaCacheDemo
4 | //
5 | // Created by dangercheng on 2018/7/30.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #import "ShortMediaManager.h"
12 |
13 | @protocol ShortMediaResourceLoaderDelegate
14 | @end
15 |
16 | @interface ShortMediaResourceLoader : NSObject
17 |
18 | - (instancetype)initWithDelegate:(id)delegate;
19 |
20 | @property (nonatomic, strong, readonly) NSURL *url;
21 |
22 | @property (nonatomic, weak) id delegate;
23 |
24 | - (AVPlayerItem *)playItemWithUrl:(NSURL *)url;
25 |
26 | - (AVPlayerItem *)playItemWithUrl:(NSURL *)url options:(ShortMediaOptions)options;
27 |
28 | - (void)endLoading;
29 |
30 | @end
31 |
--------------------------------------------------------------------------------
/ShortMediaCache/AVAssetResourceLoadingDataRequest+ShortMediaCache.m:
--------------------------------------------------------------------------------
1 | //
2 | // AVAssetResourceLoadingDataRequest+ShortMediaCache.m
3 | // ShortMediaCacheDemo
4 | //
5 | // Created by dangercheng on 2018/8/9.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import "AVAssetResourceLoadingDataRequest+ShortMediaCache.h"
10 | #import
11 |
12 | static const NSString *RespondedSizeKey = @"RespondedSizeKey";
13 |
14 | @implementation AVAssetResourceLoadingDataRequest (ShortMediaCache)
15 |
16 | - (void)setRespondedSize:(NSInteger)respondedSize {
17 | objc_setAssociatedObject(self, &RespondedSizeKey, [NSNumber numberWithInteger:respondedSize], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
18 | }
19 |
20 | - (NSInteger)respondedSize {
21 | NSNumber *value = objc_getAssociatedObject(self, &RespondedSizeKey);
22 | return [value integerValue];
23 | }
24 |
25 |
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/ShortMediaCache/ShortMediaDownloadOperation.h:
--------------------------------------------------------------------------------
1 | //
2 | // ShortMediaDownloadOperation.h
3 | // ShortMediaCacheDemo
4 | //
5 | // Created by dangercheng on 2018/7/30.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ShortMediaCache.h"
11 | #import "ShortMediaDownloader.h"
12 |
13 | @interface ShortMediaDownloadOperation : NSOperation
14 |
15 | @property (nonatomic, assign) ShortMediaOptions options;
16 |
17 | @property (nonatomic, strong) NSURLCredential *credential;
18 |
19 | @property (nonatomic, assign) BOOL isPreloading;
20 |
21 | - (instancetype)initWithRequest:(NSURLRequest *)request
22 | session:(NSURLSession *)session
23 | options:(ShortMediaOptions)options
24 | progress:(ShortMediaProgressBlock)progress
25 | completion:(ShortMediaCompletionBlock)completion;
26 |
27 | - (void)cancelOperation;
28 | @end
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 DandJ
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/ShortMediaCache/ShortMediaManager.h:
--------------------------------------------------------------------------------
1 | //
2 | // ShortMediaManager.h
3 | // ShortMediaCacheDemo
4 | //
5 | // Created by dangercheng on 2018/7/31.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ShortMediaDownloader.h"
11 | #import "ShortMediaCacheConfig.h"
12 |
13 | @interface ShortMediaManager : NSObject
14 |
15 | @property (nonatomic, strong) ShortMediaCacheConfig *cacheConfig;
16 |
17 | @property (nonatomic, strong, readonly) NSArray *prloadingMediaUrls;
18 |
19 | + (instancetype)shareManager;
20 |
21 | - (void)loadMediaWithUrl:(NSURL *)url
22 | options:(ShortMediaOptions)options
23 | progress:(ShortMediaProgressBlock)progress
24 | completion:(ShortMediaCompletionBlock)completion;
25 |
26 | - (void)endLoadMediaWithUrl:(NSURL *)url;
27 |
28 | - (NSData *)cacheDataFromOffset:(NSUInteger)offset
29 | length:(NSUInteger)length
30 | withUrl:(NSURL *)url;
31 |
32 | - (NSInteger)totalCachedSize;
33 |
34 | - (NSString *)totalCachedSizeStr;
35 |
36 | - (void)cleanCache;
37 |
38 | - (void)resetPreloadingWithMediaUrls:(NSArray *)mediaUrls;
39 |
40 | @end
41 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ShortMediaCache
2 |
3 | A Cache Library based on AVPLayer for short video on ios, you can creat AVPlayerItem with it directly.
4 |
5 | [中文介绍](https://segmentfault.com/a/1190000016228456)
6 |
7 | ## Main features
8 | - **1.Designed for short video, easy to access, does not encroach on business**
9 | - **2.cache video while playing, Play directly after caching**
10 | - **3.Support preloading, Play the next video in one second**
11 | - **4.Automatic cache management**
12 |
13 | ## Installation
14 |
15 | **cocoapods**
16 |
17 | ```
18 | pod 'ShortMediaCache'
19 | ```
20 |
21 | ## Usage
22 |
23 | **Normal**
24 |
25 | ```
26 | #import "ShortMediaResourceLoader.h"
27 | ```
28 |
29 | ```
30 | ShortMediaResourceLoader _resourceLoader = [ShortMediaResourceLoader new];
31 | AVPlayerItem _playerItem = [_resourceLoader playItemWithUrl:videoUrl];
32 | AVPlayer _player = [AVPlayer playerWithPlayerItem:_playerItem];
33 | ```
34 |
35 | tips: should hold the _resourceLoader object.
36 |
37 | **Preloading**
38 |
39 | ```
40 | [[ShortMediaManager shareManager] resetPreloadingWithMediaUrls:preloadUrls];
41 | ```
42 |
43 | tips:'preloadUrls' is an array with video urls
44 |
45 | **More detail**
46 |
47 | The source code
48 |
49 | **License**
50 |
51 | MIT
52 |
--------------------------------------------------------------------------------
/ShortMediaCache/ShortMediaCache.h:
--------------------------------------------------------------------------------
1 | //
2 | // ShortMediaCache.h
3 | // ShortMediaCacheDemo
4 | //
5 | // Created by dangercheng on 2018/7/30.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ShortMediaDiskCache.h"
11 |
12 | @class ShortMediaCacheConfig;
13 |
14 | typedef void(^CompletionBlock)(void);
15 |
16 | @interface ShortMediaCache : NSObject
17 |
18 | @property (nonatomic, strong) ShortMediaDiskCache *diskCache;
19 |
20 | + (instancetype)shareCache;
21 |
22 | - (NSString *)createCacheFileWithUrl:(NSURL *)url;
23 |
24 | - (void)appendWithData:(NSData *)data url:(NSURL *)url;
25 |
26 | - (NSData *)cacheDataFromOffset:(NSUInteger)offset
27 | length:(NSUInteger)length
28 | withUrl:(NSURL *)url;
29 |
30 | - (void)cacheCompletedWithUrl:(NSURL *)url;
31 |
32 | - (BOOL)isCacheCompletedWithUrl:(NSURL *)url;
33 |
34 | - (NSInteger)cachedSizeWithUrl:(NSURL *)url;
35 |
36 | - (NSInteger)finalCachedSizeWithUrl:(NSURL *)url;
37 |
38 | - (NSInteger)totalCachedSize;
39 |
40 | - (void)cleanCache;
41 |
42 | - (void)resetCacheWithConfig:(ShortMediaCacheConfig *)cacheConfig completion:(CompletionBlock)completion;
43 |
44 | - (void)resetFinalCacheWithConfig:(ShortMediaCacheConfig *)cacheConfig completion:(CompletionBlock)completion;
45 |
46 | @end
47 |
--------------------------------------------------------------------------------
/ShortMediaCache/ShortMediaDiskCache.h:
--------------------------------------------------------------------------------
1 | //
2 | // ShortMediaDiskCache.h
3 | // ShortMediaCacheDemo
4 | //
5 | // Created by dangercheng on 2018/7/31.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @class ShortMediaCacheConfig;
12 |
13 | @interface ShortMediaDiskCache : NSObject
14 |
15 | @property (nonatomic, copy) NSString *path;
16 |
17 | - (instancetype)initWithPath:(NSString *)path;
18 |
19 | - (void)appendData:(NSData *)data tempFileWithName:(NSString *)fileName;
20 |
21 | - (void)moveTempFileToFinalWithName:(NSString *)fileName;
22 |
23 | - (BOOL)fileExistAtFinalWithName:(NSString *)fileName;
24 |
25 | - (NSData *)dataFromOffset:(NSUInteger)offset
26 | length:(NSUInteger)length
27 | withName:(NSString *)fileName;
28 |
29 | - (NSString *)createTempFileWithName:(NSString *)fileName;
30 |
31 | - (NSInteger)tempCachedSizehWithName:(NSString *)fileName;
32 |
33 | - (NSInteger)finalCachedSizeWithName:(NSString *)fileName;
34 |
35 | - (NSInteger)totalCachedSize;
36 |
37 | - (void)moveAllCacheToTrash;
38 |
39 | - (void)cleanTrash;
40 |
41 | - (void)resetCacheWithConfig:(ShortMediaCacheConfig *)config;
42 |
43 | - (void)resetTempCacheWitchConfig:(ShortMediaCacheConfig *)config;
44 |
45 | - (void)resetFinalCacheWitchConfig:(ShortMediaCacheConfig *)config;
46 |
47 | @end
48 |
--------------------------------------------------------------------------------
/ShortMediaCache/ShortMediaDownloader.h:
--------------------------------------------------------------------------------
1 | //
2 | // ShortMediaDownloader.h
3 | // ShortMediaCacheDemo
4 | //
5 | // Created by dangercheng on 2018/8/7.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ShortMediaCache.h"
11 |
12 | typedef NS_OPTIONS(NSUInteger, ShortMediaOptions) {
13 | ShortMediaOptionsHandleCookies = 1 << 0,
14 | ShortMediaOptionsOptionAllowInvalidSSLCertificates = 1 << 1,
15 | };
16 |
17 | typedef void(^ShortMediaProgressBlock)(NSInteger receivedSize, NSInteger expectedSize);
18 | typedef void(^ShortMediaCompletionBlock)(NSError *error);
19 |
20 | @interface ShortMediaDownloader : NSObject
21 |
22 | @property (nonatomic, copy) NSString *userName;
23 | @property (nonatomic, copy) NSString *password;
24 | @property (nonatomic, assign, readonly) BOOL canPreload;
25 |
26 | + (instancetype)shareDownloader;
27 |
28 | - (void)downloadMediaWithUrl:(NSURL *)url
29 | options:(ShortMediaOptions)options
30 | progress:(ShortMediaProgressBlock)progress
31 | completion:(ShortMediaCompletionBlock)completion;
32 |
33 | - (void)preloadMediaWithUrl:(NSURL *)url
34 | options:(ShortMediaOptions)options
35 | progress:(ShortMediaProgressBlock)progress
36 | completion:(ShortMediaCompletionBlock)completion;
37 |
38 | - (void)cancelDownloadWithUrl:(NSURL *)url;
39 |
40 | @end
41 |
--------------------------------------------------------------------------------
/ShortMediaCacheDemo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSAppTransportSecurity
6 |
7 | NSAllowsArbitraryLoads
8 |
9 |
10 | CFBundleDevelopmentRegion
11 | $(DEVELOPMENT_LANGUAGE)
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 | CFBundleVersion
25 | 1
26 | LSRequiresIPhoneOS
27 |
28 | UILaunchStoryboardName
29 | LaunchScreen
30 | UIMainStoryboardFile
31 | Main
32 | UIRequiredDeviceCapabilities
33 |
34 | armv7
35 |
36 | UISupportedInterfaceOrientations
37 |
38 | UIInterfaceOrientationPortrait
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UISupportedInterfaceOrientations~ipad
43 |
44 | UIInterfaceOrientationPortrait
45 | UIInterfaceOrientationPortraitUpsideDown
46 | UIInterfaceOrientationLandscapeLeft
47 | UIInterfaceOrientationLandscapeRight
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/ShortMediaCache/ShortMediaWeakProxy.m:
--------------------------------------------------------------------------------
1 | //
2 | // ShortMediaWeakProxy.m
3 | // ShortMediaCacheDemo
4 | //
5 | // Created by dangercheng on 2018/8/7.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import "ShortMediaWeakProxy.h"
10 | #import
11 |
12 | @implementation ShortMediaWeakProxy
13 |
14 | + (instancetype)proxyWithtTarget:(id)target {
15 | return [[ShortMediaWeakProxy alloc] initWithTarget:target];
16 | }
17 |
18 | - (instancetype)initWithTarget:(id)target {
19 | _target = target;
20 | return self;
21 | }
22 |
23 | #pragma mark - runtime
24 | - (id)forwardingTargetForSelector:(SEL)aSelector {
25 | return _target;
26 | }
27 |
28 | - (void)forwardInvocation:(NSInvocation *)invocation {
29 | void *null = NULL;
30 | [invocation setReturnValue:&null];
31 | }
32 |
33 | - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
34 | return [NSObject instanceMethodSignatureForSelector:@selector(init)];
35 | }
36 |
37 | #pragma mark - override
38 | - (BOOL)respondsToSelector:(SEL)aSelector {
39 | return [_target respondsToSelector:aSelector];
40 | }
41 |
42 | - (BOOL)isEqual:(id)object {
43 | return [_target isEqual:object];
44 | }
45 |
46 | - (NSUInteger)hash {
47 | return [_target hash];
48 | }
49 |
50 | - (Class)class {
51 | return [_target class];
52 | }
53 |
54 | - (Class)superclass {
55 | return [_target superclass];
56 | }
57 |
58 | - (BOOL)isKindOfClass:(Class)aClass {
59 | return [_target isKindOfClass:aClass];
60 | }
61 |
62 | - (BOOL)isMemberOfClass:(Class)aClass {
63 | return [_target isMemberOfClass:aClass];
64 | }
65 |
66 | - (BOOL)conformsToProtocol:(Protocol *)aProtocol {
67 | return [_target conformsToProtocol:aProtocol];
68 | }
69 |
70 | - (BOOL)isProxy {
71 | return YES;
72 | }
73 |
74 | - (NSString *)description {
75 | return [_target description];
76 | }
77 |
78 | - (NSString *)debugDescription {
79 | return [_target debugDescription];
80 | }
81 |
82 | @end
83 |
--------------------------------------------------------------------------------
/ShortMediaCacheDemo/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 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/ShortMediaCacheDemo/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/ShortMediaCacheDemo/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // ShortMediaCacheDemo
4 | //
5 | // Created by dangercheng on 2018/7/30.
6 | // Copyright © 2018年 DandJ. 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 |
24 | - (void)applicationWillResignActive:(UIApplication *)application {
25 | // 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.
26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
27 | }
28 |
29 |
30 | - (void)applicationDidEnterBackground:(UIApplication *)application {
31 | // 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.
32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
33 | }
34 |
35 |
36 | - (void)applicationWillEnterForeground:(UIApplication *)application {
37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
38 | }
39 |
40 |
41 | - (void)applicationDidBecomeActive:(UIApplication *)application {
42 | // 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.
43 | }
44 |
45 |
46 | - (void)applicationWillTerminate:(UIApplication *)application {
47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
48 | }
49 |
50 |
51 | @end
52 |
--------------------------------------------------------------------------------
/ShortMediaCacheDemo/PlayerCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // PlayerCell.m
3 | // ShortMediaCacheDemo
4 | //
5 | // Created by dangercheng on 2018/8/9.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import "PlayerCell.h"
10 | #import "ShortMediaResourceLoader.h"
11 |
12 | #import
13 |
14 | @implementation PlayerCell {
15 | AVPlayer *_player;
16 | AVPlayerItem *_playerItem;
17 | AVPlayerLayer *_playerLayer;
18 | ShortMediaResourceLoader *_resourceLoader;
19 | }
20 |
21 | - (void)awakeFromNib {
22 | [super awakeFromNib];
23 | self.playerView.backgroundColor = [UIColor colorWithRed:arc4random() % 100 / 256.0 green:arc4random() % 100 / 256.0 blue:arc4random() % 100 / 256.0 alpha:1.0];
24 | }
25 |
26 | - (void)playVideoWithUrl:(NSURL *)videoUrl {
27 | if(!videoUrl) {
28 | return;
29 | }
30 | if(_player) {
31 | return;
32 | }
33 | _resourceLoader = [ShortMediaResourceLoader new];
34 | _playerItem = [_resourceLoader playItemWithUrl:videoUrl];
35 |
36 | _player = [AVPlayer playerWithPlayerItem:_playerItem];
37 |
38 | _playerLayer = [AVPlayerLayer playerLayerWithPlayer:_player];
39 | _playerLayer.frame = _playerView.bounds;
40 | [self.playerView.layer addSublayer:_playerLayer];
41 | [_player play];
42 |
43 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:_player.currentItem];
44 | }
45 |
46 | -(void)playbackFinished:(NSNotification *)notification{
47 | NSLog(@"Complete.");
48 | [_player seekToTime:CMTimeMake(0, 1)];
49 | [_player play];
50 | }
51 |
52 | - (void)dealloc {
53 | [[NSNotificationCenter defaultCenter] removeObserver:self];
54 | [self stopPlayWithUrl:_resourceLoader.url];
55 | }
56 |
57 | - (void)stopPlayWithUrl:(NSURL *)videoUrl {
58 | if(![videoUrl isEqual:_resourceLoader.url]) {
59 | return;
60 | }
61 | [[NSNotificationCenter defaultCenter] removeObserver:self];
62 | if(_player) {
63 | [_player pause];
64 | [_resourceLoader endLoading];
65 | AVURLAsset *asset = (AVURLAsset *)_playerItem.asset;
66 | [asset.resourceLoader setDelegate:nil queue:dispatch_get_main_queue()];
67 | [_playerLayer removeFromSuperlayer];
68 | _resourceLoader = nil;
69 | _playerItem = nil;
70 | _player = nil;
71 | _playerLayer = nil;
72 | }
73 | }
74 |
75 | @end
76 |
--------------------------------------------------------------------------------
/ShortMediaCache/ShortMediaManager.m:
--------------------------------------------------------------------------------
1 | //
2 | // ShortMediaManager.m
3 | // ShortMediaCacheDemo
4 | //
5 | // Created by dangercheng on 2018/7/31.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import "ShortMediaManager.h"
10 | #import
11 |
12 | #define Lock() dispatch_semaphore_wait(self.lock, DISPATCH_TIME_FOREVER)
13 | #define UnLock() dispatch_semaphore_signal(self.lock)
14 |
15 | @interface ShortMediaManager()
16 | @property (nonatomic, strong) NSMutableArray *waitingPreloadingUrls;
17 | @property (nonatomic, strong) dispatch_semaphore_t lock;
18 | @end
19 |
20 | @implementation ShortMediaManager
21 | + (instancetype)shareManager {
22 | static ShortMediaManager *manager;
23 | static dispatch_once_t onceToken;
24 | dispatch_once(&onceToken, ^{
25 | manager = [[ShortMediaManager alloc] init];
26 | });
27 | return manager;
28 | }
29 |
30 | - (instancetype)init {
31 | self = [super init];
32 | if(!self) return nil;
33 | _lock = dispatch_semaphore_create(1);
34 | _cacheConfig = [ShortMediaCacheConfig defaultConfig];
35 | [[NSNotificationCenter defaultCenter] addObserver:self
36 | selector:@selector(resetCache)
37 | name:UIApplicationWillTerminateNotification
38 | object:nil];
39 | [[NSNotificationCenter defaultCenter] addObserver:self
40 | selector:@selector(resetCacheInbackgroud)
41 | name:UIApplicationDidEnterBackgroundNotification
42 | object:nil];
43 | return self;
44 | }
45 |
46 | - (void)loadMediaWithUrl:(NSURL *)url
47 | options:(ShortMediaOptions)options
48 | progress:(ShortMediaProgressBlock)progress
49 | completion:(ShortMediaCompletionBlock)completion {
50 | [[ShortMediaDownloader shareDownloader] downloadMediaWithUrl:url options:options progress:^(NSInteger receivedSize, NSInteger expectedSize) {
51 | dispatch_async(dispatch_get_main_queue(), ^{
52 | if(progress) progress(receivedSize, expectedSize);
53 | });
54 | } completion:^(NSError *error) {
55 | if(!error) {
56 | [self startPreloading];
57 | }
58 | dispatch_async(dispatch_get_main_queue(), ^{
59 | if(completion) completion(error);
60 | });
61 | }];
62 | }
63 |
64 | - (void)endLoadMediaWithUrl:(NSURL *)url {
65 | [[ShortMediaDownloader shareDownloader] cancelDownloadWithUrl:url];
66 | }
67 |
68 | - (NSData *)cacheDataFromOffset:(NSUInteger)offset
69 | length:(NSUInteger)length
70 | withUrl:(NSURL *)url {
71 | return [[ShortMediaCache shareCache] cacheDataFromOffset:offset length:length withUrl:url];
72 | }
73 |
74 | - (void)resetCache {
75 | [[ShortMediaCache shareCache] resetCacheWithConfig:_cacheConfig completion:nil];
76 | }
77 |
78 | - (void)resetCacheInbackgroud {
79 | Class UIApplicationClass = NSClassFromString(@"UIApplication");
80 | if(!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) {
81 | return;
82 | }
83 | UIApplication *application = [UIApplication performSelector:@selector(sharedApplication)];
84 | __block UIBackgroundTaskIdentifier backgroundTask = [application beginBackgroundTaskWithExpirationHandler:^{
85 | [application endBackgroundTask:backgroundTask];
86 | backgroundTask = UIBackgroundTaskInvalid;
87 | }];
88 |
89 | [[ShortMediaCache shareCache] resetFinalCacheWithConfig:_cacheConfig completion:^{
90 | [application endBackgroundTask:backgroundTask];
91 | backgroundTask = UIBackgroundTaskInvalid;
92 | }];
93 | }
94 |
95 | - (NSInteger)totalCachedSize {
96 | return [[ShortMediaCache shareCache] totalCachedSize];
97 | }
98 |
99 | - (NSString *)totalCachedSizeStr {
100 | NSInteger size = [self totalCachedSize];
101 | if (size <= 0) {
102 | return @"0.00M";
103 | }
104 | if (size < 1023 && size > 0)
105 | return([NSString stringWithFormat:@"%libytes",(long)size]);
106 | CGFloat floatSize = size / 1024.0;
107 | if (floatSize < 1023) return ([NSString stringWithFormat:@"%.2fKB", floatSize]);
108 | floatSize = floatSize / 1024.0;
109 | if (floatSize < 1023) return ([NSString stringWithFormat:@"%.2fMB", floatSize]);
110 | floatSize = floatSize / 1024.0;
111 |
112 | return ([NSString stringWithFormat:@"%.2fGB", floatSize]);
113 | }
114 |
115 | - (void)cleanCache {
116 | [[ShortMediaCache shareCache] cleanCache];
117 | }
118 |
119 | - (void)resetPreloadingWithMediaUrls:(NSArray *)mediaUrls {
120 | for (NSURL *url in mediaUrls) {
121 | [[ShortMediaDownloader shareDownloader] cancelDownloadWithUrl:url];
122 | }
123 | Lock();
124 | _prloadingMediaUrls = mediaUrls;
125 | _waitingPreloadingUrls = [NSMutableArray arrayWithArray:mediaUrls];
126 | UnLock();
127 | [self startPreloading];
128 | }
129 |
130 | - (void)startPreloading {
131 | Lock();
132 | BOOL canPreload = YES;
133 | if(!([_waitingPreloadingUrls count] > 0)) {
134 | NSLog(@"No preloading waitting");
135 | canPreload = NO;
136 | }
137 | else if(![ShortMediaDownloader shareDownloader].canPreload) {
138 | NSLog(@"Can not start preloading");
139 | canPreload = NO;
140 | }
141 |
142 | if(canPreload) {
143 | NSLog(@"start preloading");
144 | NSURL *url = _waitingPreloadingUrls.firstObject;
145 | __weak typeof(self) _self = self;
146 | UnLock();
147 | [[ShortMediaDownloader shareDownloader] preloadMediaWithUrl:url options:kNilOptions progress:nil completion:^(NSError *error) {
148 | __strong typeof(_self) self = _self;
149 | NSLog(@"End preloading");
150 | if(!error) {
151 | Lock();
152 | [self.waitingPreloadingUrls removeObject:url];
153 | UnLock();
154 | [self startPreloading];
155 | }
156 | }];
157 | }
158 | else {
159 | UnLock();
160 | }
161 | }
162 |
163 | @end
164 |
--------------------------------------------------------------------------------
/ShortMediaCache/ShortMediaCache.m:
--------------------------------------------------------------------------------
1 | //
2 | // ShortMediaCache.m
3 | // ShortMediaCacheDemo
4 | //
5 | // Created by dangercheng on 2018/7/30.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import "ShortMediaCache.h"
10 | #import
11 |
12 | #define Lock() dispatch_semaphore_wait(self->_lock, DISPATCH_TIME_FOREVER)
13 | #define UnLock() dispatch_semaphore_signal(self->_lock)
14 |
15 |
16 | @implementation ShortMediaCache {
17 | NSMutableDictionary *_urlFileNameCache;
18 | dispatch_queue_t _fileQueue;
19 | dispatch_semaphore_t _lock;
20 | }
21 |
22 | + (instancetype)shareCache {
23 | static ShortMediaCache *cache;
24 | static dispatch_once_t onceToken;
25 | dispatch_once(&onceToken, ^{
26 | NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
27 | cachePath = [cachePath stringByAppendingPathComponent:@"com.DandJ.shortMedia"];
28 | ShortMediaDiskCache *diskCache = [[ShortMediaDiskCache alloc] initWithPath:cachePath];
29 | cache = [[ShortMediaCache alloc] initWithDiskCache:diskCache];
30 | });
31 | return cache;
32 | }
33 |
34 | #pragma mark - pubulic
35 | - (instancetype)initWithDiskCache:(ShortMediaDiskCache *)diskCache {
36 | self = [super init];
37 | if(!self) return nil;
38 | self.diskCache = diskCache;
39 | _lock = dispatch_semaphore_create(1);
40 | _urlFileNameCache = [NSMutableDictionary dictionary];
41 | _fileQueue = dispatch_queue_create("com.DandJ.shortMedia.cache", DISPATCH_QUEUE_SERIAL);
42 | return self;
43 | }
44 |
45 | - (NSString *)createCacheFileWithUrl:(NSURL *)url {
46 | if(!url) {
47 | return nil;
48 | }
49 | Lock();
50 | NSString *fileName = [self fileNameWithUrl:url];
51 | NSString *filePath = [_diskCache createTempFileWithName:fileName];
52 | UnLock();
53 | return filePath;
54 | }
55 |
56 | - (void)appendWithData:(NSData *)data url:(NSURL *)url {
57 | __weak typeof(self) _self = self;
58 | dispatch_async(_fileQueue, ^{
59 | __strong typeof(_self) self = _self;
60 | if(!self) return;
61 | Lock();
62 | NSString *fileName = [self fileNameWithUrl:url];
63 | [self.diskCache appendData:data tempFileWithName:fileName];
64 | UnLock();
65 | });
66 | }
67 |
68 | - (NSData *)cacheDataFromOffset:(NSUInteger)offset
69 | length:(NSUInteger)length
70 | withUrl:(NSURL *)url {
71 | Lock();
72 | NSString *fileName = [self fileNameWithUrl:url];
73 | NSData *retData = [_diskCache dataFromOffset:offset length:length withName:fileName];
74 | UnLock();
75 | return retData;
76 | }
77 |
78 | - (void)cacheCompletedWithUrl:(NSURL *)url {
79 | NSString *fileName = [self fileNameWithUrl:url];
80 | __weak typeof(self) _self = self;
81 | dispatch_sync(_fileQueue, ^{
82 | __strong typeof(_self) self = _self;
83 | if(!self) return;
84 | Lock();
85 | [self.diskCache moveTempFileToFinalWithName:fileName];
86 | UnLock();
87 | });
88 | }
89 |
90 | - (BOOL)isCacheCompletedWithUrl:(NSURL *)url {
91 | NSString *fileName = [self fileNameWithUrl:url];
92 | Lock();
93 | BOOL ret = [_diskCache fileExistAtFinalWithName:fileName];
94 | UnLock();
95 | return ret;
96 | }
97 |
98 | - (NSInteger)cachedSizeWithUrl:(NSURL *)url {
99 | NSString *fileName = [self fileNameWithUrl:url];
100 | Lock();
101 | NSInteger size = [_diskCache tempCachedSizehWithName:fileName];
102 | UnLock();
103 | return size;
104 | }
105 |
106 | - (NSInteger)finalCachedSizeWithUrl:(NSURL *)url {
107 | NSString *fileName = [self fileNameWithUrl:url];
108 | Lock();
109 | NSInteger size = [_diskCache finalCachedSizeWithName:fileName];
110 | UnLock();
111 | return size;
112 | }
113 |
114 | - (NSInteger)totalCachedSize {
115 | Lock();
116 | NSInteger size = [_diskCache totalCachedSize];
117 | UnLock();
118 | return size;
119 | }
120 |
121 | - (void)cleanCache {
122 | Lock();
123 | [_diskCache moveAllCacheToTrash];
124 | UnLock();
125 | __weak typeof(self) _self = self;
126 | dispatch_async(_fileQueue, ^{
127 | __strong typeof(_self) self = _self;
128 | if(!self) return;
129 | Lock();
130 | [self.diskCache cleanTrash];
131 | UnLock();
132 | });
133 | }
134 |
135 | - (void)resetCacheWithConfig:(ShortMediaCacheConfig *)cacheConfig completion:(CompletionBlock)completion {
136 | __weak typeof(self) _self = self;
137 | dispatch_async(_fileQueue, ^{
138 | __strong typeof(_self) self = _self;
139 | if(!self) return;
140 | Lock();
141 | [self.diskCache resetCacheWithConfig:cacheConfig];
142 | UnLock();
143 |
144 | if(completion) {
145 | completion();
146 | }
147 | });
148 | }
149 |
150 | - (void)resetFinalCacheWithConfig:(ShortMediaCacheConfig *)cacheConfig completion:(CompletionBlock)completion {
151 | __weak typeof(self) _self = self;
152 | dispatch_async(_fileQueue, ^{
153 | __strong typeof(_self) self = _self;
154 | if(!self) return;
155 |
156 | Lock();
157 | [self.diskCache resetFinalCacheWitchConfig:cacheConfig];
158 | UnLock();
159 |
160 | if(completion) {
161 | completion();
162 | }
163 | });
164 | }
165 |
166 |
167 | #pragma mark - private
168 | - (NSString *)fileNameWithUrl:(NSURL *)url {
169 | NSString *fileName = [_urlFileNameCache objectForKey:url];
170 | if(!fileName) {
171 | fileName = [NSString stringWithFormat:@"%@.%@", [self md5:url.absoluteString], url.pathExtension];
172 | [_urlFileNameCache setObject:fileName forKey:url];
173 | }
174 | return fileName;
175 | }
176 |
177 | - (NSString *)md5:(NSString *)str {
178 | const char *cStr = [str UTF8String];
179 | unsigned char result[16];
180 | CC_MD5(cStr, (CC_LONG)strlen(cStr), result);
181 | return [NSString stringWithFormat:
182 | @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
183 | result[0], result[1], result[2], result[3],
184 | result[4], result[5], result[6], result[7],
185 | result[8], result[9], result[10], result[11],
186 | result[12], result[13], result[14], result[15]
187 | ];
188 | }
189 |
190 | @end
191 |
--------------------------------------------------------------------------------
/ShortMediaCache/ShortMediaResourceLoader.m:
--------------------------------------------------------------------------------
1 | //
2 | // ShortMediaResourceLoader.m
3 | // ShortMediaCacheDemo
4 | //
5 | // Created by dangercheng on 2018/7/30.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import "ShortMediaResourceLoader.h"
10 | #import "AVAssetResourceLoadingDataRequest+ShortMediaCache.h"
11 | #import
12 |
13 | @interface ShortMediaResourceLoader()
14 | @property (nonatomic, strong) NSMutableArray *pendingRequests;
15 | @property (nonatomic, assign) NSInteger expectedSize;
16 | @property (nonatomic, assign) NSInteger receivedSize;
17 | @property (nonatomic, assign) ShortMediaOptions options;
18 | @end
19 |
20 | @implementation ShortMediaResourceLoader
21 |
22 | - (instancetype)init {
23 | self = [super init];
24 | if(!self) return nil;
25 | _pendingRequests = [NSMutableArray array];
26 | return self;
27 | }
28 |
29 | - (instancetype)initWithDelegate:(id)delegate {
30 | _delegate = delegate;
31 | return [self init];
32 | }
33 |
34 | - (AVPlayerItem *)playItemWithUrl:(NSURL *)url {
35 | return [self playItemWithUrl:url options:kNilOptions];
36 | }
37 |
38 | - (AVPlayerItem *)playItemWithUrl:(NSURL *)url options:(ShortMediaOptions)options {
39 | _url = url;
40 | _options = options;
41 | [self startLoading];
42 | AVURLAsset *asset = [AVURLAsset URLAssetWithURL:[self unRecognizerUrl] options:nil];
43 | [asset.resourceLoader setDelegate:self queue:dispatch_get_main_queue()];
44 | AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:asset];
45 | return item;
46 | }
47 |
48 | - (void)startLoading {
49 | __weak typeof(self) _self = self;
50 | [[ShortMediaManager shareManager] loadMediaWithUrl:_url options:_options progress:^(NSInteger receivedSize, NSInteger expectedSize) {
51 | __strong typeof(_self) self = _self;
52 | if(!self) return;
53 | self.receivedSize = receivedSize;
54 | self.expectedSize = expectedSize;
55 | [self dealPendingRequests];
56 | } completion:^(NSError *error) {
57 | __strong typeof(_self) self = _self;
58 | if(!self) return;
59 | if(!error) {
60 | self.receivedSize = self.expectedSize;
61 | }
62 | [self dealPendingRequests];
63 | }];
64 | }
65 |
66 | - (void)endLoading {
67 | [[ShortMediaManager shareManager] endLoadMediaWithUrl:_url];
68 | }
69 |
70 | - (void)dealloc {
71 | [self endLoading];
72 | }
73 |
74 | #pragma mark - AVAssetResourceLoaderDelegate
75 | - (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest {
76 | if (resourceLoader && loadingRequest) {
77 | loadingRequest.dataRequest.respondedSize = 0;
78 | [self.pendingRequests addObject:loadingRequest];
79 | [self dealPendingRequests];
80 | }
81 | return YES;
82 | }
83 |
84 | - (void)resourceLoader:(AVAssetResourceLoader *)resourceLoader didCancelLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest {
85 | if (!loadingRequest.isFinished) {
86 | [loadingRequest finishLoadingWithError:[self loaderCancelledError]];
87 | }
88 | [self.pendingRequests removeObject:loadingRequest];
89 | }
90 |
91 | #pragma mark - private
92 | - (void)dealPendingRequests {
93 | NSMutableArray *finishedRequests = [NSMutableArray array];
94 | [self.pendingRequests enumerateObjectsUsingBlock:^(AVAssetResourceLoadingRequest *loadingRequest, NSUInteger idx, BOOL * _Nonnull stop) {
95 | [self fillInContentInformation:loadingRequest.contentInformationRequest];
96 | BOOL finish = [self respondWithDataForRequest:loadingRequest];
97 | if (finish) {
98 | [finishedRequests addObject:loadingRequest];
99 | [loadingRequest finishLoading];
100 | }
101 | }];
102 | if (finishedRequests.count) {
103 | [self.pendingRequests removeObjectsInArray:finishedRequests];
104 | }
105 | }
106 |
107 | - (BOOL)respondWithDataForRequest:(AVAssetResourceLoadingRequest *)loadingRequest {
108 | AVAssetResourceLoadingDataRequest *dataRequest = loadingRequest.dataRequest;
109 | NSInteger startOffset = (NSInteger)dataRequest.requestedOffset;
110 | if (dataRequest.currentOffset != 0) {
111 | startOffset = (NSInteger)dataRequest.currentOffset;
112 | }
113 | startOffset = MAX(0, startOffset);
114 | if(startOffset > _receivedSize) {
115 | return NO;
116 | }
117 | NSInteger canReadsize = _receivedSize - startOffset;
118 | canReadsize = MAX(0, canReadsize);
119 | NSInteger realReadSize = MIN(dataRequest.requestedLength, canReadsize);
120 | NSData *respondData = [NSData data];
121 | if(realReadSize > 2) {
122 | NSData *cacheData = [[ShortMediaManager shareManager] cacheDataFromOffset:startOffset length:realReadSize withUrl:_url];
123 | if(cacheData) {
124 | respondData = cacheData;
125 | }
126 | }
127 | dataRequest.respondedSize += realReadSize;
128 | [dataRequest respondWithData:respondData];
129 | if(dataRequest.respondedSize >= dataRequest.requestedLength) {
130 | return YES;
131 | } else {
132 | return NO;
133 | }
134 | }
135 |
136 |
137 | - (void)fillInContentInformation:(AVAssetResourceLoadingContentInformationRequest * _Nonnull)contentInformationRequest{
138 | if (contentInformationRequest && !contentInformationRequest.contentType && _expectedSize > 0) {
139 | NSString *fileExtension = [self.url pathExtension];
140 | NSString *UTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)fileExtension, NULL);
141 | NSString *contentTypeS = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)UTI, kUTTagClassMIMEType);
142 | if (!contentTypeS) {
143 | contentTypeS = @"application/octet-stream";
144 | }
145 | NSString *mimetype = contentTypeS;
146 | CFStringRef contentType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (__bridge CFStringRef _Nonnull)(mimetype), NULL);
147 | contentInformationRequest.byteRangeAccessSupported = YES;
148 | contentInformationRequest.contentType = CFBridgingRelease(contentType);
149 | contentInformationRequest.contentLength = _expectedSize;
150 | }
151 | }
152 |
153 | - (NSURL *)unRecognizerUrl {
154 | NSURLComponents *components = [[NSURLComponents alloc] initWithURL:[NSURL URLWithString:@"www.dandj.top"] resolvingAgainstBaseURL:NO];
155 | components.scheme = @"SystemCannotRecognition";
156 | return [components URL];
157 | }
158 |
159 | - (NSError *)loaderCancelledError{
160 | NSError *error = [[NSError alloc] initWithDomain:@"dandj.top"
161 | code:-3
162 | userInfo:@{NSLocalizedDescriptionKey:@"Resource loader cancelled"}];
163 | return error;
164 | }
165 |
166 | @end
167 |
--------------------------------------------------------------------------------
/ShortMediaCacheDemo/ViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // ShortMediaCacheDemo
4 | //
5 | // Created by dangercheng on 2018/7/30.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import "ViewController.h"
10 | #import "PlayerCell.h"
11 | #import "ShortMediaManager.h"
12 |
13 | @interface ViewController ()
14 | @property (weak, nonatomic) IBOutlet UICollectionView *collectionView;
15 | @property (nonatomic, strong) NSArray *videoUrls;
16 | @end
17 |
18 | @implementation ViewController
19 |
20 | - (void)viewDidLoad {
21 | [super viewDidLoad];
22 | // Do any additional setup after loading the view, typically from a nib.
23 | _collectionView.delegate = self;
24 | _collectionView.dataSource = self;
25 | _collectionView.pagingEnabled = YES;
26 |
27 | UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
28 | flowLayout.minimumLineSpacing = 0.0;
29 | flowLayout.minimumInteritemSpacing = 0.0;
30 | flowLayout.footerReferenceSize = CGSizeZero;
31 | flowLayout.headerReferenceSize = CGSizeZero;
32 | flowLayout.sectionInset = UIEdgeInsetsZero;
33 | flowLayout.scrollDirection = UICollectionViewScrollDirectionVertical;
34 |
35 | flowLayout.itemSize = CGSizeMake([UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height);
36 | _collectionView.collectionViewLayout = flowLayout;
37 |
38 | NSArray *videoUrls = @[
39 | @"http://jmdianbostag.ks3-cn-beijing.ksyun.com/MjQ0NzY5NDU2/MTUxNjY5NzAzNTgwNg_E_E/OTQ2NTc2MA_E_E/MDY2OTVDOUItMzYwNi00MUM1LUJFRUQtRjJCQkJGOThFOTIyLk1PVg_E_E_default.mp4",
40 | @"http://jmdianbostag.ks3-cn-beijing.ksyun.com/MTE1NzIzNzAz/MTUxNjUzNzA4MDk4Nw_E_E/MTAyNzE1NDM_E/dHJpbS4wN0JDOTEzMy01M0VELTQ5OUQtOTY2RS1GNUM4NDZEMUY5OTAuTU9W_default.mp4",
41 | @"http://jmvideo1.jumei.com/MQ_E_E/MTUxOTY0MjA2ODI4OQ_E_E/Mjg1NTk4OQ_E_E/L2hvbWUvd3d3L2xvZ3MvdmlkZW8vZmlsZV84OTkyMTMtOThiNGUyNzEzMzIyMGMyMTdhZmU2Y2FhMWEyYTZlZDUvdmlkZW8ubXA0.mp4",
42 | @"http://jmvideo1.jumei.com/MQ_E_E/MTUxOTY0MTA4MjUxMA_E_E/Mzg0NTAxMA_E_E/L2hvbWUvd3d3L2xvZ3MvdmlkZW8vZmlsZV84OTkyMTMtOTdkNDRlNTQ5NTM5NjZhMWZmZDA1OTRlYzhlNzQwYmMvdmlkZW8ubXA0.mp4",
43 | @"http://jmvideo1.jumei.com/MQ_E_E/MTUxOTM1NTUwNDYxNQ_E_E/MjEyNzc3MA_E_E/L2hvbWUvd3d3L2xvZ3MvdmlkZW8vZG91eWluXzY1MjQwOTQ0MzEzMjk1MjA5MDQvdmlkZW8ubXA0.mp4",
44 | @"http://jmvideo1.jumei.com/MQ_E_E/MTUxOTY0MjA5MzQ4Ng_E_E/MjcwNTgwOA_E_E/L2hvbWUvd3d3L2xvZ3MvdmlkZW8vZmlsZV84OTkyMTMtOThiYTA0MTZiNTU3NGVhN2QxMjA4MGZlMzdiYmI0MWIvdmlkZW8ubXA0.mp4",
45 | @"http://jmvideo1.jumei.com/MQ_E_E/MTUxOTY0MTkwMDA3MA_E_E/NDA0NDkzNA_E_E/L2hvbWUvd3d3L2xvZ3MvdmlkZW8vZmlsZV84OTkyMTMtOTg3N2Y3ZTM2YTYzN2I2ZjY2OTE0ZGU1YjIxNDFkZDQvdmlkZW8ubXA0.mp4",
46 | @"http://jmvideo1.jumei.com/MQ_E_E/MTUxOTY0MTA2NTQ1OA_E_E/NDE0OTE2Nw_E_E/L2hvbWUvd3d3L2xvZ3MvdmlkZW8vZmlsZV84OTkyMTMtOTdjZWI5ODk3Yzk1NTY1MjBmY2E0NjZmZTI4MmQ0MmUvdmlkZW8ubXA0.mp4",
47 | @"http://jmvideo.jumei.com/MQ_E_E/MTUzMzcxMjMxMzcxNw_E_E/MTEwODU4Nw_E_E/L2hvbWUvd3d3L2xvZ3MvdmlkZW8vZG91eWluXzY1MjcxNjgzNTE2NjIyNDcxODEvdmlkZW8ubXA0.mp4",
48 | @"http://jmvideo.jumei.com/MQ_E_E/MTUzMjQyMTU4NzczMQ_E_E/MjE1MzE0Mw_E_E/L2hvbWUvd3d3L2xvZ3MvdmlkZW8vZG91eWluXzY1MzgxNzU0MzQ5MDU4ODE4NjMvdmlkZW8ubXA0.mp4",
49 | @"http://jmvideo.jumei.com/MQ_E_E/MTUzMjQzNDc5MTUwOA_E_E/ODk3Mjg1/L2hvbWUvd3d3L2xvZ3MvdmlkZW8vZG91eWluXzY1ODA4OTQwOTI1Mzg5NDA2NzYvdmlkZW8ubXA0.mp4",
50 | @"http://jmvideo.jumei.com/MQ_E_E/MTUzMjUxMjE1NDY2Nw_E_E/OTI1ODU4Mg_E_E/L2hvbWUvd3d3L2xvZ3MvdmlkZW8vZG91eWluXzY1Njc5ODM4NTg0Mzg5MDA5OTUvdmlkZW8ubXA0.mp4",
51 | @"http://jmvideo.jumei.com/MQ_E_E/MTUyMjY1NDAwOTg3Nw_E_E/MjYxMTg2Mw_E_E/L2hvbWUvd3d3L2xvZ3MvdmlkZW8vZG91eWluXzY1MTkyNDcwMzQ4NjY3MzIzMDIvdmlkZW8ubXA0.mp4"];
52 | _videoUrls = videoUrls;
53 | }
54 |
55 | - (void)viewDidAppear:(BOOL)animated {
56 | [super viewDidAppear:animated];
57 | [self scrollViewDidEndDecelerating:_collectionView];
58 | _collectionView.contentOffset = CGPointZero;
59 | }
60 |
61 | - (IBAction)clickCleanCacheBtn:(UIButton *)sender {
62 | NSString *cachedSizeStr = [[ShortMediaManager shareManager] totalCachedSizeStr];
63 | NSString *message = [NSString stringWithFormat:@"Confirm to clean cache? \n cache size:%@", cachedSizeStr];
64 | UIAlertController *alertControoler = [UIAlertController alertControllerWithTitle:nil message:message preferredStyle:UIAlertControllerStyleAlert];
65 | UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"NO" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
66 |
67 | }];
68 | UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"YES" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
69 | [[ShortMediaManager shareManager] cleanCache];
70 | }];
71 | [alertControoler addAction:cancelAction];
72 | [alertControoler addAction:okAction];
73 | [self presentViewController:alertControoler animated:YES completion:nil];
74 | }
75 |
76 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
77 | return _videoUrls.count;
78 | }
79 |
80 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
81 | PlayerCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"PlayerCell" forIndexPath:indexPath];
82 | return cell;
83 | }
84 |
85 | - (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
86 | NSLog(@"1====>enddisplay index: %ld, cell: %@, visibleCells: %@",(long)indexPath.row, cell, collectionView.visibleCells);
87 | PlayerCell *playerCell = (PlayerCell*)cell;
88 | NSString *videoStr = [_videoUrls objectAtIndex:indexPath.row];
89 | [playerCell stopPlayWithUrl:[NSURL URLWithString:videoStr]];
90 | }
91 |
92 | - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
93 | __weak typeof(self) _self = self;
94 | dispatch_async(dispatch_get_main_queue(), ^{
95 | __strong typeof(_self) self = _self;
96 | NSInteger index = scrollView.contentOffset.y / self.collectionView.frame.size.height;
97 | NSArray *visibleCells = self.collectionView.visibleCells;
98 | NSString *videoStr = [self.videoUrls objectAtIndex:index];
99 | PlayerCell *cell = visibleCells.lastObject;
100 | NSLog(@"2====>scroll to cell %@ index: %ld, visibleCells:%d",cell, (long)index, visibleCells.count);
101 | [cell playVideoWithUrl:[NSURL URLWithString:videoStr]];
102 | [self resetPreloadWithIndex:index];
103 | });
104 | }
105 |
106 | - (void)resetPreloadWithIndex:(NSInteger)index {
107 | index ++;
108 | if(index >= _videoUrls.count) {
109 | return;
110 | }
111 | NSInteger maxPreloadCount = 3;
112 | NSMutableArray *preloadUrls = [NSMutableArray arrayWithCapacity:maxPreloadCount];
113 | for(NSInteger i = index; i < _videoUrls.count; i++) {
114 | NSString *videoUrlStr = _videoUrls[i];
115 | NSURL *videoUrl = [NSURL URLWithString:videoUrlStr];
116 | if(videoUrl) {
117 | [preloadUrls addObject:videoUrl];
118 | if(preloadUrls.count == maxPreloadCount) {
119 | break;
120 | }
121 | }
122 | }
123 | [[ShortMediaManager shareManager] resetPreloadingWithMediaUrls:preloadUrls];
124 | }
125 |
126 | @end
127 |
--------------------------------------------------------------------------------
/ShortMediaCache/ShortMediaDownloader.m:
--------------------------------------------------------------------------------
1 | //
2 | // ShortMediaDownloader.m
3 | // ShortMediaCacheDemo
4 | //
5 | // Created by dangercheng on 2018/8/7.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import "ShortMediaDownloader.h"
10 | #import "ShortMediaDownloadOperation.h"
11 |
12 | #define Lock() dispatch_semaphore_wait(self.lock, DISPATCH_TIME_FOREVER)
13 | #define UnLock() dispatch_semaphore_signal(self.lock)
14 |
15 | @interface ShortMediaDownloader()
16 | @property (nonatomic, strong) NSURLSession *session;
17 | @property (nonatomic, strong) NSOperationQueue *queue;
18 | @property (nonatomic, strong) NSMutableDictionary *operationCache;
19 |
20 | @property (nonatomic, strong) dispatch_semaphore_t lock;
21 | @end
22 |
23 | @implementation ShortMediaDownloader
24 |
25 | + (instancetype)shareDownloader {
26 | static ShortMediaDownloader *instance;
27 | static dispatch_once_t onceToken;
28 | dispatch_once(&onceToken, ^{
29 | instance = [[ShortMediaDownloader alloc] init];
30 | });
31 | return instance;
32 | }
33 |
34 | - (instancetype)init {
35 | self = [super init];
36 | if(!self) return nil;
37 | _lock = dispatch_semaphore_create(1);
38 | _queue = [NSOperationQueue new];
39 | _operationCache = [NSMutableDictionary dictionary];
40 | NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
41 | _session = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil];
42 | return self;
43 | }
44 |
45 | - (void)downloadMediaWithUrl:(NSURL *)url
46 | options:(ShortMediaOptions)options
47 | progress:(ShortMediaProgressBlock)progress
48 | completion:(ShortMediaCompletionBlock)completion {
49 | [self downloadMediaWithUrl:url options:options preload:NO progress:progress completion:completion];
50 | }
51 |
52 | - (void)preloadMediaWithUrl:(NSURL *)url
53 | options:(ShortMediaOptions)options
54 | progress:(ShortMediaProgressBlock)progress
55 | completion:(ShortMediaCompletionBlock)completion {
56 | [self downloadMediaWithUrl:url options:options preload:YES progress:progress completion:completion];
57 | }
58 |
59 | - (void)downloadMediaWithUrl:(NSURL *)url
60 | options:(ShortMediaOptions)options
61 | preload:(BOOL)preload
62 | progress:(ShortMediaProgressBlock)progress
63 | completion:(ShortMediaCompletionBlock)completion {
64 | BOOL cached = [[ShortMediaCache shareCache] isCacheCompletedWithUrl:url];
65 | if(cached) {
66 | NSInteger fileSize = [[ShortMediaCache shareCache] finalCachedSizeWithUrl:url];
67 | if(progress) progress(fileSize, fileSize);
68 | if(completion) completion(nil);
69 | return;
70 | }
71 |
72 | [self cancelDownloadWithUrl:url];
73 | NSInteger cachedSize = [[ShortMediaCache shareCache] cachedSizeWithUrl:url];
74 | NSMutableURLRequest *downloadRequest = [NSMutableURLRequest requestWithURL:url];
75 | NSString *range = [NSString stringWithFormat:@"bytes=%ld-", (long)cachedSize];
76 | [downloadRequest setValue:range forHTTPHeaderField:@"Range"];
77 | downloadRequest.HTTPShouldHandleCookies = (options & ShortMediaOptionsHandleCookies);
78 | downloadRequest.HTTPShouldUsePipelining = YES;
79 |
80 | __weak typeof(self) _self = self;
81 | ShortMediaDownloadOperation *downloadOperation = [[ShortMediaDownloadOperation alloc] initWithRequest:downloadRequest session:_session options:options progress:progress completion:^(NSError *error) {
82 | __strong typeof(_self) self = _self;
83 | if(!self) return;
84 | Lock();
85 | [self.operationCache removeObjectForKey:url];
86 | UnLock();
87 | if(completion) completion(error);
88 | }];
89 | if(_userName && _password) {
90 | downloadOperation.credential = [NSURLCredential credentialWithUser:_userName password:_password persistence:NSURLCredentialPersistenceForSession];
91 | }
92 | downloadOperation.isPreloading = preload;
93 | Lock();
94 | if(!preload) {
95 | NSMutableArray *needCancelUrls = [NSMutableArray array];
96 | for (NSURL *url in _operationCache.allKeys) {
97 | ShortMediaDownloadOperation *operation = _operationCache[url];
98 | if(operation.isPreloading) {
99 | [needCancelUrls addObject:url];
100 | }
101 | }
102 | for (NSURL *url in needCancelUrls) {
103 | [_operationCache removeObjectForKey:url];
104 | }
105 | }
106 | [_operationCache setObject:downloadOperation forKey:url];
107 | [_queue addOperation:downloadOperation];
108 | UnLock();
109 | }
110 |
111 | - (void)cancelDownloadWithUrl:(NSURL *)url {
112 | if(!url) {
113 | return;
114 | }
115 | Lock();
116 | ShortMediaDownloadOperation *operation = [_operationCache objectForKey:url];
117 | if(operation) {
118 | [operation cancel];
119 | [_operationCache removeObjectForKey:url];
120 | }
121 | UnLock();
122 | }
123 |
124 | - (BOOL)canPreload {
125 | BOOL retValue = YES;
126 | Lock();
127 | for (ShortMediaDownloadOperation *operation in self.operationCache.allValues) {
128 | if(!operation.isPreloading) {
129 | retValue = NO;
130 | break;
131 | }
132 | }
133 | UnLock();
134 | return retValue;
135 | }
136 |
137 | - (void)dealloc {
138 | [_session invalidateAndCancel];
139 | }
140 |
141 | #pragma mark NSURLSessionTaskDelegate
142 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
143 | Lock();
144 | ShortMediaDownloadOperation *operation = [_operationCache objectForKey:task.originalRequest.URL];
145 | UnLock();
146 | [operation URLSession:session task:task didCompleteWithError:error];
147 | }
148 |
149 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
150 | ShortMediaDownloadOperation *operation = [_operationCache objectForKey:task.originalRequest.URL];
151 | [operation URLSession:session task:task didReceiveChallenge:challenge completionHandler:completionHandler];
152 | }
153 |
154 | #pragma mark - NSURLSessionDataDelegate
155 | - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
156 | didReceiveResponse:(NSURLResponse *)response
157 | completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
158 | Lock();
159 | ShortMediaDownloadOperation *operation = [_operationCache objectForKey:dataTask.originalRequest.URL];
160 | UnLock();
161 | [operation URLSession:session dataTask:dataTask didReceiveResponse:response completionHandler:completionHandler];
162 | }
163 |
164 |
165 | - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
166 | Lock();
167 | ShortMediaDownloadOperation *operation = [_operationCache objectForKey:dataTask.originalRequest.URL];
168 | UnLock();
169 | [operation URLSession:session dataTask:dataTask didReceiveData:data];
170 | }
171 |
172 | @end
173 |
--------------------------------------------------------------------------------
/ShortMediaCacheDemo/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 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/ShortMediaCache/ShortMediaDownloadOperation.m:
--------------------------------------------------------------------------------
1 | //
2 | // ShortMediaDownloadOperation.m
3 | // ShortMediaCacheDemo
4 | //
5 | // Created by dangercheng on 2018/7/30.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import "ShortMediaDownloadOperation.h"
10 | #import
11 |
12 | @interface ShortMediaDownloadOperation()
13 | @property (readwrite, getter=isExecuting) BOOL executing;
14 | @property (readwrite, getter=isFinished) BOOL finished;
15 | @property (readwrite, getter=isCancelled) BOOL cancelled;
16 | @property (readwrite) BOOL started;
17 |
18 | @property (nonatomic, assign) NSInteger expectedSize;
19 | @property (nonatomic, assign) NSInteger receivedSize;
20 |
21 | @property (nonatomic, strong) NSRecursiveLock *lock;
22 | @property (nonatomic, weak) NSURLSession *session;
23 | @property (nonatomic, strong) NSURLSessionDataTask *dataTask;
24 | @property (nonatomic, strong) NSURLRequest *request;
25 |
26 | @property (nonatomic, copy) ShortMediaProgressBlock progress;
27 | @property (nonatomic, copy) ShortMediaCompletionBlock completion;
28 |
29 | @end
30 |
31 | @implementation ShortMediaDownloadOperation
32 | @synthesize executing = _executing;
33 | @synthesize finished = _finished;
34 | @synthesize cancelled = _cancelled;
35 |
36 | - (instancetype)initWithRequest:(NSURLRequest *)request
37 | session:(NSURLSession *)session
38 | options:(ShortMediaOptions)options
39 | progress:(ShortMediaProgressBlock)progress
40 | completion:(ShortMediaCompletionBlock)completion {
41 | self = [super init];
42 | if (!self) return nil;
43 | _request = request;
44 | _session = session;
45 | _options = options;
46 | _progress = progress;
47 | _completion = completion;
48 | _executing = NO;
49 | _finished = NO;
50 | _cancelled = NO;
51 | _started = NO;
52 | return self;
53 | }
54 |
55 | - (void)dealloc {
56 | [self finish];
57 | }
58 |
59 | #pragma mark - NSURLSessionTaskDelegate
60 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
61 |
62 | NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
63 | __block NSURLCredential *credential = nil;
64 |
65 | if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
66 | if (!(_options & ShortMediaOptionsOptionAllowInvalidSSLCertificates)) {
67 | disposition = NSURLSessionAuthChallengePerformDefaultHandling;
68 | } else {
69 | credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
70 | disposition = NSURLSessionAuthChallengeUseCredential;
71 | }
72 | } else {
73 | if ([challenge previousFailureCount] == 0) {
74 | if (_credential) {
75 | credential = _credential;
76 | disposition = NSURLSessionAuthChallengeUseCredential;
77 | } else {
78 | disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
79 | }
80 | } else {
81 | disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
82 | }
83 | }
84 |
85 | if (completionHandler) {
86 | completionHandler(disposition, credential);
87 | }
88 | }
89 |
90 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
91 | if(!error) {
92 | [[ShortMediaCache shareCache] cacheCompletedWithUrl:_request.URL];
93 | NSLog(@"ShortMediaDownloadOperation donwload complete: %@", _request.URL.absoluteString);
94 | } else {
95 | NSLog(@"ShortMediaDownloadOperation donwload error: %@", _request.URL.absoluteString);
96 | }
97 | if (_completion) _completion(error);
98 |
99 | [self finish];
100 | }
101 |
102 | #pragma mark - NSURLSessionDataDelegate
103 | - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
104 | didReceiveResponse:(NSURLResponse *)response
105 | completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
106 | if (![response respondsToSelector:@selector(statusCode)] || ([((NSHTTPURLResponse *)response) statusCode] < 400 && [((NSHTTPURLResponse *)response) statusCode] != 304)) {
107 | NSInteger expected = response.expectedContentLength > 0 ? (NSInteger)response.expectedContentLength : 0;
108 | NSString *rangeHead = [_request valueForHTTPHeaderField:@"Range"];
109 | NSInteger startOffset = 0;
110 | if(rangeHead && rangeHead.length >= 7) {
111 | NSString *startOffsetStr = [rangeHead substringWithRange:NSMakeRange(6, rangeHead.length - 7)];
112 | startOffset = [startOffsetStr integerValue];
113 | }
114 | _expectedSize = startOffset + expected;
115 | _receivedSize = startOffset;
116 |
117 | [[ShortMediaCache shareCache] createCacheFileWithUrl:_request.URL];
118 | if(_progress) _progress(startOffset, _expectedSize);
119 | }
120 | else {
121 | NSUInteger code = [((NSHTTPURLResponse *)response) statusCode];
122 | NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:code userInfo:nil];
123 | if(_completion) _completion(error);
124 | [self finish];
125 | }
126 |
127 | if (completionHandler) {
128 | completionHandler(NSURLSessionResponseAllow);
129 | }
130 | }
131 |
132 |
133 | - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
134 | [[ShortMediaCache shareCache] appendWithData:data url:_request.URL];
135 | [_lock lock];
136 | _receivedSize += data.length;
137 | [_lock unlock];
138 | if(_progress) _progress(_receivedSize, _expectedSize);
139 | }
140 |
141 | #pragma mark - Methods
142 | - (void)start {
143 | [_lock lock];
144 | self.started = YES;
145 | if([self isCancelled]) {
146 | [self cancelOperation];
147 | NSError *cancelError = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:nil];
148 | if(_completion) _completion(cancelError);
149 | } else if([self isReady] && ![self isFinished] && ![self isExecuting]) {
150 | if(!_request) {
151 | NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil];
152 | if(_completion) _completion(error);
153 | [self finish];
154 | } else {
155 | self.executing = YES;
156 | _dataTask = [_session dataTaskWithRequest:_request];
157 | [_dataTask resume];
158 | }
159 | }
160 | [_lock unlock];
161 | }
162 |
163 | - (void)cancel {
164 | [_lock lock];
165 | if(![self isCancelled]) {
166 | [super cancel];
167 | self.cancelled = YES;
168 | if ([self isExecuting]) {
169 | self.executing = NO;
170 | [self cancelOperation];
171 | }
172 | if(self.started) {
173 | self.finished = YES;
174 | }
175 | }
176 | [_lock unlock];
177 | }
178 |
179 | - (void)cancelOperation {
180 | if(_dataTask) {
181 | [_dataTask cancel];
182 | _dataTask = nil;
183 | [self finish];
184 | }
185 | }
186 |
187 | - (void)finish {
188 | self.finished = YES;
189 | self.executing = NO;
190 | }
191 |
192 | - (void)setExecuting:(BOOL)executing {
193 | [_lock lock];
194 | if(_executing != executing) {
195 | [self willChangeValueForKey:@"isExecuting"];
196 | _executing = executing;
197 | [self didChangeValueForKey:@"isExecuting"];
198 | }
199 | [_lock unlock];
200 | }
201 |
202 | - (BOOL)isExecuting {
203 | [_lock lock];
204 | BOOL executing = _executing;
205 | [_lock unlock];
206 | return executing;
207 | }
208 |
209 | - (void)setFinished:(BOOL)finished {
210 | [_lock lock];
211 | if(_finished != finished) {
212 | [self willChangeValueForKey:@"isFinished"];
213 | _finished = finished;
214 | [self didChangeValueForKey:@"isFinished"];
215 | }
216 | [_lock unlock];
217 | }
218 |
219 | - (BOOL)isFinished {
220 | [_lock lock];
221 | BOOL finished = _finished;
222 | [_lock unlock];
223 | return finished;
224 | }
225 |
226 | - (void)setCancelled:(BOOL)cancelled {
227 | [_lock lock];
228 | if(_cancelled != cancelled) {
229 | [self willChangeValueForKey:@"isCancelled"];
230 | _cancelled = cancelled;
231 | [self didChangeValueForKey:@"isCancelled"];
232 | }
233 | [_lock unlock];
234 | }
235 |
236 | - (BOOL)isCancelled {
237 | [_lock lock];
238 | BOOL cancelled = _cancelled;
239 | [_lock unlock];
240 | return cancelled;
241 | }
242 |
243 | - (BOOL)isConcurrent {
244 | return YES;
245 | }
246 |
247 | - (BOOL)isAsynchronous {
248 | return YES;
249 | }
250 |
251 | + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
252 | if([key isEqualToString:@"isExecuting"] ||
253 | [key isEqualToString:@"isFinished"] ||
254 | [key isEqualToString:@"isCancelled"]) {
255 | return NO;
256 | }
257 | return YES;
258 | }
259 |
260 | @end
261 |
--------------------------------------------------------------------------------
/ShortMediaCache/ShortMediaDiskCache.m:
--------------------------------------------------------------------------------
1 | //
2 | // ShortMediaDiskCache.m
3 | // ShortMediaCacheDemo
4 | //
5 | // Created by dangercheng on 2018/7/31.
6 | // Copyright © 2018年 DandJ. All rights reserved.
7 | //
8 |
9 | #import "ShortMediaDiskCache.h"
10 | #import "ShortMediaCacheConfig.h"
11 |
12 | static NSString *const kMediaFinalDirectoryName = @"media";
13 | static NSString *const kMediaTempDirectoryName = @"temp";
14 | static NSString *const kMediaTrashDirectoryName = @"trash";
15 |
16 | @implementation ShortMediaDiskCache {
17 | NSString *_finalPath;
18 | NSString *_tempPath;
19 | NSString *_trashPath;
20 | CFMutableDictionaryRef _fileHandleCache;
21 | }
22 |
23 |
24 | - (instancetype)initWithPath:(NSString *)path {
25 | self = [super init];
26 | if(!self) return nil;
27 | _path = path;
28 | _finalPath = [path stringByAppendingPathComponent:kMediaFinalDirectoryName];
29 | _tempPath = [path stringByAppendingPathComponent:kMediaTempDirectoryName];
30 | _trashPath = [path stringByAppendingPathComponent:kMediaTrashDirectoryName];
31 | NSFileManager *fileManager = [NSFileManager defaultManager];
32 |
33 | _fileHandleCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
34 |
35 | NSError *error = nil;
36 | if(![fileManager createDirectoryAtPath:_finalPath withIntermediateDirectories:YES attributes:nil error:&error] ||
37 | ![fileManager createDirectoryAtPath:_tempPath withIntermediateDirectories:YES attributes:nil error:&error] ||
38 | ![fileManager createDirectoryAtPath:_trashPath withIntermediateDirectories:YES attributes:nil error:&error]) {
39 | return nil;
40 | }
41 |
42 | NSLog(@"ShortMediaCache tempPath:%@", _tempPath);
43 | NSLog(@"ShortMediaCache finalPath:%@", _finalPath);
44 | NSLog(@"ShortMediaCache trashPath:%@",_trashPath);
45 | return self;
46 | }
47 |
48 | - (void)dealloc {
49 | if (_fileHandleCache) CFRelease(_fileHandleCache);
50 | _fileHandleCache = NULL;
51 | }
52 |
53 | - (NSString *)createTempFileWithName:(NSString *)fileName {
54 | NSString *filePath = [_tempPath stringByAppendingPathComponent:fileName];
55 | NSFileManager *fileManager = [[NSFileManager alloc] init];
56 | if(![fileManager fileExistsAtPath:filePath]) {
57 | [fileManager createFileAtPath:filePath contents:nil attributes:nil];
58 | }
59 | return filePath;
60 | }
61 |
62 | - (void)appendData:(NSData *)data tempFileWithName:(NSString *)fileName {
63 | if(!data) {
64 | return;
65 | }
66 | if(!(fileName.length > 0)) {
67 | return;
68 | }
69 | NSString *filePath = [_tempPath stringByAppendingPathComponent:fileName];
70 | NSFileHandle *fileHandle = CFDictionaryGetValue(_fileHandleCache, (__bridge const void *)(fileName));
71 | if(!fileHandle) {
72 | fileHandle = [NSFileHandle fileHandleForWritingAtPath:filePath];
73 | CFDictionarySetValue(_fileHandleCache, (__bridge const void *)(fileName), (__bridge const void *)(fileHandle));
74 | }
75 | [fileHandle seekToEndOfFile];
76 | [fileHandle writeData:data];
77 | }
78 |
79 | - (void)moveTempFileToFinalWithName:(NSString *)fileName {
80 | if(!(fileName.length > 0)) {
81 | return;
82 | }
83 | NSString *tempFilePath = [_tempPath stringByAppendingPathComponent:fileName];
84 | NSString *finalFilePath = [_finalPath stringByAppendingPathComponent:fileName];
85 | NSError *error = nil;
86 | NSFileManager *fileManager = [[NSFileManager alloc] init];
87 | [fileManager moveItemAtPath:tempFilePath toPath:finalFilePath error:&error];
88 | if(!error) {
89 | CFDictionaryRemoveValue(_fileHandleCache, (__bridge const void *)(fileName));
90 | }
91 | }
92 |
93 | - (NSData *)dataFromOffset:(NSUInteger)offset
94 | length:(NSUInteger)length
95 | withName:(NSString *)fileName {
96 | if(!(fileName.length > 0)) {
97 | return nil;
98 | }
99 |
100 | NSFileManager *fileManager = [[NSFileManager alloc] init];
101 | NSString *tempFileFath = [_tempPath stringByAppendingPathComponent:fileName];
102 | NSString *finalFilePath = [_finalPath stringByAppendingPathComponent:fileName];
103 |
104 | NSString *targetPath = tempFileFath;
105 | BOOL fileExist = [fileManager fileExistsAtPath:finalFilePath];
106 | if(fileExist) {
107 | targetPath = finalFilePath;
108 | } else {
109 | fileExist = [fileManager fileExistsAtPath:tempFileFath];
110 | if(!fileExist) {
111 | return nil;
112 | }
113 | }
114 |
115 | NSError *error;
116 | NSData *tempVideoData = [NSData dataWithContentsOfFile:targetPath options:NSDataReadingMappedIfSafe error:&error];
117 | if (!error && !(tempVideoData.length < (offset + length))) {
118 | NSRange respondRange = NSMakeRange(offset, length);
119 | return [tempVideoData subdataWithRange:respondRange];
120 | } else {
121 | return nil;
122 | }
123 | }
124 |
125 | - (NSInteger)finalCachedSizeWithName:(NSString *)fileName {
126 | NSString *path = [_finalPath stringByAppendingPathComponent:fileName];
127 | return [self fileSizeWithPath:path];
128 | }
129 |
130 | - (NSInteger)tempCachedSizehWithName:(NSString *)fileName {
131 | NSString *path = [_tempPath stringByAppendingPathComponent:fileName];
132 | return [self fileSizeWithPath:path];
133 | }
134 |
135 | - (NSInteger)fileSizeWithPath:(NSString *)filePath {
136 | if(!(filePath.length > 0)) {
137 | return 0;
138 | }
139 | NSFileManager *fileManager = [[NSFileManager alloc] init];
140 | if(![fileManager fileExistsAtPath:filePath]) {
141 | return 0;
142 | }
143 | NSError *error = nil;
144 | NSInteger fileSize = 0;
145 | NSDictionary *fileDict = [fileManager attributesOfItemAtPath:filePath error:&error];
146 | if (!error && fileDict) {
147 | fileSize = (NSInteger)[fileDict fileSize];
148 | }
149 |
150 | return fileSize;
151 | }
152 |
153 | - (BOOL)fileExistAtFinalWithName:(NSString *)fileName {
154 | if(!(fileName.length > 0)) {
155 | return NO;
156 | }
157 | NSFileManager *fileManager = [[NSFileManager alloc] init];
158 | NSString *filePath = [_finalPath stringByAppendingPathComponent:fileName];
159 | return [fileManager fileExistsAtPath:filePath];
160 | }
161 |
162 | - (NSInteger)totalCachedSize {
163 | NSFileManager *fileManager = [[NSFileManager alloc] init];
164 | NSDirectoryEnumerator *fileEnumerator = [fileManager enumeratorAtPath:_tempPath];
165 | NSInteger totalFileSize = 0;
166 | for (NSString *fileName in fileEnumerator) {
167 | NSString *filePath = [_tempPath stringByAppendingPathComponent:fileName];
168 | NSInteger fileSize = [self fileSizeWithPath:filePath];
169 | totalFileSize += fileSize;
170 | }
171 | fileEnumerator = [fileManager enumeratorAtPath:_finalPath];
172 | for (NSString *fileName in fileEnumerator) {
173 | NSString *filePath = [_finalPath stringByAppendingPathComponent:fileName];
174 | NSInteger fileSize = [self fileSizeWithPath:filePath];
175 | totalFileSize += fileSize;
176 | }
177 | return totalFileSize;
178 | }
179 |
180 | - (void)moveAllCacheToTrash {
181 | NSFileManager *fileManager = [[NSFileManager alloc] init];
182 | NSDirectoryEnumerator *fileEnumerator_temp = [fileManager enumeratorAtPath:_tempPath];
183 | for (NSString *fileName in fileEnumerator_temp) {
184 | NSError *error;
185 | NSString *filePath = [_tempPath stringByAppendingPathComponent:fileName];
186 | NSString *toPath = [_trashPath stringByAppendingPathComponent:fileName];
187 | [fileManager moveItemAtPath:filePath toPath:toPath error:&error];
188 | if(!error) {
189 | CFDictionaryRemoveValue(_fileHandleCache, (__bridge const void *)(fileName));
190 | }
191 | }
192 |
193 | NSDirectoryEnumerator *fileEnumerator_full = [fileManager enumeratorAtPath:_finalPath];
194 | for (NSString *fileName in fileEnumerator_full) {
195 | NSError *error;
196 | NSString *filePath = [_finalPath stringByAppendingPathComponent:fileName];
197 | NSString *toPath = [_trashPath stringByAppendingPathComponent:fileName];
198 | [fileManager moveItemAtPath:filePath toPath:toPath error:nil];
199 | if(!error) {
200 | CFDictionaryRemoveValue(_fileHandleCache, (__bridge const void *)(fileName));
201 | }
202 | }
203 | }
204 |
205 | - (void)cleanTrash {
206 | NSFileManager *fileManager = [[NSFileManager alloc] init];
207 | [fileManager removeItemAtPath:_trashPath error:nil];
208 | [fileManager createDirectoryAtPath:_trashPath withIntermediateDirectories:YES attributes:nil error:nil];
209 | }
210 |
211 | - (void)resetCacheWithConfig:(ShortMediaCacheConfig *)config {
212 | [self resetTempCacheWitchConfig:config];
213 | [self resetFinalCacheWitchConfig:config];
214 | }
215 |
216 | - (void)resetTempCacheWitchConfig:(ShortMediaCacheConfig *)config {
217 | [self resetCacheWithPath:_tempPath maxTimeInterval:config.maxTempCacheTimeInterval maxSize:config.maxTempCacheSize];
218 | }
219 |
220 | - (void)resetFinalCacheWitchConfig:(ShortMediaCacheConfig *)config {
221 | [self resetCacheWithPath:_finalPath maxTimeInterval:config.maxFinalCacheTimeInterval maxSize:config.maxFinalCacheSize];
222 | }
223 |
224 | - (void)resetCacheWithPath:(NSString *)path
225 | maxTimeInterval:(NSInteger)timeInterval
226 | maxSize:(NSInteger)maxSize {
227 | NSFileManager *fileManager = [[NSFileManager alloc] init];
228 | NSURL *cacheUrl = [NSURL fileURLWithPath:path isDirectory:YES];
229 | NSArray *resourceKeys = @[NSURLIsDirectoryKey, NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey];
230 | NSDirectoryEnumerator *fileEnumerator = [fileManager enumeratorAtURL:cacheUrl
231 | includingPropertiesForKeys:resourceKeys
232 | options:NSDirectoryEnumerationSkipsHiddenFiles
233 | errorHandler:NULL];
234 |
235 | NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-timeInterval];
236 | NSMutableDictionary *> *remainningFiles = [NSMutableDictionary dictionary];
237 | NSUInteger remainingSize = 0;
238 |
239 | NSMutableArray *urlsToDelete = [[NSMutableArray alloc] init];
240 |
241 | for (NSURL *fileURL in fileEnumerator) {
242 | @autoreleasepool {
243 | NSError *error;
244 | NSDictionary *resourceValues = [fileURL resourceValuesForKeys:resourceKeys error:&error];
245 | if (error || !resourceValues || [resourceValues[NSURLIsDirectoryKey] boolValue]) {
246 | continue;
247 | }
248 | NSDate *modificationDate = resourceValues[NSURLContentModificationDateKey];
249 | if ([[modificationDate laterDate:expirationDate] isEqualToDate:expirationDate]) {
250 | [urlsToDelete addObject:fileURL];
251 | continue;
252 | }
253 | NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
254 | remainingSize += totalAllocatedSize.unsignedIntegerValue;
255 | remainningFiles[fileURL] = resourceValues;
256 | }
257 | }
258 |
259 | for (NSURL *fileURL in urlsToDelete) {
260 | NSString *trashPath = [_trashPath stringByAppendingPathComponent:fileURL.lastPathComponent];
261 | [fileManager moveItemAtPath:fileURL.path toPath:trashPath error:nil];
262 | }
263 |
264 | if (maxSize > 0 && remainingSize > maxSize) {
265 | const NSUInteger targteCacheSize = maxSize / 2;
266 | NSArray *sortedFiles = [remainningFiles keysSortedByValueWithOptions:NSSortConcurrent
267 | usingComparator:^NSComparisonResult(id obj1, id obj2) {
268 | return [obj1[NSURLContentModificationDateKey] compare:obj2[NSURLContentModificationDateKey]];
269 | }];
270 |
271 | for (NSURL *fileURL in sortedFiles) {
272 | NSString *trashPath = [_trashPath stringByAppendingPathComponent:fileURL.lastPathComponent];
273 | if([fileManager moveItemAtPath:fileURL.path toPath:trashPath error:nil]) {
274 | NSDictionary *resourceValues = remainningFiles[fileURL];
275 | NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
276 | remainingSize -= totalAllocatedSize.unsignedIntegerValue;
277 |
278 | if (remainingSize < targteCacheSize) {
279 | break;
280 | }
281 | }
282 | }
283 | }
284 | [self cleanTrash];
285 | }
286 |
287 | @end
288 |
--------------------------------------------------------------------------------
/ShortMediaCacheDemo.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 53140E26212570EF0086B96A /* ShortMediaCacheConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 53140E25212570EF0086B96A /* ShortMediaCacheConfig.m */; };
11 | 5382708F2119870D0049A100 /* ShortMediaWeakProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 5382708E2119870C0049A100 /* ShortMediaWeakProxy.m */; };
12 | 5382709221199A8D0049A100 /* ShortMediaDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 5382709121199A8D0049A100 /* ShortMediaDownloader.m */; };
13 | 53827095211BE1AD0049A100 /* PlayerCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 53827094211BE1AD0049A100 /* PlayerCell.m */; };
14 | 53827098211C673F0049A100 /* AVAssetResourceLoadingDataRequest+ShortMediaCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 53827097211C673F0049A100 /* AVAssetResourceLoadingDataRequest+ShortMediaCache.m */; };
15 | 53BF338E210F0A4B00DB23DD /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 53BF338D210F0A4B00DB23DD /* AppDelegate.m */; };
16 | 53BF3391210F0A4C00DB23DD /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 53BF3390210F0A4C00DB23DD /* ViewController.m */; };
17 | 53BF3394210F0A4C00DB23DD /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 53BF3392210F0A4C00DB23DD /* Main.storyboard */; };
18 | 53BF3396210F0A4D00DB23DD /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 53BF3395210F0A4D00DB23DD /* Assets.xcassets */; };
19 | 53BF3399210F0A4D00DB23DD /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 53BF3397210F0A4D00DB23DD /* LaunchScreen.storyboard */; };
20 | 53BF339C210F0A4D00DB23DD /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 53BF339B210F0A4D00DB23DD /* main.m */; };
21 | 53BF33A5210F0FD200DB23DD /* ShortMediaResourceLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 53BF33A4210F0FD200DB23DD /* ShortMediaResourceLoader.m */; };
22 | 53BF33A8210F1A4C00DB23DD /* ShortMediaDownloadOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 53BF33A7210F1A4C00DB23DD /* ShortMediaDownloadOperation.m */; };
23 | 53BF33AB210F1E0600DB23DD /* ShortMediaCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 53BF33AA210F1E0600DB23DD /* ShortMediaCache.m */; };
24 | 53BF33AE210FF77A00DB23DD /* ShortMediaManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 53BF33AD210FF77A00DB23DD /* ShortMediaManager.m */; };
25 | 53BF33B12110024B00DB23DD /* ShortMediaDiskCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 53BF33B02110024B00DB23DD /* ShortMediaDiskCache.m */; };
26 | /* End PBXBuildFile section */
27 |
28 | /* Begin PBXFileReference section */
29 | 53140E24212570EF0086B96A /* ShortMediaCacheConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ShortMediaCacheConfig.h; sourceTree = ""; };
30 | 53140E25212570EF0086B96A /* ShortMediaCacheConfig.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ShortMediaCacheConfig.m; sourceTree = ""; };
31 | 5382708D2119870C0049A100 /* ShortMediaWeakProxy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ShortMediaWeakProxy.h; sourceTree = ""; };
32 | 5382708E2119870C0049A100 /* ShortMediaWeakProxy.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ShortMediaWeakProxy.m; sourceTree = ""; };
33 | 5382709021199A8D0049A100 /* ShortMediaDownloader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ShortMediaDownloader.h; sourceTree = ""; };
34 | 5382709121199A8D0049A100 /* ShortMediaDownloader.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ShortMediaDownloader.m; sourceTree = ""; };
35 | 53827093211BE1AD0049A100 /* PlayerCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PlayerCell.h; sourceTree = ""; };
36 | 53827094211BE1AD0049A100 /* PlayerCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PlayerCell.m; sourceTree = ""; };
37 | 53827096211C673F0049A100 /* AVAssetResourceLoadingDataRequest+ShortMediaCache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "AVAssetResourceLoadingDataRequest+ShortMediaCache.h"; sourceTree = ""; };
38 | 53827097211C673F0049A100 /* AVAssetResourceLoadingDataRequest+ShortMediaCache.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "AVAssetResourceLoadingDataRequest+ShortMediaCache.m"; sourceTree = ""; };
39 | 53BF3389210F0A4B00DB23DD /* ShortMediaCacheDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ShortMediaCacheDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
40 | 53BF338C210F0A4B00DB23DD /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
41 | 53BF338D210F0A4B00DB23DD /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
42 | 53BF338F210F0A4B00DB23DD /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; };
43 | 53BF3390210F0A4C00DB23DD /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; };
44 | 53BF3393210F0A4C00DB23DD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
45 | 53BF3395210F0A4D00DB23DD /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
46 | 53BF3398210F0A4D00DB23DD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
47 | 53BF339A210F0A4D00DB23DD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
48 | 53BF339B210F0A4D00DB23DD /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
49 | 53BF33A3210F0FD200DB23DD /* ShortMediaResourceLoader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ShortMediaResourceLoader.h; sourceTree = ""; };
50 | 53BF33A4210F0FD200DB23DD /* ShortMediaResourceLoader.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ShortMediaResourceLoader.m; sourceTree = ""; };
51 | 53BF33A6210F1A4C00DB23DD /* ShortMediaDownloadOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ShortMediaDownloadOperation.h; sourceTree = ""; };
52 | 53BF33A7210F1A4C00DB23DD /* ShortMediaDownloadOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ShortMediaDownloadOperation.m; sourceTree = ""; };
53 | 53BF33A9210F1E0600DB23DD /* ShortMediaCache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ShortMediaCache.h; sourceTree = ""; };
54 | 53BF33AA210F1E0600DB23DD /* ShortMediaCache.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ShortMediaCache.m; sourceTree = ""; };
55 | 53BF33AC210FF77A00DB23DD /* ShortMediaManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ShortMediaManager.h; sourceTree = ""; };
56 | 53BF33AD210FF77A00DB23DD /* ShortMediaManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ShortMediaManager.m; sourceTree = ""; };
57 | 53BF33AF2110024B00DB23DD /* ShortMediaDiskCache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ShortMediaDiskCache.h; sourceTree = ""; };
58 | 53BF33B02110024B00DB23DD /* ShortMediaDiskCache.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ShortMediaDiskCache.m; sourceTree = ""; };
59 | /* End PBXFileReference section */
60 |
61 | /* Begin PBXFrameworksBuildPhase section */
62 | 53BF3386210F0A4B00DB23DD /* Frameworks */ = {
63 | isa = PBXFrameworksBuildPhase;
64 | buildActionMask = 2147483647;
65 | files = (
66 | );
67 | runOnlyForDeploymentPostprocessing = 0;
68 | };
69 | /* End PBXFrameworksBuildPhase section */
70 |
71 | /* Begin PBXGroup section */
72 | 53BF3380210F0A4B00DB23DD = {
73 | isa = PBXGroup;
74 | children = (
75 | 53BF33A2210F0D2A00DB23DD /* ShortMediaCache */,
76 | 53BF338B210F0A4B00DB23DD /* ShortMediaCacheDemo */,
77 | 53BF338A210F0A4B00DB23DD /* Products */,
78 | );
79 | sourceTree = "";
80 | };
81 | 53BF338A210F0A4B00DB23DD /* Products */ = {
82 | isa = PBXGroup;
83 | children = (
84 | 53BF3389210F0A4B00DB23DD /* ShortMediaCacheDemo.app */,
85 | );
86 | name = Products;
87 | sourceTree = "";
88 | };
89 | 53BF338B210F0A4B00DB23DD /* ShortMediaCacheDemo */ = {
90 | isa = PBXGroup;
91 | children = (
92 | 53BF338C210F0A4B00DB23DD /* AppDelegate.h */,
93 | 53BF338D210F0A4B00DB23DD /* AppDelegate.m */,
94 | 53BF338F210F0A4B00DB23DD /* ViewController.h */,
95 | 53BF3390210F0A4C00DB23DD /* ViewController.m */,
96 | 53827093211BE1AD0049A100 /* PlayerCell.h */,
97 | 53827094211BE1AD0049A100 /* PlayerCell.m */,
98 | 53BF3392210F0A4C00DB23DD /* Main.storyboard */,
99 | 53BF3395210F0A4D00DB23DD /* Assets.xcassets */,
100 | 53BF3397210F0A4D00DB23DD /* LaunchScreen.storyboard */,
101 | 53BF339A210F0A4D00DB23DD /* Info.plist */,
102 | 53BF339B210F0A4D00DB23DD /* main.m */,
103 | );
104 | path = ShortMediaCacheDemo;
105 | sourceTree = "";
106 | };
107 | 53BF33A2210F0D2A00DB23DD /* ShortMediaCache */ = {
108 | isa = PBXGroup;
109 | children = (
110 | 53BF33A3210F0FD200DB23DD /* ShortMediaResourceLoader.h */,
111 | 53BF33A4210F0FD200DB23DD /* ShortMediaResourceLoader.m */,
112 | 53BF33AC210FF77A00DB23DD /* ShortMediaManager.h */,
113 | 53BF33AD210FF77A00DB23DD /* ShortMediaManager.m */,
114 | 53BF33A9210F1E0600DB23DD /* ShortMediaCache.h */,
115 | 53BF33AA210F1E0600DB23DD /* ShortMediaCache.m */,
116 | 53BF33AF2110024B00DB23DD /* ShortMediaDiskCache.h */,
117 | 53BF33B02110024B00DB23DD /* ShortMediaDiskCache.m */,
118 | 5382709021199A8D0049A100 /* ShortMediaDownloader.h */,
119 | 5382709121199A8D0049A100 /* ShortMediaDownloader.m */,
120 | 53BF33A6210F1A4C00DB23DD /* ShortMediaDownloadOperation.h */,
121 | 53BF33A7210F1A4C00DB23DD /* ShortMediaDownloadOperation.m */,
122 | 5382708D2119870C0049A100 /* ShortMediaWeakProxy.h */,
123 | 5382708E2119870C0049A100 /* ShortMediaWeakProxy.m */,
124 | 53827096211C673F0049A100 /* AVAssetResourceLoadingDataRequest+ShortMediaCache.h */,
125 | 53827097211C673F0049A100 /* AVAssetResourceLoadingDataRequest+ShortMediaCache.m */,
126 | 53140E24212570EF0086B96A /* ShortMediaCacheConfig.h */,
127 | 53140E25212570EF0086B96A /* ShortMediaCacheConfig.m */,
128 | );
129 | path = ShortMediaCache;
130 | sourceTree = "";
131 | };
132 | /* End PBXGroup section */
133 |
134 | /* Begin PBXNativeTarget section */
135 | 53BF3388210F0A4B00DB23DD /* ShortMediaCacheDemo */ = {
136 | isa = PBXNativeTarget;
137 | buildConfigurationList = 53BF339F210F0A4D00DB23DD /* Build configuration list for PBXNativeTarget "ShortMediaCacheDemo" */;
138 | buildPhases = (
139 | 53BF3385210F0A4B00DB23DD /* Sources */,
140 | 53BF3386210F0A4B00DB23DD /* Frameworks */,
141 | 53BF3387210F0A4B00DB23DD /* Resources */,
142 | );
143 | buildRules = (
144 | );
145 | dependencies = (
146 | );
147 | name = ShortMediaCacheDemo;
148 | productName = ShortMediaCacheDemo;
149 | productReference = 53BF3389210F0A4B00DB23DD /* ShortMediaCacheDemo.app */;
150 | productType = "com.apple.product-type.application";
151 | };
152 | /* End PBXNativeTarget section */
153 |
154 | /* Begin PBXProject section */
155 | 53BF3381210F0A4B00DB23DD /* Project object */ = {
156 | isa = PBXProject;
157 | attributes = {
158 | LastUpgradeCheck = 0930;
159 | ORGANIZATIONNAME = DandJ;
160 | TargetAttributes = {
161 | 53BF3388210F0A4B00DB23DD = {
162 | CreatedOnToolsVersion = 9.3;
163 | };
164 | };
165 | };
166 | buildConfigurationList = 53BF3384210F0A4B00DB23DD /* Build configuration list for PBXProject "ShortMediaCacheDemo" */;
167 | compatibilityVersion = "Xcode 9.3";
168 | developmentRegion = en;
169 | hasScannedForEncodings = 0;
170 | knownRegions = (
171 | en,
172 | Base,
173 | );
174 | mainGroup = 53BF3380210F0A4B00DB23DD;
175 | productRefGroup = 53BF338A210F0A4B00DB23DD /* Products */;
176 | projectDirPath = "";
177 | projectRoot = "";
178 | targets = (
179 | 53BF3388210F0A4B00DB23DD /* ShortMediaCacheDemo */,
180 | );
181 | };
182 | /* End PBXProject section */
183 |
184 | /* Begin PBXResourcesBuildPhase section */
185 | 53BF3387210F0A4B00DB23DD /* Resources */ = {
186 | isa = PBXResourcesBuildPhase;
187 | buildActionMask = 2147483647;
188 | files = (
189 | 53BF3399210F0A4D00DB23DD /* LaunchScreen.storyboard in Resources */,
190 | 53BF3396210F0A4D00DB23DD /* Assets.xcassets in Resources */,
191 | 53BF3394210F0A4C00DB23DD /* Main.storyboard in Resources */,
192 | );
193 | runOnlyForDeploymentPostprocessing = 0;
194 | };
195 | /* End PBXResourcesBuildPhase section */
196 |
197 | /* Begin PBXSourcesBuildPhase section */
198 | 53BF3385210F0A4B00DB23DD /* Sources */ = {
199 | isa = PBXSourcesBuildPhase;
200 | buildActionMask = 2147483647;
201 | files = (
202 | 53827095211BE1AD0049A100 /* PlayerCell.m in Sources */,
203 | 53BF33AB210F1E0600DB23DD /* ShortMediaCache.m in Sources */,
204 | 53BF33A5210F0FD200DB23DD /* ShortMediaResourceLoader.m in Sources */,
205 | 5382709221199A8D0049A100 /* ShortMediaDownloader.m in Sources */,
206 | 53140E26212570EF0086B96A /* ShortMediaCacheConfig.m in Sources */,
207 | 53BF33AE210FF77A00DB23DD /* ShortMediaManager.m in Sources */,
208 | 53BF3391210F0A4C00DB23DD /* ViewController.m in Sources */,
209 | 53BF33A8210F1A4C00DB23DD /* ShortMediaDownloadOperation.m in Sources */,
210 | 53BF339C210F0A4D00DB23DD /* main.m in Sources */,
211 | 53BF338E210F0A4B00DB23DD /* AppDelegate.m in Sources */,
212 | 53827098211C673F0049A100 /* AVAssetResourceLoadingDataRequest+ShortMediaCache.m in Sources */,
213 | 53BF33B12110024B00DB23DD /* ShortMediaDiskCache.m in Sources */,
214 | 5382708F2119870D0049A100 /* ShortMediaWeakProxy.m in Sources */,
215 | );
216 | runOnlyForDeploymentPostprocessing = 0;
217 | };
218 | /* End PBXSourcesBuildPhase section */
219 |
220 | /* Begin PBXVariantGroup section */
221 | 53BF3392210F0A4C00DB23DD /* Main.storyboard */ = {
222 | isa = PBXVariantGroup;
223 | children = (
224 | 53BF3393210F0A4C00DB23DD /* Base */,
225 | );
226 | name = Main.storyboard;
227 | sourceTree = "";
228 | };
229 | 53BF3397210F0A4D00DB23DD /* LaunchScreen.storyboard */ = {
230 | isa = PBXVariantGroup;
231 | children = (
232 | 53BF3398210F0A4D00DB23DD /* Base */,
233 | );
234 | name = LaunchScreen.storyboard;
235 | sourceTree = "";
236 | };
237 | /* End PBXVariantGroup section */
238 |
239 | /* Begin XCBuildConfiguration section */
240 | 53BF339D210F0A4D00DB23DD /* Debug */ = {
241 | isa = XCBuildConfiguration;
242 | buildSettings = {
243 | ALWAYS_SEARCH_USER_PATHS = NO;
244 | CLANG_ANALYZER_NONNULL = YES;
245 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
246 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
247 | CLANG_CXX_LIBRARY = "libc++";
248 | CLANG_ENABLE_MODULES = YES;
249 | CLANG_ENABLE_OBJC_ARC = YES;
250 | CLANG_ENABLE_OBJC_WEAK = YES;
251 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
252 | CLANG_WARN_BOOL_CONVERSION = YES;
253 | CLANG_WARN_COMMA = YES;
254 | CLANG_WARN_CONSTANT_CONVERSION = YES;
255 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
256 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
257 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
258 | CLANG_WARN_EMPTY_BODY = YES;
259 | CLANG_WARN_ENUM_CONVERSION = YES;
260 | CLANG_WARN_INFINITE_RECURSION = YES;
261 | CLANG_WARN_INT_CONVERSION = YES;
262 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
263 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
264 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
265 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
266 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
267 | CLANG_WARN_STRICT_PROTOTYPES = YES;
268 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
269 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
270 | CLANG_WARN_UNREACHABLE_CODE = YES;
271 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
272 | CODE_SIGN_IDENTITY = "iPhone Developer";
273 | COPY_PHASE_STRIP = NO;
274 | DEBUG_INFORMATION_FORMAT = dwarf;
275 | ENABLE_STRICT_OBJC_MSGSEND = YES;
276 | ENABLE_TESTABILITY = YES;
277 | GCC_C_LANGUAGE_STANDARD = gnu11;
278 | GCC_DYNAMIC_NO_PIC = NO;
279 | GCC_NO_COMMON_BLOCKS = YES;
280 | GCC_OPTIMIZATION_LEVEL = 0;
281 | GCC_PREPROCESSOR_DEFINITIONS = (
282 | "DEBUG=1",
283 | "$(inherited)",
284 | );
285 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
286 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
287 | GCC_WARN_UNDECLARED_SELECTOR = YES;
288 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
289 | GCC_WARN_UNUSED_FUNCTION = YES;
290 | GCC_WARN_UNUSED_VARIABLE = YES;
291 | IPHONEOS_DEPLOYMENT_TARGET = 11.3;
292 | MTL_ENABLE_DEBUG_INFO = YES;
293 | ONLY_ACTIVE_ARCH = YES;
294 | SDKROOT = iphoneos;
295 | };
296 | name = Debug;
297 | };
298 | 53BF339E210F0A4D00DB23DD /* Release */ = {
299 | isa = XCBuildConfiguration;
300 | buildSettings = {
301 | ALWAYS_SEARCH_USER_PATHS = NO;
302 | CLANG_ANALYZER_NONNULL = YES;
303 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
304 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
305 | CLANG_CXX_LIBRARY = "libc++";
306 | CLANG_ENABLE_MODULES = YES;
307 | CLANG_ENABLE_OBJC_ARC = YES;
308 | CLANG_ENABLE_OBJC_WEAK = YES;
309 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
310 | CLANG_WARN_BOOL_CONVERSION = YES;
311 | CLANG_WARN_COMMA = YES;
312 | CLANG_WARN_CONSTANT_CONVERSION = YES;
313 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
314 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
315 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
316 | CLANG_WARN_EMPTY_BODY = YES;
317 | CLANG_WARN_ENUM_CONVERSION = YES;
318 | CLANG_WARN_INFINITE_RECURSION = YES;
319 | CLANG_WARN_INT_CONVERSION = YES;
320 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
321 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
322 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
323 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
324 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
325 | CLANG_WARN_STRICT_PROTOTYPES = YES;
326 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
327 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
328 | CLANG_WARN_UNREACHABLE_CODE = YES;
329 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
330 | CODE_SIGN_IDENTITY = "iPhone Developer";
331 | COPY_PHASE_STRIP = NO;
332 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
333 | ENABLE_NS_ASSERTIONS = NO;
334 | ENABLE_STRICT_OBJC_MSGSEND = YES;
335 | GCC_C_LANGUAGE_STANDARD = gnu11;
336 | GCC_NO_COMMON_BLOCKS = YES;
337 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
338 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
339 | GCC_WARN_UNDECLARED_SELECTOR = YES;
340 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
341 | GCC_WARN_UNUSED_FUNCTION = YES;
342 | GCC_WARN_UNUSED_VARIABLE = YES;
343 | IPHONEOS_DEPLOYMENT_TARGET = 11.3;
344 | MTL_ENABLE_DEBUG_INFO = NO;
345 | SDKROOT = iphoneos;
346 | VALIDATE_PRODUCT = YES;
347 | };
348 | name = Release;
349 | };
350 | 53BF33A0210F0A4D00DB23DD /* Debug */ = {
351 | isa = XCBuildConfiguration;
352 | buildSettings = {
353 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
354 | CODE_SIGN_STYLE = Automatic;
355 | DEVELOPMENT_TEAM = DTCHC66PTA;
356 | INFOPLIST_FILE = ShortMediaCacheDemo/Info.plist;
357 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
358 | LD_RUNPATH_SEARCH_PATHS = (
359 | "$(inherited)",
360 | "@executable_path/Frameworks",
361 | );
362 | PRODUCT_BUNDLE_IDENTIFIER = com.DandJ.ShortMediaCacheDemo;
363 | PRODUCT_NAME = "$(TARGET_NAME)";
364 | TARGETED_DEVICE_FAMILY = "1,2";
365 | };
366 | name = Debug;
367 | };
368 | 53BF33A1210F0A4D00DB23DD /* Release */ = {
369 | isa = XCBuildConfiguration;
370 | buildSettings = {
371 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
372 | CODE_SIGN_STYLE = Automatic;
373 | DEVELOPMENT_TEAM = DTCHC66PTA;
374 | INFOPLIST_FILE = ShortMediaCacheDemo/Info.plist;
375 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
376 | LD_RUNPATH_SEARCH_PATHS = (
377 | "$(inherited)",
378 | "@executable_path/Frameworks",
379 | );
380 | PRODUCT_BUNDLE_IDENTIFIER = com.DandJ.ShortMediaCacheDemo;
381 | PRODUCT_NAME = "$(TARGET_NAME)";
382 | TARGETED_DEVICE_FAMILY = "1,2";
383 | };
384 | name = Release;
385 | };
386 | /* End XCBuildConfiguration section */
387 |
388 | /* Begin XCConfigurationList section */
389 | 53BF3384210F0A4B00DB23DD /* Build configuration list for PBXProject "ShortMediaCacheDemo" */ = {
390 | isa = XCConfigurationList;
391 | buildConfigurations = (
392 | 53BF339D210F0A4D00DB23DD /* Debug */,
393 | 53BF339E210F0A4D00DB23DD /* Release */,
394 | );
395 | defaultConfigurationIsVisible = 0;
396 | defaultConfigurationName = Release;
397 | };
398 | 53BF339F210F0A4D00DB23DD /* Build configuration list for PBXNativeTarget "ShortMediaCacheDemo" */ = {
399 | isa = XCConfigurationList;
400 | buildConfigurations = (
401 | 53BF33A0210F0A4D00DB23DD /* Debug */,
402 | 53BF33A1210F0A4D00DB23DD /* Release */,
403 | );
404 | defaultConfigurationIsVisible = 0;
405 | defaultConfigurationName = Release;
406 | };
407 | /* End XCConfigurationList section */
408 | };
409 | rootObject = 53BF3381210F0A4B00DB23DD /* Project object */;
410 | }
411 |
--------------------------------------------------------------------------------