├── HJDownloadManager.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ └── whj.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
└── xcuserdata
│ └── whj.xcuserdatad
│ ├── xcdebugger
│ └── Breakpoints_v2.xcbkptlist
│ └── xcschemes
│ └── xcschememanagement.plist
├── HJDownloadManager.xcworkspace
├── contents.xcworkspacedata
└── xcuserdata
│ └── whj.xcuserdatad
│ ├── UserInterfaceState.xcuserstate
│ └── xcdebugger
│ └── Breakpoints_v2.xcbkptlist
├── HJDownloadManager
├── AppDelegate.h
├── AppDelegate.m
├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ ├── Contents.json
│ └── HJDownloadIcon
│ │ ├── Contents.json
│ │ ├── HJ_download_fail.imageset
│ │ ├── Contents.json
│ │ ├── HJ_download_fail@2x.png
│ │ └── HJ_download_fail@3x.png
│ │ ├── HJ_download_finished.imageset
│ │ ├── Contents.json
│ │ ├── HJ_download_finished@2x.png
│ │ └── HJ_download_finished@3x.png
│ │ ├── HJ_download_pause.imageset
│ │ ├── Contents.json
│ │ ├── HJ_download_pause@2x.png
│ │ └── HJ_download_pause@3x.png
│ │ ├── HJ_download_ready.imageset
│ │ ├── Contents.json
│ │ ├── HJ_download_ready@2x.png
│ │ └── HJ_download_ready@3x.png
│ │ └── HJ_download_waiting.imageset
│ │ ├── Contents.json
│ │ ├── HJ_download_waiting@2x.png
│ │ └── HJ_download_waiting@3x.png
├── ExampleDemo
│ ├── Controllers
│ │ ├── HJDownloadHome_VC.h
│ │ ├── HJDownloadHome_VC.m
│ │ ├── HJDownloadList_VC.h
│ │ ├── HJDownloadList_VC.m
│ │ ├── HJDownloadTabBar_VC.h
│ │ └── HJDownloadTabBar_VC.m
│ ├── Models
│ │ ├── HJExampleModel.h
│ │ └── HJExampleModel.m
│ └── Views
│ │ ├── HJBaseDownloadCell.h
│ │ ├── HJBaseDownloadCell.m
│ │ ├── HJDownloadHomeCell.h
│ │ ├── HJDownloadHomeCell.m
│ │ ├── HJDownloadListCell.h
│ │ └── HJDownloadListCell.m
├── HJDownloadClass
│ ├── HJDownloadConst.h
│ ├── HJDownloadHeaders.h
│ ├── HJDownloadManager.h
│ ├── HJDownloadManager.m
│ ├── HJDownloadModel.h
│ ├── HJDownloadModel.m
│ ├── HJDownloadOperation.h
│ ├── HJDownloadOperation.m
│ ├── HJUncaughtExceptionHandler.h
│ ├── HJUncaughtExceptionHandler.m
│ ├── NSURLSessionTask+HJModel.h
│ └── NSURLSessionTask+HJModel.m
├── Info.plist
└── main.m
├── HJDownloadManagerTests
├── HJDownloadManagerTests.m
└── Info.plist
├── HJDownloadManagerUITests
├── HJDownloadManagerUITests.m
└── Info.plist
├── Podfile.lock
├── Pods
├── Headers
│ ├── Private
│ │ └── MJExtension
│ │ │ ├── MJExtension.h
│ │ │ ├── MJExtensionConst.h
│ │ │ ├── MJFoundation.h
│ │ │ ├── MJProperty.h
│ │ │ ├── MJPropertyKey.h
│ │ │ ├── MJPropertyType.h
│ │ │ ├── NSObject+MJClass.h
│ │ │ ├── NSObject+MJCoding.h
│ │ │ ├── NSObject+MJKeyValue.h
│ │ │ ├── NSObject+MJProperty.h
│ │ │ └── NSString+MJExtension.h
│ └── Public
│ │ └── MJExtension
│ │ ├── MJExtension.h
│ │ ├── MJExtensionConst.h
│ │ ├── MJFoundation.h
│ │ ├── MJProperty.h
│ │ ├── MJPropertyKey.h
│ │ ├── MJPropertyType.h
│ │ ├── NSObject+MJClass.h
│ │ ├── NSObject+MJCoding.h
│ │ ├── NSObject+MJKeyValue.h
│ │ ├── NSObject+MJProperty.h
│ │ └── NSString+MJExtension.h
├── MJExtension
│ ├── LICENSE
│ ├── MJExtension
│ │ ├── MJExtension.h
│ │ ├── MJExtensionConst.h
│ │ ├── MJExtensionConst.m
│ │ ├── MJFoundation.h
│ │ ├── MJFoundation.m
│ │ ├── MJProperty.h
│ │ ├── MJProperty.m
│ │ ├── MJPropertyKey.h
│ │ ├── MJPropertyKey.m
│ │ ├── MJPropertyType.h
│ │ ├── MJPropertyType.m
│ │ ├── NSObject+MJClass.h
│ │ ├── NSObject+MJClass.m
│ │ ├── NSObject+MJCoding.h
│ │ ├── NSObject+MJCoding.m
│ │ ├── NSObject+MJKeyValue.h
│ │ ├── NSObject+MJKeyValue.m
│ │ ├── NSObject+MJProperty.h
│ │ ├── NSObject+MJProperty.m
│ │ ├── NSString+MJExtension.h
│ │ └── NSString+MJExtension.m
│ └── README.md
├── Manifest.lock
├── Pods.xcodeproj
│ ├── project.pbxproj
│ └── xcuserdata
│ │ └── whj.xcuserdatad
│ │ └── xcschemes
│ │ ├── MJExtension.xcscheme
│ │ ├── Pods-HJDownloadManager.xcscheme
│ │ └── xcschememanagement.plist
└── Target Support Files
│ ├── MJExtension
│ ├── MJExtension-dummy.m
│ ├── MJExtension-prefix.pch
│ └── MJExtension.xcconfig
│ └── Pods-HJDownloadManager
│ ├── Pods-HJDownloadManager-acknowledgements.markdown
│ ├── Pods-HJDownloadManager-acknowledgements.plist
│ ├── Pods-HJDownloadManager-dummy.m
│ ├── Pods-HJDownloadManager-frameworks.sh
│ ├── Pods-HJDownloadManager-resources.sh
│ ├── Pods-HJDownloadManager.debug.xcconfig
│ └── Pods-HJDownloadManager.release.xcconfig
├── README.md
└── podfile
/HJDownloadManager.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/HJDownloadManager.xcodeproj/project.xcworkspace/xcuserdata/whj.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WuHuijian/HJDownloadManager/54315c8791de5a16cdc2c1ad929e02b2c4845e06/HJDownloadManager.xcodeproj/project.xcworkspace/xcuserdata/whj.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/HJDownloadManager.xcodeproj/xcuserdata/whj.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
8 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/HJDownloadManager.xcodeproj/xcuserdata/whj.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | HJDownloadManager.xcscheme
8 |
9 | orderHint
10 | 2
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/HJDownloadManager.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/HJDownloadManager.xcworkspace/xcuserdata/whj.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WuHuijian/HJDownloadManager/54315c8791de5a16cdc2c1ad929e02b2c4845e06/HJDownloadManager.xcworkspace/xcuserdata/whj.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/HJDownloadManager.xcworkspace/xcuserdata/whj.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/HJDownloadManager/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // HJDownloadManager
4 | //
5 | // Created by WHJ on 2018/2/21.
6 | // Copyright © 2018年 WHJ. 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 |
--------------------------------------------------------------------------------
/HJDownloadManager/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // HJDownloadManager
4 | //
5 | // Created by WHJ on 2018/2/21.
6 | // Copyright © 2018年 WHJ. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 | #import "HJDownloadTabBar_VC.h"
11 | #import "HJDownloadManager.h"
12 | #import "HJUncaughtExceptionHandler.h"
13 |
14 | @interface AppDelegate ()
15 |
16 | @end
17 |
18 | @implementation AppDelegate
19 |
20 |
21 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
22 | // Override point for customization after application launch.
23 | self.window = [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];
24 | self.window.backgroundColor = [UIColor whiteColor];
25 | [self.window makeKeyAndVisible];
26 |
27 | HJDownloadTabBar_VC *tabBarVC = [[HJDownloadTabBar_VC alloc] init];
28 | self.window.rootViewController = tabBarVC;
29 |
30 | [[HJDownloadManager sharedManager] setMaxConcurrentOperationCount:3];
31 | [[HJDownloadManager sharedManager] enableProgressLog:NO];
32 |
33 |
34 |
35 | return YES;
36 | }
37 |
38 |
39 | - (void)applicationWillResignActive:(UIApplication *)application {
40 | // 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.
41 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
42 | }
43 |
44 |
45 | - (void)applicationDidEnterBackground:(UIApplication *)application {
46 | // 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.
47 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
48 | }
49 |
50 |
51 | - (void)applicationWillEnterForeground:(UIApplication *)application {
52 | // 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.
53 | }
54 |
55 |
56 | - (void)applicationDidBecomeActive:(UIApplication *)application {
57 | // 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.
58 | }
59 |
60 |
61 | - (void)applicationWillTerminate:(UIApplication *)application {
62 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
63 | }
64 |
65 |
66 | @end
67 |
--------------------------------------------------------------------------------
/HJDownloadManager/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 | }
--------------------------------------------------------------------------------
/HJDownloadManager/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/HJDownloadManager/Assets.xcassets/HJDownloadIcon/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/HJDownloadManager/Assets.xcassets/HJDownloadIcon/HJ_download_fail.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "HJ_download_fail@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "HJ_download_fail@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/HJDownloadManager/Assets.xcassets/HJDownloadIcon/HJ_download_fail.imageset/HJ_download_fail@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WuHuijian/HJDownloadManager/54315c8791de5a16cdc2c1ad929e02b2c4845e06/HJDownloadManager/Assets.xcassets/HJDownloadIcon/HJ_download_fail.imageset/HJ_download_fail@2x.png
--------------------------------------------------------------------------------
/HJDownloadManager/Assets.xcassets/HJDownloadIcon/HJ_download_fail.imageset/HJ_download_fail@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WuHuijian/HJDownloadManager/54315c8791de5a16cdc2c1ad929e02b2c4845e06/HJDownloadManager/Assets.xcassets/HJDownloadIcon/HJ_download_fail.imageset/HJ_download_fail@3x.png
--------------------------------------------------------------------------------
/HJDownloadManager/Assets.xcassets/HJDownloadIcon/HJ_download_finished.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "HJ_download_finished@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "HJ_download_finished@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/HJDownloadManager/Assets.xcassets/HJDownloadIcon/HJ_download_finished.imageset/HJ_download_finished@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WuHuijian/HJDownloadManager/54315c8791de5a16cdc2c1ad929e02b2c4845e06/HJDownloadManager/Assets.xcassets/HJDownloadIcon/HJ_download_finished.imageset/HJ_download_finished@2x.png
--------------------------------------------------------------------------------
/HJDownloadManager/Assets.xcassets/HJDownloadIcon/HJ_download_finished.imageset/HJ_download_finished@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WuHuijian/HJDownloadManager/54315c8791de5a16cdc2c1ad929e02b2c4845e06/HJDownloadManager/Assets.xcassets/HJDownloadIcon/HJ_download_finished.imageset/HJ_download_finished@3x.png
--------------------------------------------------------------------------------
/HJDownloadManager/Assets.xcassets/HJDownloadIcon/HJ_download_pause.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "HJ_download_pause@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "HJ_download_pause@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/HJDownloadManager/Assets.xcassets/HJDownloadIcon/HJ_download_pause.imageset/HJ_download_pause@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WuHuijian/HJDownloadManager/54315c8791de5a16cdc2c1ad929e02b2c4845e06/HJDownloadManager/Assets.xcassets/HJDownloadIcon/HJ_download_pause.imageset/HJ_download_pause@2x.png
--------------------------------------------------------------------------------
/HJDownloadManager/Assets.xcassets/HJDownloadIcon/HJ_download_pause.imageset/HJ_download_pause@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WuHuijian/HJDownloadManager/54315c8791de5a16cdc2c1ad929e02b2c4845e06/HJDownloadManager/Assets.xcassets/HJDownloadIcon/HJ_download_pause.imageset/HJ_download_pause@3x.png
--------------------------------------------------------------------------------
/HJDownloadManager/Assets.xcassets/HJDownloadIcon/HJ_download_ready.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "HJ_download_ready@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "HJ_download_ready@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/HJDownloadManager/Assets.xcassets/HJDownloadIcon/HJ_download_ready.imageset/HJ_download_ready@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WuHuijian/HJDownloadManager/54315c8791de5a16cdc2c1ad929e02b2c4845e06/HJDownloadManager/Assets.xcassets/HJDownloadIcon/HJ_download_ready.imageset/HJ_download_ready@2x.png
--------------------------------------------------------------------------------
/HJDownloadManager/Assets.xcassets/HJDownloadIcon/HJ_download_ready.imageset/HJ_download_ready@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WuHuijian/HJDownloadManager/54315c8791de5a16cdc2c1ad929e02b2c4845e06/HJDownloadManager/Assets.xcassets/HJDownloadIcon/HJ_download_ready.imageset/HJ_download_ready@3x.png
--------------------------------------------------------------------------------
/HJDownloadManager/Assets.xcassets/HJDownloadIcon/HJ_download_waiting.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "HJ_download_waiting@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "HJ_download_waiting@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/HJDownloadManager/Assets.xcassets/HJDownloadIcon/HJ_download_waiting.imageset/HJ_download_waiting@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WuHuijian/HJDownloadManager/54315c8791de5a16cdc2c1ad929e02b2c4845e06/HJDownloadManager/Assets.xcassets/HJDownloadIcon/HJ_download_waiting.imageset/HJ_download_waiting@2x.png
--------------------------------------------------------------------------------
/HJDownloadManager/Assets.xcassets/HJDownloadIcon/HJ_download_waiting.imageset/HJ_download_waiting@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WuHuijian/HJDownloadManager/54315c8791de5a16cdc2c1ad929e02b2c4845e06/HJDownloadManager/Assets.xcassets/HJDownloadIcon/HJ_download_waiting.imageset/HJ_download_waiting@3x.png
--------------------------------------------------------------------------------
/HJDownloadManager/ExampleDemo/Controllers/HJDownloadHome_VC.h:
--------------------------------------------------------------------------------
1 | //
2 | // HJDownloadHome_VC.h
3 | // HJDownloadManager
4 | //
5 | // Created by WHJ on 2018/2/27.
6 | // Copyright © 2018年 WHJ. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface HJDownloadHome_VC : UIViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/HJDownloadManager/ExampleDemo/Controllers/HJDownloadHome_VC.m:
--------------------------------------------------------------------------------
1 | //
2 | // HJDownloadHome_VC.m
3 | // HJDownloadManager
4 | //
5 | // Created by WHJ on 2018/2/27.
6 | // Copyright © 2018年 WHJ. All rights reserved.
7 | //
8 |
9 | #import "HJDownloadHome_VC.h"
10 | #import "HJDownloadHomeCell.h"
11 | #import "HJExampleModel.h"
12 | #import "HJDownloadManager.h"
13 |
14 | @interface HJDownloadHome_VC ()
15 |
16 | @property (nonatomic, strong) UITableView *homeTable;
17 |
18 | @property (nonatomic, strong) NSMutableArray *datas;
19 |
20 | @property (nonatomic, strong) UIButton *tableHeader;
21 |
22 | @end
23 |
24 | static NSString * const kHJHomeTableCellID = @"HJHomeTableCellIdentifier";
25 |
26 | @implementation HJDownloadHome_VC
27 | #pragma mark - Life Circle
28 | - (void)viewDidLoad {
29 | [super viewDidLoad];
30 |
31 | [self buildData];
32 |
33 | [self setupUI];
34 | }
35 | - (void)didReceiveMemoryWarning {
36 | [super didReceiveMemoryWarning];
37 | }
38 |
39 | - (void)buildData{
40 |
41 | NSArray *urls = @[@"http://sw.bos.baidu.com/sw-search-sp/software/4ea1aa9dfac30/QQ_mac_6.2.1.dmg",
42 | @"http://sw.bos.baidu.com/sw-search-sp/software/034dd6ef7b5fd/QQMusicMac_5.3.1.dmg",
43 | @"http://sw.bos.baidu.com/sw-search-sp/software/5a442685f4f80/kwplayer_mac_1.4.0.pkg",
44 | @"http://sw.bos.baidu.com/sw-search-sp/software/5e77ab765868f/NeteaseMusic_mac_1.5.9.622.dmg",
45 | @"http://sw.bos.baidu.com/sw-search-sp/software/b3282eadef1fd/Kugou_mac_2.0.2.dmg"];
46 | NSArray *names = @[@"QQ.dmg",@"QQ音乐.dmg",@"酷我音乐.dmg",@"网易云.dmg",@"酷狗音乐.dmg"];
47 |
48 | NSMutableArray *datas = [NSMutableArray arrayWithCapacity:urls.count];
49 | self.datas = datas;
50 | [urls enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
51 | HJExampleModel *model = [[HJExampleModel alloc] init];
52 | model.url = obj;
53 | model.name = names[idx];
54 | [datas addObject:model];
55 | }];
56 |
57 |
58 | }
59 |
60 | - (void)viewWillAppear:(BOOL)animated{
61 | [super viewWillAppear:animated];
62 | if(!self.datas){
63 | [self buildData];
64 | }
65 | [self.homeTable reloadData];
66 | }
67 |
68 | #pragma mark - About UI
69 | - (void)setupUI{
70 |
71 | UITableView *tableView = [[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStylePlain];
72 | tableView.delegate = self;
73 | tableView.dataSource = self;
74 | tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
75 | tableView.estimatedRowHeight = 0.f;
76 | tableView.estimatedSectionHeaderHeight = 0.f;
77 | tableView.estimatedSectionFooterHeight = 0.f;
78 | [tableView registerClass:[HJDownloadHomeCell class] forCellReuseIdentifier:kHJHomeTableCellID];
79 | [self.view addSubview:tableView];
80 |
81 | self.homeTable = tableView;
82 | self.homeTable.tableHeaderView = self.tableHeader;
83 |
84 |
85 | UIButton * monitorBtn = [UIButton buttonWithType:UIButtonTypeCustom];
86 | [monitorBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
87 | monitorBtn.frame = CGRectMake(0, 0, 80, 44);
88 | monitorBtn.titleLabel.font = [UIFont systemFontOfSize:16.f];
89 | [monitorBtn setTitle:@"闪退模拟" forState:UIControlStateNormal];
90 | monitorBtn.contentHorizontalAlignment = UIControlContentHorizontalAlignmentRight;
91 | [monitorBtn addTarget:self action:@selector(monitorFlashAction) forControlEvents:UIControlEventTouchUpInside];
92 | UIBarButtonItem *rightBarItem = [[UIBarButtonItem alloc] initWithCustomView:monitorBtn];
93 | self.navigationItem.rightBarButtonItem = rightBarItem;
94 | }
95 |
96 | #pragma mark - Request Data
97 |
98 | #pragma mark - Pravite Method
99 |
100 | #pragma mark - Public Method
101 |
102 | #pragma mark - Event response
103 | - (void)downloadAll{
104 |
105 | NSMutableArray *models = [NSMutableArray arrayWithCapacity:self.datas.count];
106 | [self.datas enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
107 | HJExampleModel *exampleModel = obj;
108 | HJDownloadModel *model = [[HJDownloadModel alloc] init];
109 | model.urlString = exampleModel.url;
110 | model.downloadDesc = exampleModel.name;
111 | [models addObject:model];
112 | }];
113 |
114 | [kHJDownloadManager startWithDownloadModels:models];
115 | //下载任务不是从cell中添加,所以需要刷新列表
116 | [self.homeTable reloadData];
117 | }
118 |
119 | - (void)monitorFlashAction{
120 |
121 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
122 | NSArray *arr = @[];
123 | NSLog(@"%@",arr[1]);
124 | });
125 | }
126 | #pragma mark - Delegate methods
127 |
128 | #pragma mark - UITableViewDataSource
129 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
130 |
131 | return self.datas.count;
132 | }
133 |
134 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
135 |
136 | HJDownloadHomeCell *cell = [tableView dequeueReusableCellWithIdentifier:kHJHomeTableCellID];
137 | cell.model = self.datas[indexPath.row];
138 | return cell;
139 | }
140 |
141 |
142 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
143 |
144 | return 1;
145 | }
146 |
147 |
148 | #pragma mark - UITableViewDelegate
149 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
150 |
151 | }
152 |
153 |
154 | - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
155 |
156 | return nil;
157 | }
158 |
159 |
160 | - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section{
161 | return nil;
162 | }
163 |
164 |
165 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
166 | return [HJDownloadHomeCell backCellHeight];
167 | }
168 |
169 | - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
170 |
171 | return 0;
172 | }
173 |
174 | - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{
175 |
176 | return 0.001f;
177 | }
178 |
179 |
180 | #pragma mark - Getters/Setters/Lazy
181 | - (UIButton *)tableHeader{
182 |
183 | if (!_tableHeader) {
184 | _tableHeader = [UIButton buttonWithType:UIButtonTypeSystem];
185 | [_tableHeader setFrame:CGRectMake(0, 0, 0, 44)];
186 | [_tableHeader addTarget:self action:@selector(downloadAll) forControlEvents:UIControlEventTouchUpInside];
187 | [_tableHeader setTitle:@"下载全部" forState:UIControlStateNormal];
188 | [_tableHeader setBackgroundColor:[UIColor lightGrayColor]];
189 | }
190 | return _tableHeader;
191 | }
192 | @end
193 |
--------------------------------------------------------------------------------
/HJDownloadManager/ExampleDemo/Controllers/HJDownloadList_VC.h:
--------------------------------------------------------------------------------
1 | //
2 | // HJDownloadList_VC.h
3 | // HJDownloadManager
4 | //
5 | // Created by WHJ on 2018/2/27.
6 | // Copyright © 2018年 WHJ. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface HJDownloadList_VC : UIViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/HJDownloadManager/ExampleDemo/Controllers/HJDownloadList_VC.m:
--------------------------------------------------------------------------------
1 | //
2 | // HJDownloadList_VC.m
3 | // HJDownloadManager
4 | //
5 | // Created by WHJ on 2018/2/27.
6 | // Copyright © 2018年 WHJ. All rights reserved.
7 | //
8 |
9 | #import "HJDownloadList_VC.h"
10 | #import "HJDownloadListCell.h"
11 | #import "HJDownloadManager.h"
12 |
13 | @interface HJDownloadList_VC ()
14 |
15 | @property (nonatomic, strong) UITableView *listTable;
16 |
17 | @property (nonatomic, strong) NSMutableArray *datas;
18 |
19 | @property (nonatomic, strong) UIView *tableHeader;
20 |
21 | @end
22 |
23 | static NSString * const kHJlistTableCellID = @"HJlistTableCellIdentifier";
24 |
25 | @implementation HJDownloadList_VC
26 | #pragma mark - Life Circle
27 | - (void)viewDidLoad {
28 | [super viewDidLoad];
29 |
30 | [self setupUI];
31 | }
32 | - (void)didReceiveMemoryWarning {
33 | [super didReceiveMemoryWarning];
34 | }
35 |
36 | - (void)buildDatas{
37 |
38 | self.datas = [NSMutableArray arrayWithArray:[kHJDownloadManager downloadModels]];
39 | }
40 |
41 |
42 | - (void)viewWillAppear:(BOOL)animated{
43 | [super viewWillAppear:animated];
44 |
45 | [self buildDatas];
46 |
47 | if(self.datas){
48 | self.listTable.editing = NO;
49 | [self.listTable reloadData];
50 | }
51 | }
52 |
53 | #pragma mark - About UI
54 | - (void)setupUI{
55 |
56 | UITableView *tableView = [[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStylePlain];
57 | tableView.delegate = self;
58 | tableView.dataSource = self;
59 | tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
60 | tableView.estimatedRowHeight = 0.f;
61 | tableView.estimatedSectionHeaderHeight = 0.f;
62 | tableView.estimatedSectionFooterHeight = 0.f;
63 | [tableView registerClass:[HJDownloadListCell class] forCellReuseIdentifier:kHJlistTableCellID];
64 | [self.view addSubview:tableView];
65 | self.listTable = tableView;
66 | self.listTable.tableHeaderView = self.tableHeader;
67 |
68 | UIButton * deleteAllBtn = [UIButton buttonWithType:UIButtonTypeCustom];
69 | [deleteAllBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
70 | deleteAllBtn.frame = CGRectMake(0, 0, 44, 44);
71 | deleteAllBtn.titleLabel.font = [UIFont systemFontOfSize:16.f];
72 | [deleteAllBtn setTitle:@"删除" forState:UIControlStateNormal];
73 | deleteAllBtn.contentHorizontalAlignment = UIControlContentHorizontalAlignmentRight;
74 | [deleteAllBtn addTarget:self action:@selector(deleteAllAction) forControlEvents:UIControlEventTouchUpInside];
75 | UIBarButtonItem *rightBarItem = [[UIBarButtonItem alloc] initWithCustomView:deleteAllBtn];
76 | self.navigationItem.rightBarButtonItem = rightBarItem;
77 | }
78 |
79 |
80 |
81 |
82 | #pragma mark - Request Data
83 |
84 | #pragma mark - Pravite Method
85 |
86 | #pragma mark - Public Method
87 |
88 | #pragma mark - Event response
89 | - (void)deleteAllAction{
90 |
91 | UIAlertController *alertC = [[UIAlertController alloc] init];
92 | UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"考虑一下" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
93 |
94 | }];
95 |
96 | UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:@"确认删除" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
97 |
98 | [kHJDownloadManager stopAll];
99 | [self.datas removeAllObjects];
100 |
101 | dispatch_async(dispatch_get_main_queue(), ^{
102 | [self.listTable reloadData];
103 | });
104 | }];
105 |
106 | [alertC addAction:cancelAction];
107 | [alertC addAction:confirmAction];
108 | [self presentViewController:alertC animated:YES completion:nil] ;
109 | }
110 |
111 |
112 | - (void)pauseAll{
113 |
114 | [kHJDownloadManager suspendAll];
115 |
116 | }
117 |
118 |
119 | - (void)resumeAll{
120 |
121 | [kHJDownloadManager resumeAll];
122 | }
123 | #pragma mark - Delegate methods
124 |
125 | #pragma mark - UITableViewDataSource
126 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
127 |
128 | return self.datas.count;
129 | }
130 |
131 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
132 |
133 | HJDownloadListCell *cell = [tableView dequeueReusableCellWithIdentifier:kHJlistTableCellID];
134 | cell.downloadModel = self.datas[indexPath.row];
135 | return cell;
136 | }
137 |
138 |
139 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
140 |
141 | return 1;
142 | }
143 |
144 |
145 | #pragma mark - UITableViewDelegate
146 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
147 |
148 | }
149 |
150 |
151 | - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
152 |
153 | return nil;
154 | }
155 |
156 |
157 | - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section{
158 | return nil;
159 | }
160 |
161 |
162 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
163 | return [HJDownloadListCell backCellHeight];
164 | }
165 |
166 | - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
167 |
168 | return 0;
169 | }
170 |
171 | - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{
172 |
173 | return 0.001f;
174 | }
175 |
176 |
177 | - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
178 | return YES;
179 | }
180 |
181 | - (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
182 | return UITableViewCellEditingStyleDelete;
183 | }
184 |
185 | - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath{
186 |
187 | }
188 |
189 |
190 |
191 | - (NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath
192 | {
193 | __weak HJDownloadListCell *weakCell = [tableView cellForRowAtIndexPath:indexPath];
194 | __weak typeof(self) weakSelf = self;
195 |
196 | UITableViewRowAction *action = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:@"删除" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) {
197 | [tableView beginUpdates];
198 |
199 | HJDownloadModel *downloadModel = weakCell.downloadModel;
200 | [kHJDownloadManager stopWithDownloadModel:weakSelf.datas[indexPath.row]];
201 | [weakSelf.datas removeObject:downloadModel];
202 | [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationBottom];
203 |
204 | [tableView endUpdates];
205 | }];
206 |
207 | return @[action];
208 | }
209 |
210 |
211 | #pragma mark - Getters/Setters/Lazy
212 | - (UIView *)tableHeader{
213 |
214 | if (!_tableHeader) {
215 | CGFloat width = self.view.frame.size.width;
216 | _tableHeader = [[UIView alloc] initWithFrame:CGRectMake(0, 0, width, 44)];
217 | UIButton * pauseAllBtn = [self buttonWithTitle:@"暂停全部" action:@selector(pauseAll)];
218 | [pauseAllBtn setFrame:CGRectMake(0, 0,width/2.f, 44)];
219 | [_tableHeader addSubview:pauseAllBtn];
220 |
221 | UIButton * resumeAllBtn = [self buttonWithTitle:@"全部继续" action:@selector(resumeAll)];
222 | [resumeAllBtn setFrame:CGRectMake(width/2.f, 0,width/2.f, 44)];
223 | [_tableHeader addSubview:resumeAllBtn];
224 |
225 | }
226 | return _tableHeader;
227 | }
228 |
229 |
230 | - (UIButton *)buttonWithTitle:(NSString *)title action:(SEL)sel{
231 |
232 | UIButton * btn = [UIButton buttonWithType:UIButtonTypeSystem];
233 | [btn addTarget:self action:sel forControlEvents:UIControlEventTouchUpInside];
234 | [btn setTitle:title forState:UIControlStateNormal];
235 | [btn setBackgroundColor:[UIColor lightGrayColor]];
236 | return btn;
237 | }
238 | @end
239 |
--------------------------------------------------------------------------------
/HJDownloadManager/ExampleDemo/Controllers/HJDownloadTabBar_VC.h:
--------------------------------------------------------------------------------
1 | //
2 | // HJDownloadTabBar_VC.h
3 | // HJDownloadManager
4 | //
5 | // Created by WHJ on 2018/2/27.
6 | // Copyright © 2018年 WHJ. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface HJDownloadTabBar_VC : UITabBarController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/HJDownloadManager/ExampleDemo/Controllers/HJDownloadTabBar_VC.m:
--------------------------------------------------------------------------------
1 | //
2 | // HJDownloadTabBar_VC.m
3 | // HJDownloadManager
4 | //
5 | // Created by WHJ on 2018/2/27.
6 | // Copyright © 2018年 WHJ. All rights reserved.
7 | //
8 |
9 | #import "HJDownloadTabBar_VC.h"
10 | #import "HJDownloadHome_VC.h"
11 | #import "HJDownloadList_VC.h"
12 |
13 | @interface HJDownloadTabBar_VC ()
14 |
15 | @end
16 |
17 | @implementation HJDownloadTabBar_VC
18 |
19 | #pragma mark - Life Circle
20 | - (void)viewDidLoad {
21 | [super viewDidLoad];
22 |
23 | [self setupUI];
24 | }
25 | - (void)didReceiveMemoryWarning {
26 | [super didReceiveMemoryWarning];
27 | }
28 |
29 |
30 | #pragma mark - About UI
31 | - (void)setupUI{
32 |
33 | HJDownloadHome_VC *homeVC = [[HJDownloadHome_VC alloc] init];
34 | HJDownloadList_VC *listVC = [[HJDownloadList_VC alloc] init];
35 |
36 | UINavigationController *homeNav = [[UINavigationController alloc] initWithRootViewController:homeVC];
37 |
38 | UINavigationController *listNav = [[UINavigationController alloc] initWithRootViewController:listVC];
39 |
40 | homeNav.tabBarItem.title = @"下载首页";
41 | listNav.tabBarItem.title = @"下载列表";
42 |
43 | homeVC.title = @"下载首页";
44 | listVC.title = @"下载列表";
45 |
46 | self.viewControllers = @[homeNav,listNav];
47 |
48 | }
49 |
50 | #pragma mark - Request Data
51 |
52 | #pragma mark - Pravite Method
53 |
54 | #pragma mark - Public Method
55 |
56 | #pragma mark - Event response
57 |
58 | #pragma mark - Delegate methods
59 |
60 | #pragma mark - Getters/Setters/Lazy
61 |
62 | @end
63 |
--------------------------------------------------------------------------------
/HJDownloadManager/ExampleDemo/Models/HJExampleModel.h:
--------------------------------------------------------------------------------
1 | //
2 | // HJExampleModel.h
3 | // HJDownloadManager
4 | //
5 | // Created by WHJ on 2018/2/28.
6 | // Copyright © 2018年 WHJ. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface HJExampleModel : NSObject
12 |
13 | @property (nonatomic, copy) NSString * url;
14 |
15 | @property (nonatomic, copy) NSString * name;
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/HJDownloadManager/ExampleDemo/Models/HJExampleModel.m:
--------------------------------------------------------------------------------
1 | //
2 | // HJExampleModel.m
3 | // HJDownloadManager
4 | //
5 | // Created by WHJ on 2018/2/28.
6 | // Copyright © 2018年 WHJ. All rights reserved.
7 | //
8 |
9 | #import "HJExampleModel.h"
10 |
11 | @implementation HJExampleModel
12 |
13 |
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/HJDownloadManager/ExampleDemo/Views/HJBaseDownloadCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // HJBaseDownloadCell.h
3 | // HJDownloadManager
4 | //
5 | // Created by WHJ on 2018/2/27.
6 | // Copyright © 2018年 WHJ. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface HJBaseDownloadCell : UITableViewCell
12 |
13 | /** <#describe#> */
14 | @property (nonatomic, strong) UILabel * titleLabel;
15 |
16 | /** <#describe#> */
17 | @property (nonatomic, strong) UIButton * downloadBtn;
18 |
19 | /** <#describe#> */
20 | @property (nonatomic, strong) UIProgressView * progressView;
21 |
22 | /** <#describe#> */
23 | @property (nonatomic, strong) UILabel *fileSizeLabel;
24 |
25 | /** <#describe#> */
26 | @property (nonatomic, strong) UILabel *fileFormatLabel;
27 |
28 |
29 | - (void)downloadAction:(UIButton *)sender;
30 |
31 | @end
32 |
--------------------------------------------------------------------------------
/HJDownloadManager/ExampleDemo/Views/HJBaseDownloadCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // HJBaseDownloadCell.m
3 | // HJDownloadManager
4 | //
5 | // Created by WHJ on 2018/2/27.
6 | // Copyright © 2018年 WHJ. All rights reserved.
7 | //
8 |
9 | #import "HJBaseDownloadCell.h"
10 |
11 | @implementation HJBaseDownloadCell
12 |
13 | #pragma mark - Life Circle
14 | - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
15 |
16 | self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
17 |
18 | if (self) {
19 |
20 | [self setupUI];
21 | }
22 | return self;
23 | }
24 |
25 |
26 | #pragma mark - About UI
27 | - (void)setupUI{
28 |
29 | [self.contentView addSubview:self.titleLabel];
30 |
31 | [self.contentView addSubview:self.downloadBtn];
32 |
33 | [self.contentView addSubview:self.fileSizeLabel];
34 |
35 | [self.contentView addSubview:self.progressView];
36 | }
37 |
38 |
39 |
40 | #pragma mark - Pravite Method
41 |
42 | #pragma mark - Public Method
43 |
44 | #pragma mark - Event response
45 | - (void)downloadAction:(UIButton *)sender{
46 |
47 |
48 | }
49 | #pragma mark - Delegate methods
50 |
51 | #pragma mark - Getters/Setters/Lazy
52 | - (UILabel *)titleLabel{
53 |
54 | if (!_titleLabel) {
55 | _titleLabel = [[UILabel alloc] init];
56 | _titleLabel.font = [UIFont systemFontOfSize:14.f];
57 | }
58 | return _titleLabel;
59 | }
60 |
61 |
62 | - (UILabel *)fileSizeLabel{
63 |
64 | if (!_fileSizeLabel) {
65 | _fileSizeLabel = [[UILabel alloc] init];
66 | _fileSizeLabel.font = [UIFont systemFontOfSize:14.f];
67 | }
68 | return _fileSizeLabel;
69 | }
70 |
71 |
72 |
73 | - (UILabel *)fileFormatLabel{
74 |
75 | if (!_fileFormatLabel) {
76 | _fileFormatLabel = [[UILabel alloc] init];
77 | _fileFormatLabel.font = [UIFont systemFontOfSize:14.f];
78 | [self.contentView addSubview:_fileFormatLabel];
79 | }
80 | return _fileFormatLabel;
81 | }
82 |
83 |
84 | - (UIButton *)downloadBtn{
85 |
86 | if (!_downloadBtn) {
87 | _downloadBtn = [UIButton buttonWithType:UIButtonTypeCustom];
88 | [_downloadBtn addTarget:self action:@selector(downloadAction:) forControlEvents:UIControlEventTouchUpInside];
89 | [_downloadBtn setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
90 | _downloadBtn.titleLabel.font = [UIFont systemFontOfSize:10];
91 | }
92 | return _downloadBtn;
93 | }
94 |
95 | - (UIProgressView *)progressView{
96 |
97 | if (!_progressView) {
98 | _progressView = [[UIProgressView alloc] init];
99 | }
100 | return _progressView;
101 | }
102 | @end
103 |
--------------------------------------------------------------------------------
/HJDownloadManager/ExampleDemo/Views/HJDownloadHomeCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // HJDownloadHomeCell.h
3 | // HJDownloadManager
4 | //
5 | // Created by WHJ on 2018/2/27.
6 | // Copyright © 2018年 WHJ. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "HJBaseDownloadCell.h"
11 |
12 | @class HJExampleModel;
13 | @interface HJDownloadHomeCell : HJBaseDownloadCell
14 |
15 | @property (nonatomic, strong) HJExampleModel *model;
16 |
17 | + (CGFloat)backCellHeight;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/HJDownloadManager/ExampleDemo/Views/HJDownloadHomeCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // HJDownloadHomeCell.m
3 | // HJDownloadManager
4 | //
5 | // Created by WHJ on 2018/2/27.
6 | // Copyright © 2018年 WHJ. All rights reserved.
7 | //
8 |
9 | #import "HJDownloadHomeCell.h"
10 | #import "HJExampleModel.h"
11 | #import "HJDownloadModel.h"
12 | #import "HJDownloadManager.h"
13 |
14 | static const CGFloat kHomeCellHeight = 53.f;
15 |
16 | @interface HJDownloadHomeCell ()
17 |
18 | @property (nonatomic, strong) HJDownloadModel *downloadModel;
19 |
20 | @end
21 |
22 | @implementation HJDownloadHomeCell
23 |
24 | #pragma mark - Life Circle
25 |
26 | #pragma mark - About UI
27 |
28 | - (void)refreshUIWithDownloadModel:(HJDownloadModel *)downloadModel{
29 |
30 | HJDownloadStatus downloadStatus = downloadModel.status;
31 | CGFloat progress = downloadModel.progress;
32 | NSInteger progressInt = (NSInteger)(progress * 100);
33 | dispatch_async(dispatch_get_main_queue(), ^{
34 |
35 | [self.progressView setProgress:progress];
36 | [self.downloadBtn setTitle:nil forState:UIControlStateNormal];
37 |
38 | if(downloadStatus == kHJDownloadStatus_None){
39 | [self.downloadBtn setImage:[UIImage imageNamed:@"HJ_download_ready"] forState:UIControlStateNormal];
40 | }else if(downloadStatus == kHJDownloadStatus_Running){
41 | [self.downloadBtn setImage:nil forState:UIControlStateNormal];
42 | NSString *title = [NSString stringWithFormat:@"%zd%%",progressInt];
43 | [self.downloadBtn setTitle:title forState:UIControlStateNormal];
44 | }else if(downloadStatus == kHJDownloadStatus_Suspended){
45 | [self.downloadBtn setImage:[UIImage imageNamed:@"HJ_download_pause"] forState:UIControlStateNormal];
46 | }else if(downloadStatus == kHJDownloadStatus_Completed){
47 | [self.downloadBtn setImage:[UIImage imageNamed:@"HJ_download_finished"] forState:UIControlStateNormal];
48 | }else if(downloadStatus == kHJDownloadStatus_Failed){
49 | [self.downloadBtn setImage:[UIImage imageNamed:@"HJ_download_fail"] forState:UIControlStateNormal];
50 | }else if(downloadStatus == kHJDownloadStatus_Waiting){
51 | [self.downloadBtn setImage:[UIImage imageNamed:@"HJ_download_waiting"] forState:UIControlStateNormal];
52 | }else{
53 |
54 | }
55 | });
56 | }
57 |
58 | - (void)layoutSubviews{
59 |
60 | self.titleLabel.frame = CGRectMake(10, 10, 200, 20);
61 | self.downloadBtn.frame = CGRectMake(CGRectGetWidth(self.bounds) - kHomeCellHeight - 10, 0, kHomeCellHeight, kHomeCellHeight);
62 | self.downloadBtn.center = CGPointMake(self.downloadBtn.center.x, self.contentView.center.y);
63 | self.progressView.frame = CGRectMake(0, 50, CGRectGetWidth(self.bounds), 3.f);
64 |
65 | }
66 | #pragma mark - Pravite Method
67 | - (HJDownloadModel *)downloadModel{
68 |
69 | if(!_downloadModel){
70 | _downloadModel = [kHJDownloadManager downloadModelWithUrl:self.model.url];
71 |
72 | if(!_downloadModel && _model){
73 | _downloadModel = [[HJDownloadModel alloc] init];
74 | _downloadModel.urlString = self.model.url;
75 | _downloadModel.downloadDesc = self.model.name;
76 | }
77 | __weak typeof(self) weakSelf = self;
78 | _downloadModel.statusChanged = ^(HJDownloadModel *downloadModel) {
79 | [weakSelf refreshUIWithDownloadModel:downloadModel];
80 | };
81 |
82 | _downloadModel.progressChanged = ^(HJDownloadModel *downloadModel) {
83 | [weakSelf refreshUIWithDownloadModel:downloadModel];
84 | };
85 | }
86 | return _downloadModel;
87 | }
88 |
89 | #pragma mark - Public Method
90 | + (CGFloat)backCellHeight{
91 |
92 | return kHomeCellHeight;
93 | }
94 | #pragma mark - Event response
95 | - (void)downloadAction:(UIButton *)sender{
96 |
97 | HJDownloadStatus downloadStatus = self.downloadModel.status;
98 |
99 | if(downloadStatus == kHJDownloadStatus_None){
100 |
101 | [kHJDownloadManager startWithDownloadModel:self.downloadModel];
102 |
103 | }else if(downloadStatus == kHJDownloadStatus_Running){
104 |
105 | [kHJDownloadManager suspendWithDownloadModel:self.downloadModel];
106 |
107 | }else if(downloadStatus == kHJDownloadStatus_Suspended){
108 |
109 | [kHJDownloadManager resumeWithDownloadModel:self.downloadModel];
110 |
111 | }else if(downloadStatus == kHJDownloadStatus_Completed){
112 |
113 |
114 | }else if(downloadStatus == kHJDownloadStatus_Failed){
115 |
116 | [kHJDownloadManager resumeWithDownloadModel:self.downloadModel];
117 |
118 | }else if(downloadStatus == kHJDownloadStatus_Waiting){
119 |
120 | }else if(downloadStatus == kHJDownloadStatus_Cancel){
121 |
122 | [kHJDownloadManager resumeWithDownloadModel:self.downloadModel];
123 | }
124 |
125 | }
126 | #pragma mark - Delegate methods
127 |
128 | #pragma mark - Getters/Setters/Lazy
129 | - (void)setModel:(HJExampleModel *)model{
130 | _model = model;
131 |
132 | self.titleLabel.text = model.name;
133 |
134 | self.downloadModel = nil;
135 |
136 | [self refreshUIWithDownloadModel:[self downloadModel]];
137 | }
138 |
139 | @end
140 |
--------------------------------------------------------------------------------
/HJDownloadManager/ExampleDemo/Views/HJDownloadListCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // HJDownloadListCell.h
3 | // HJDownloadManager
4 | //
5 | // Created by WHJ on 2018/2/28.
6 | // Copyright © 2018年 WHJ. All rights reserved.
7 | //
8 |
9 | #import "HJBaseDownloadCell.h"
10 |
11 | @class HJDownloadModel;
12 | @interface HJDownloadListCell : HJBaseDownloadCell
13 |
14 | @property (nonatomic, strong) HJDownloadModel *downloadModel;
15 |
16 | + (CGFloat)backCellHeight;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/HJDownloadManager/ExampleDemo/Views/HJDownloadListCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // HJDownloadListCell.m
3 | // HJDownloadManager
4 | //
5 | // Created by WHJ on 2018/2/28.
6 | // Copyright © 2018年 WHJ. All rights reserved.
7 | //
8 |
9 | #import "HJDownloadListCell.h"
10 | #import "HJDownloadModel.h"
11 | #import "HJDownloadManager.h"
12 |
13 | static const CGFloat kListCellHeight = 58.f;
14 |
15 | @interface HJDownloadListCell ()
16 |
17 | @end
18 |
19 | @implementation HJDownloadListCell
20 |
21 | #pragma mark - Life Circle
22 |
23 | #pragma mark - About UI
24 |
25 | - (void)refreshUIWithDownloadModel:(HJDownloadModel *)downloadModel{
26 |
27 | HJDownloadStatus downloadStatus = downloadModel.status;
28 | CGFloat progress = downloadModel.progress;
29 | NSInteger progressInt = (NSInteger)(progress * 100);
30 | dispatch_async(dispatch_get_main_queue(), ^{
31 |
32 | [self.progressView setProgress:progress];
33 | [self.downloadBtn setTitle:nil forState:UIControlStateNormal];
34 | //设置文件下载大小
35 | CGFloat downloadSize = downloadModel.fileDownloadSize/1024/1024.f;
36 | CGFloat totalSize = downloadModel.fileTotalSize/1024/1024.f;
37 | NSString *fileSizeText = [NSString stringWithFormat:@"已下载:%.2fM/总大小:%.2fM",downloadSize,totalSize];
38 | self.fileSizeLabel.text = fileSizeText;
39 |
40 | if(downloadStatus == kHJDownloadStatus_None){
41 | [self.downloadBtn setImage:[UIImage imageNamed:@"HJ_download_ready"] forState:UIControlStateNormal];
42 | }else if(downloadStatus == kHJDownloadStatus_Running){
43 | [self.downloadBtn setImage:nil forState:UIControlStateNormal];
44 | NSString *title = [NSString stringWithFormat:@"%zd%%",progressInt];
45 | [self.downloadBtn setTitle:title forState:UIControlStateNormal];
46 | }else if(downloadStatus == kHJDownloadStatus_Suspended){
47 | [self.downloadBtn setImage:[UIImage imageNamed:@"HJ_download_pause"] forState:UIControlStateNormal];
48 | }else if(downloadStatus == kHJDownloadStatus_Completed){
49 | [self.downloadBtn setImage:[UIImage imageNamed:@"HJ_download_finished"] forState:UIControlStateNormal];
50 | }else if(downloadStatus == kHJDownloadStatus_Failed){
51 | [self.downloadBtn setImage:[UIImage imageNamed:@"HJ_download_fail"] forState:UIControlStateNormal];
52 | }else if(downloadStatus == kHJDownloadStatus_Waiting){
53 | [self.downloadBtn setImage:[UIImage imageNamed:@"HJ_download_waiting"] forState:UIControlStateNormal];
54 | }else{
55 |
56 | }
57 | });
58 | }
59 |
60 | - (void)layoutSubviews{
61 |
62 | [super layoutSubviews];
63 |
64 | self.titleLabel.frame = CGRectMake(10, 10, 200, 20);
65 |
66 | self.fileFormatLabel.frame = CGRectMake(CGRectGetMaxX(self.titleLabel.frame)+20, 10, 100, 20);
67 |
68 | self.fileSizeLabel.frame = CGRectMake(10, CGRectGetMaxY(self.titleLabel.frame)+5, 200, 20);
69 |
70 | self.downloadBtn.frame = CGRectMake(CGRectGetWidth(self.bounds) - kListCellHeight - 10, 0, kListCellHeight, kListCellHeight);
71 | self.downloadBtn.center = CGPointMake(self.downloadBtn.center.x, self.contentView.center.y);
72 |
73 | self.progressView.frame = CGRectMake(0, 55, CGRectGetWidth(self.bounds), 3.f);
74 | }
75 | #pragma mark - Pravite Method
76 | - (void)setDownloadModel:(HJDownloadModel *)downloadModel{
77 |
78 | _downloadModel = downloadModel;
79 |
80 | self.titleLabel.text = downloadModel.downloadDesc;
81 |
82 | __weak typeof(self) weakSelf = self;
83 | _downloadModel.statusChanged = ^(HJDownloadModel *downloadModel) {
84 | [weakSelf refreshUIWithDownloadModel:downloadModel];
85 | };
86 |
87 | _downloadModel.progressChanged = ^(HJDownloadModel *downloadModel) {
88 | [weakSelf refreshUIWithDownloadModel:downloadModel];
89 | };
90 |
91 | [self refreshUIWithDownloadModel:self.downloadModel];
92 | //文件格式
93 | self.fileFormatLabel.text = [NSString stringWithFormat:@"文件格式:%@", self.downloadModel.fileFormat];
94 |
95 | [self setNeedsLayout];
96 | }
97 |
98 | #pragma mark - Public Method
99 | + (CGFloat)backCellHeight{
100 |
101 | return kListCellHeight;
102 | }
103 | #pragma mark - Event response
104 | - (void)downloadAction:(UIButton *)sender{
105 |
106 | HJDownloadStatus downloadStatus = self.downloadModel.status;
107 |
108 | if(downloadStatus == kHJDownloadStatus_None){
109 |
110 | [kHJDownloadManager startWithDownloadModel:self.downloadModel];
111 |
112 | }else if(downloadStatus == kHJDownloadStatus_Running){
113 |
114 | [kHJDownloadManager suspendWithDownloadModel:self.downloadModel];
115 |
116 | }else if(downloadStatus == kHJDownloadStatus_Suspended){
117 |
118 | [kHJDownloadManager resumeWithDownloadModel:self.downloadModel];
119 |
120 | }else if(downloadStatus == kHJDownloadStatus_Completed){
121 |
122 |
123 | }else if(downloadStatus == kHJDownloadStatus_Failed){
124 |
125 | [kHJDownloadManager startWithDownloadModel:self.downloadModel];
126 |
127 | }else if(downloadStatus == kHJDownloadStatus_Waiting){
128 |
129 | }else if(downloadStatus == kHJDownloadStatus_Cancel){
130 |
131 | [kHJDownloadManager startWithDownloadModel:self.downloadModel];
132 | }
133 |
134 | }
135 | #pragma mark - Delegate methods
136 |
137 | #pragma mark - Getters/Setters/Lazy
138 |
139 |
140 | @end
141 |
--------------------------------------------------------------------------------
/HJDownloadManager/HJDownloadClass/HJDownloadConst.h:
--------------------------------------------------------------------------------
1 | //
2 | // HJDownloadConst.h
3 | // HJDownloadManager
4 | //
5 | // Created by WHJ on 2018/2/28.
6 | // Copyright © 2018年 WHJ. All rights reserved.
7 | //
8 |
9 | #ifndef HJDownloadConst_h
10 | #define HJDownloadConst_h
11 |
12 |
13 | #define kFileManager [NSFileManager defaultManager]
14 |
15 | // 缓存主目录
16 | #define HJCachesDirectory [[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingString:@"/WHJDownload/"]
17 |
18 | #define HJSavedDownloadModelsFilePath [HJCachesDirectory stringByAppendingFormat:@"HJSavedDownloadModels"]
19 |
20 | #define HJSavedDownloadModelsBackup [HJCachesDirectory stringByAppendingFormat:@"HJSavedDownloadModelsBackup"]
21 |
22 |
23 | #define hj_tmpDownloadBaseFilePath [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0]
24 |
25 |
26 | // 下载operation最大并发数
27 | #define HJDownloadMaxConcurrentOperationCount 3
28 |
29 |
30 | #endif /* HJDownloadConst_h */
31 |
--------------------------------------------------------------------------------
/HJDownloadManager/HJDownloadClass/HJDownloadHeaders.h:
--------------------------------------------------------------------------------
1 | //
2 | // HJDownloadHeaders.h
3 | //
4 | //
5 | // Created by WHJ on 16/7/15.
6 | //
7 | //
8 |
9 | #ifndef HJDownloadHeaders_h
10 | #define HJDownloadHeaders_h
11 |
12 |
13 | #endif /* HJDownloadHeaders_h */
14 |
15 | #import "HJDownloadOperation.h"
16 |
17 | #import "HJDownloadModel.h"
18 |
19 | #import "HJDownloadManager.h"
20 |
21 | #import "NSURLSessionTask+HJModel.h"
22 |
23 | #import "MJExtension.h"
24 |
25 | #import "HJDownloadConst.h"
26 |
--------------------------------------------------------------------------------
/HJDownloadManager/HJDownloadClass/HJDownloadManager.h:
--------------------------------------------------------------------------------
1 | //
2 | // HJDownloadManager.h
3 | // HJNetworkService
4 | //
5 | // Created by WHJ on 16/7/5.
6 | // Copyright © 2016年 WHJ. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "HJDownloadHeaders.h"
11 |
12 | typedef NS_ENUM(NSUInteger, HJOperationType) {
13 | kHJOperationType_startAll,
14 | kHJOperationType_suspendAll ,
15 | kHJOperationType_resumeAll,
16 | kHJOperationType_stopAll
17 | };
18 |
19 | #define kHJDownloadManager [HJDownloadManager sharedManager]
20 |
21 | @class HJDownloadModel;
22 |
23 | @interface HJDownloadManager : NSObject
24 |
25 | @property (nonatomic, strong, readonly) NSMutableArray * downloadModels;
26 |
27 | @property (nonatomic, strong, readonly) NSMutableArray * completeModels;
28 |
29 | @property (nonatomic, strong, readonly) NSMutableArray * downloadingModels;
30 |
31 | @property (nonatomic, strong, readonly) NSMutableArray * pauseModels;
32 |
33 | @property (nonatomic, strong, readonly) NSMutableArray * waitModels;
34 |
35 | @property (nonatomic, assign) NSInteger maxConcurrentOperationCount;
36 |
37 | /** 是否禁用进度打印日志 */
38 | @property (readonly, nonatomic, assign) BOOL enableProgressLog;
39 |
40 | #pragma mark - 单例方法
41 | + (instancetype)sharedManager;
42 | /**
43 | * 禁止打印进度日志
44 | */
45 | - (void)enableProgressLog:(BOOL)enable;
46 | /**
47 | * 获取下载模型
48 | */
49 | - (HJDownloadModel *)downloadModelWithUrl:(NSString *)url;
50 |
51 | #pragma mark - 单任务下载控制
52 | /**
53 | * 开始下载
54 | */
55 | - (void)startWithDownloadModel:(HJDownloadModel *)model;
56 | /**
57 | * 暂停下载
58 | */
59 | - (void)suspendWithDownloadModel:(HJDownloadModel *)model;
60 | /**
61 | * 恢复下载
62 | */
63 | - (void)resumeWithDownloadModel:(HJDownloadModel *)model;
64 | /**
65 | * 取消下载 (取消下载后 operation将从队列中移除 并 移除下载模型和对应文件)
66 | */
67 | - (void)stopWithDownloadModel:(HJDownloadModel *)model;
68 |
69 | #pragma mark - 多任务下载控制
70 | /**
71 | * 批量下载操作
72 | */
73 | - (void)startWithDownloadModels:(NSArray *)downloadModels;
74 | /**
75 | * 暂停所有下载任务
76 | */
77 | - (void)suspendAll;
78 | /**
79 | * 恢复下载任务(进行中、已完成、等待中除外)
80 | */
81 | - (void)resumeAll;
82 | /**
83 | * 停止并删除下载任务
84 | */
85 | - (void)stopAll;
86 |
87 |
88 |
89 | @end
90 |
--------------------------------------------------------------------------------
/HJDownloadManager/HJDownloadClass/HJDownloadManager.m:
--------------------------------------------------------------------------------
1 | //
2 | // HJDownloadManager.m
3 | // HJNetworkService
4 | //
5 | // Created by WHJ on 16/7/5.
6 | // Copyright © 2016年 WHJ. All rights reserved.
7 | //
8 |
9 | #import "HJDownloadManager.h"
10 | #import "HJUncaughtExceptionHandler.h"
11 | #import "AppDelegate.h"
12 |
13 | @interface HJDownloadManager (){
14 | NSMutableArray *_downloadModels;
15 | NSMutableArray *_completeModels;
16 | NSMutableArray *_downloadingModels;
17 | NSMutableArray *_pauseModels;
18 | BOOL _enableProgressLog;
19 | }
20 |
21 |
22 | @property (nonatomic, strong) NSOperationQueue *queue;
23 |
24 | @property (nonatomic, strong) NSURLSession *backgroundSession;
25 |
26 | @end
27 |
28 | static UIBackgroundTaskIdentifier bgTask;
29 |
30 |
31 | @implementation HJDownloadManager
32 |
33 |
34 | #pragma mark - 单例相关
35 | static id instace = nil;
36 | + (id)allocWithZone:(struct _NSZone *)zone
37 | {
38 | if (instace == nil) {
39 | static dispatch_once_t onceToken;
40 | dispatch_once(&onceToken, ^{
41 | instace = [super allocWithZone:zone];
42 | // 添加未捕获异常的监听
43 | [instace handleUncaughtExreption];
44 | // 添加监听
45 | [instace addObservers];
46 | // 创建缓存目录
47 | [instace createCacheDirectory];
48 | });
49 | }
50 | return instace;
51 | }
52 |
53 | - (instancetype)init
54 | {
55 | return instace;
56 | }
57 |
58 | + (instancetype)sharedManager
59 | {
60 | return [[self alloc] init];
61 | }
62 |
63 | - (id)copyWithZone:(struct _NSZone *)zone
64 | {
65 | return instace;
66 | }
67 |
68 | - (id)mutableCopyWithZone:(struct _NSZone *)zone{
69 | return instace;
70 | }
71 |
72 |
73 | #pragma mark - 单例初始化调用
74 | /**
75 | * 添加监听
76 | */
77 | - (void)addObservers
78 | {
79 | [[NSNotificationCenter defaultCenter] addObserver:instace selector:@selector(recoverDownloadModels) name:UIApplicationDidFinishLaunchingNotification object:nil];
80 |
81 | [[NSNotificationCenter defaultCenter] addObserver:instace selector:@selector(applicationWillTerminate) name:UIApplicationWillTerminateNotification object:nil];
82 |
83 | [[NSNotificationCenter defaultCenter] addObserver:instace selector:@selector(endBackgroundTask) name:UIApplicationWillEnterForegroundNotification object:nil];
84 |
85 | [[NSNotificationCenter defaultCenter] addObserver:instace selector:@selector(getBackgroundTask) name:UIApplicationDidEnterBackgroundNotification object:nil];
86 |
87 | [[NSNotificationCenter defaultCenter] addObserver:instace selector:@selector(applicationWillTerminate) name:kNotificationUncaughtException object:nil];
88 | }
89 |
90 | /**
91 | * 创建缓存目录
92 | */
93 | - (void)createCacheDirectory
94 | {
95 | NSFileManager *fileManager = [NSFileManager defaultManager];
96 | if (![fileManager fileExistsAtPath:HJCachesDirectory]) {
97 | [fileManager createDirectoryAtPath:HJCachesDirectory withIntermediateDirectories:YES attributes:nil error:NULL];
98 | }
99 | NSLog(@"创建缓存目录:%@",HJCachesDirectory);
100 | }
101 |
102 | /**
103 | * 添加未捕获异常的监听
104 | */
105 | - (void)handleUncaughtExreption
106 | {
107 | [HJUncaughtExceptionHandler setDefaultHandler];
108 | }
109 |
110 | /**
111 | * 禁止打印进度日志
112 | */
113 | - (void)enableProgressLog:(BOOL)enable
114 | {
115 | _enableProgressLog = enable;
116 | }
117 |
118 | #pragma mark - 模型相关
119 | - (void)addDownloadModel:(HJDownloadModel *)model
120 | {
121 | if (![self checkExistWithDownloadModel:model]) {
122 | [self.downloadModels addObject:model];
123 | NSLog(@"下载模型添加成功");
124 | }
125 | }
126 |
127 | - (void)addDownloadModels:(NSArray *)models
128 | {
129 | if ([models isKindOfClass:[NSArray class]]) {
130 | [models enumerateObjectsUsingBlock:^(HJDownloadModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
131 | [self addDownloadModel:obj];
132 | }];
133 | }
134 | }
135 |
136 | -(BOOL)checkExistWithDownloadModel:(HJDownloadModel *)model
137 | {
138 |
139 | for (HJDownloadModel *tmpModel in self.downloadModels) {
140 | if ([tmpModel.urlString isEqualToString:model.urlString]) {
141 | NSLog(@"Tip:下载数据模型已存在");
142 | return YES;
143 | }
144 | }
145 | return NO;
146 | }
147 |
148 |
149 | - (HJDownloadModel *)downloadModelWithUrl:(NSString *)url
150 | {
151 | for (HJDownloadModel *tmpModel in self.downloadModels) {
152 | if ([url isEqualToString:tmpModel.urlString]) {
153 | return tmpModel;
154 | }
155 | }
156 | return nil;
157 | }
158 | #pragma mark - 单任务下载控制
159 | - (void)startWithDownloadModel:(HJDownloadModel *)model
160 | {
161 | if (model.status == kHJDownloadStatus_Completed) {
162 | return;
163 | }
164 |
165 | [self addDownloadModel:model];
166 |
167 | //检查队列是否挂起
168 | if(self.queue.suspended){
169 | self.queue.suspended = NO;
170 | }
171 |
172 | model.operation = [[HJDownloadOperation alloc] initWithDownloadModel:model andSession:self.backgroundSession];
173 | [self.queue addOperation:model.operation];
174 | }
175 |
176 | //暂停后操作将销毁 若想继续执行 则需重新创建operation并添加
177 | - (void)suspendWithDownloadModel:(HJDownloadModel *)model
178 | {
179 | [self suspendWithDownloadModel:model forAll:NO];
180 | }
181 |
182 |
183 | - (void)suspendWithDownloadModel:(HJDownloadModel *)model forAll:(BOOL)forAll
184 | {
185 | if (forAll) {//暂停全部
186 | if (model.status == kHJDownloadStatus_Running) {//下载中 则暂停
187 | [model.operation suspend];
188 | }else if (model.status == kHJDownloadStatus_Waiting){//等待中 则取消
189 | [model.operation cancel];
190 | }
191 | }else{
192 | if (model.status == kHJDownloadStatus_Running) {
193 | [model.operation suspend];
194 | }
195 | }
196 |
197 | model.operation = nil;
198 | }
199 |
200 |
201 | - (void)resumeWithDownloadModel:(HJDownloadModel *)model
202 | {
203 | if (model.status == kHJDownloadStatus_Completed ||
204 | model.status == kHJDownloadStatus_Running) {
205 | return;
206 | }
207 | //等待中 且操作已在队列中 则无需恢复
208 | if (model.status == kHJDownloadStatus_Waiting && model.operation) {
209 | return;
210 | }
211 | model.operation = nil;
212 |
213 | //检查队列是否挂起
214 | if(self.queue.suspended){
215 | self.queue.suspended = NO;
216 | }
217 |
218 | model.operation = [[HJDownloadOperation alloc] initWithDownloadModel:model andSession:self.backgroundSession];
219 | [self.queue addOperation:model.operation];
220 |
221 | }
222 |
223 |
224 |
225 | - (void)stopWithDownloadModel:(HJDownloadModel *)model
226 | {
227 | [self stopWithDownloadModel:model forAll:NO];
228 | }
229 |
230 |
231 |
232 | - (void)stopWithDownloadModel:(HJDownloadModel *)model forAll:(BOOL)forAll
233 | {
234 | if (model.status != kHJDownloadStatus_Completed) {
235 | [model.operation cancel];
236 | }
237 |
238 | //移除对应的下载文件
239 | if([kFileManager fileExistsAtPath:model.destinationPath]){
240 | NSError *error = nil;
241 | [kFileManager removeItemAtPath:model.destinationPath error:&error];
242 | if (error) {
243 | NSLog(@"Tip:下载文件移除失败,%@",error);
244 | }else{
245 | NSLog(@"Tip:下载文件移除成功");
246 | }
247 | }
248 |
249 | //释放operation
250 | model.operation = nil;
251 |
252 | //单个删除 则直接从数组中移除下载模型 否则等清空文件后统一移除
253 | if(!forAll){
254 | [self.downloadModels removeObject:model];
255 | }
256 | }
257 |
258 |
259 | #pragma mark - 批量下载相关
260 | /**
261 | * 批量下载操作
262 | */
263 | - (void)startWithDownloadModels:(NSArray *)downloadModels
264 | {
265 | NSLog(@">>>%@前 operationCount = %zd", NSStringFromSelector(_cmd),self.queue.operationCount);
266 | [self.queue setSuspended:NO];
267 | [self addDownloadModels:downloadModels];
268 | [self operateTasksWithOperationType:kHJOperationType_startAll];
269 | NSLog(@"<<<%@后 operationCount = %zd",NSStringFromSelector(_cmd),self.queue.operationCount);
270 | }
271 |
272 | /**
273 | * 暂停所有下载任务
274 | */
275 | - (void)suspendAll
276 | {
277 | [self.queue setSuspended:YES];
278 | [self operateTasksWithOperationType:kHJOperationType_suspendAll];
279 | }
280 |
281 | /**
282 | * 恢复下载任务(进行中、已完成、等待中除外)
283 | */
284 | - (void)resumeAll
285 | {
286 | [self.queue setSuspended:NO];
287 | [self operateTasksWithOperationType:kHJOperationType_resumeAll];
288 | }
289 |
290 | /**
291 | * 停止并删除下载任务
292 | */
293 | - (void)stopAll
294 | {
295 | //销毁前暂停队列 防止等待中的任务执行
296 | [self.queue setSuspended:YES];
297 | [self.queue cancelAllOperations];
298 | [self operateTasksWithOperationType:kHJOperationType_stopAll];
299 | [self.queue setSuspended:NO];
300 | [self.downloadModels removeAllObjects];
301 | [self removeAllFiles];
302 | }
303 |
304 |
305 | - (void)operateTasksWithOperationType:(HJOperationType)operationType
306 | {
307 | [self.downloadModels enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
308 | HJDownloadModel *downloadModel = obj;
309 | switch (operationType) {
310 | case kHJOperationType_startAll:
311 | [self startWithDownloadModel:downloadModel];
312 | break;
313 | case kHJOperationType_suspendAll:
314 | [self suspendWithDownloadModel:downloadModel forAll:YES];
315 | break;
316 | case kHJOperationType_resumeAll:
317 | [self resumeWithDownloadModel:downloadModel];
318 | break;
319 | case kHJOperationType_stopAll:
320 | [self stopWithDownloadModel:downloadModel forAll:YES];
321 | break;
322 | default:
323 | break;
324 | }
325 | }];
326 | }
327 |
328 |
329 | /**
330 | * 从备份恢复下载数据
331 | */
332 | - (void)recoverDownloadModels
333 | {
334 | if ([kFileManager fileExistsAtPath:HJSavedDownloadModelsBackup]) {
335 | NSError * error = nil;
336 | [kFileManager removeItemAtPath:HJSavedDownloadModelsFilePath error:nil];
337 | BOOL recoverSuccess = [kFileManager copyItemAtPath:HJSavedDownloadModelsBackup toPath:HJSavedDownloadModelsFilePath error:&error];
338 | if (recoverSuccess) {
339 | NSLog(@"Tip:数据恢复成功");
340 |
341 | [self.downloadModels enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
342 | HJDownloadModel *model = (HJDownloadModel *)obj;
343 | if (model.status == kHJDownloadStatus_Running ||
344 | model.status == kHJDownloadStatus_Waiting){
345 | [self startWithDownloadModel:model];
346 | }
347 | }];
348 | }else{
349 | NSLog(@"Tip:数据恢复失败,%@",error);
350 | }
351 | }
352 | }
353 |
354 | #pragma mark - 文件相关
355 | /**
356 | * 保存下载模型
357 | */
358 | - (void)saveData
359 | {
360 | [kFileManager removeItemAtPath:HJSavedDownloadModelsFilePath error:nil];
361 | BOOL flag = [NSKeyedArchiver archiveRootObject:self.downloadModels toFile:HJSavedDownloadModelsFilePath];
362 | NSLog(@"Tip:下载数据保存路径%@",HJSavedDownloadModelsFilePath);
363 | NSLog(@"Tip:下载数据保存-%@",flag?@"成功!":@"失败");
364 |
365 | if (flag) {
366 | [self backupFile];
367 | }
368 | }
369 | /**
370 | * 备份下载模型
371 | */
372 | - (void)backupFile
373 | {
374 | dispatch_async(dispatch_get_global_queue(0, 0), ^{
375 | NSError *error = nil;
376 | [self removeBackupFile];
377 | BOOL exist = [kFileManager fileExistsAtPath:HJSavedDownloadModelsFilePath];
378 | if (exist) {
379 | BOOL backupSuccess = [kFileManager copyItemAtPath:HJSavedDownloadModelsFilePath toPath:HJSavedDownloadModelsBackup error:&error];
380 | if (backupSuccess) {
381 | NSLog(@"Tip:数据备份成功");
382 | }else{
383 | NSLog(@"Tip:数据备份失败,%@",error);
384 | [self backupFile];
385 | }
386 | }
387 | });
388 | }
389 | /**
390 | * 移除备份
391 | */
392 | - (void)removeBackupFile
393 | {
394 | if ([kFileManager fileExistsAtPath:HJSavedDownloadModelsBackup]) {
395 | NSError * error = nil;
396 | BOOL success = [kFileManager removeItemAtPath:HJSavedDownloadModelsBackup error:&error];
397 | if (success) {
398 | NSLog(@"Tip:备份移除成功");
399 | }else{
400 | NSLog(@"Tip:备份移除失败,%@",error);
401 | }
402 | }
403 | }
404 |
405 | /**
406 | * 移除目录中所有文件
407 | */
408 | - (void)removeAllFiles
409 | {
410 | //返回路径中的文件数组
411 | NSArray * files = [[NSFileManager defaultManager] subpathsAtPath:HJCachesDirectory];
412 |
413 | for(NSString *p in files){
414 | NSError*error;
415 |
416 | NSString*path = [HJCachesDirectory stringByAppendingString:[NSString stringWithFormat:@"/%@",p]];
417 |
418 | if([[NSFileManager defaultManager] fileExistsAtPath:path]){
419 | BOOL isRemove = [[NSFileManager defaultManager]removeItemAtPath:path error:&error];
420 | if(isRemove) {
421 | NSLog(@"文件:%@-->清除成功",p);
422 | }else{
423 | NSLog(@"文件:%@-->清除失败",p);
424 | }
425 | }
426 | }
427 | }
428 |
429 | #pragma mark - Private Method
430 |
431 | #pragma mark - Getters/Setters
432 | - (NSMutableArray *)downloadModels
433 | {
434 | if (!_downloadModels) {
435 | //查看本地是否有数据
436 | NSFileManager *fileManager = [NSFileManager defaultManager];
437 | BOOL exist = [fileManager fileExistsAtPath:HJSavedDownloadModelsFilePath isDirectory:nil];
438 |
439 | if (exist) {//有 则读取本地数据
440 | _downloadModels = [NSKeyedUnarchiver unarchiveObjectWithFile:HJSavedDownloadModelsFilePath];
441 | }else{
442 | _downloadModels = [NSMutableArray array];
443 | }
444 | }
445 | return _downloadModels;
446 | }
447 |
448 | - (NSMutableArray *)completeModels
449 | {
450 | __block NSMutableArray *tmpArr = [NSMutableArray array];
451 | if (self.downloadModels) {
452 | [self.downloadModels enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
453 | HJDownloadModel *model = obj;
454 | if (model.status == kHJDownloadStatus_Completed) {
455 | [tmpArr addObject:model];
456 | }
457 | }];
458 | }
459 |
460 | _completeModels = tmpArr;
461 | return _completeModels;
462 | }
463 |
464 |
465 | - (NSMutableArray *)downloadingModels
466 | {
467 | __block NSMutableArray *tmpArr = [NSMutableArray array];
468 | if (self.downloadModels) {
469 | [self.downloadModels enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
470 | HJDownloadModel *model = obj;
471 | if (model.status == kHJDownloadStatus_Running) {
472 | [tmpArr addObject:model];
473 | }
474 | }];
475 | }
476 |
477 | _downloadingModels = tmpArr;
478 | return _downloadingModels;
479 | }
480 |
481 |
482 |
483 |
484 | - (NSMutableArray *)waitModels
485 | {
486 | __block NSMutableArray *tmpArr = [NSMutableArray array];
487 | if (self.downloadModels) {
488 | [self.downloadModels enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
489 | HJDownloadModel *model = obj;
490 | if (model.status == kHJDownloadStatus_Waiting) {
491 | [tmpArr addObject:model];
492 | }
493 | }];
494 | }
495 | return tmpArr;
496 | }
497 |
498 |
499 |
500 | - (NSMutableArray *)pauseModels
501 | {
502 | __block NSMutableArray *tmpArr = [NSMutableArray array];
503 | if (self.downloadModels) {
504 | [self.downloadModels enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
505 | HJDownloadModel *model = obj;
506 | if (model.status == kHJDownloadStatus_Suspended) {
507 | [tmpArr addObject:model];
508 | }
509 | }];
510 | }
511 | _pauseModels = tmpArr;
512 | return _pauseModels;
513 | }
514 |
515 |
516 |
517 | - (NSOperationQueue *)queue
518 | {
519 | if (!_queue) {
520 | _queue = [[NSOperationQueue alloc] init];
521 | [_queue setMaxConcurrentOperationCount:HJDownloadMaxConcurrentOperationCount];
522 | }
523 | return _queue;
524 | }
525 |
526 |
527 | - (void)setMaxConcurrentOperationCount:(NSInteger)maxConcurrentOperationCount
528 | {
529 | _maxConcurrentOperationCount = maxConcurrentOperationCount;
530 | self.queue.maxConcurrentOperationCount = _maxConcurrentOperationCount;
531 | }
532 |
533 |
534 | - (NSURLSession *)backgroundSession
535 | {
536 | if (!_backgroundSession) {
537 | NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:[[NSBundle mainBundle] bundleIdentifier]];
538 | //不能传self.queue
539 | _backgroundSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
540 | }
541 |
542 | return _backgroundSession;
543 | }
544 |
545 |
546 | - (BOOL)enableProgressLog
547 | {
548 | return _enableProgressLog;
549 | }
550 |
551 | #pragma mark - 后台任务相关
552 | /**
553 | * 获取后台任务
554 | */
555 | - (void)getBackgroundTask
556 | {
557 | NSLog(@"getBackgroundTask");
558 | UIBackgroundTaskIdentifier tempTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
559 |
560 | }];
561 |
562 | if (bgTask != UIBackgroundTaskInvalid) {
563 |
564 | [self endBackgroundTask];
565 | }
566 |
567 | bgTask = tempTask;
568 |
569 | [self performSelector:@selector(getBackgroundTask) withObject:nil afterDelay:120];
570 | }
571 |
572 |
573 | /**
574 | * 结束后台任务
575 | */
576 | - (void)endBackgroundTask
577 | {
578 | [[UIApplication sharedApplication] endBackgroundTask:bgTask];
579 | bgTask = UIBackgroundTaskInvalid;
580 | }
581 |
582 |
583 |
584 | #pragma mark - Event Response
585 | /**
586 | * 应用强关或闪退时 保存下载数据
587 | */
588 | - (void)applicationWillTerminate
589 | {
590 | [self saveData];
591 | }
592 |
593 |
594 | #pragma mark - NSURLSessionDataDelegate
595 | /**
596 | * 接收到响应
597 | */
598 | - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSHTTPURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
599 | {
600 |
601 | HJDownloadModel *downloadModel = dataTask.downloadModel;
602 |
603 | // 打开流
604 | [downloadModel.stream open];
605 |
606 | // 获得服务器这次请求 返回数据的总长度
607 | NSInteger totalLength = [response.allHeaderFields[@"Content-Length"] integerValue] + downloadModel.fileDownloadSize;
608 | downloadModel.fileTotalSize = totalLength;
609 |
610 | // 接收这个请求,允许接收服务器的数据
611 | completionHandler(NSURLSessionResponseAllow);
612 | }
613 |
614 | /**
615 | * 接收到服务器返回的数据
616 | */
617 | - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
618 | {
619 |
620 | NSLog(@"还在执行!");
621 | if (!dataTask.downloadModel) {
622 | return;
623 | }
624 |
625 | HJDownloadModel *downloadModel = dataTask.downloadModel;
626 |
627 | // 写入数据
628 | [downloadModel.stream write:data.bytes maxLength:data.length];
629 |
630 | // 下载进度
631 | NSInteger totalBytesWritten = downloadModel.fileDownloadSize;
632 | NSInteger totalBytesExpectedToWrite = downloadModel.fileTotalSize;
633 |
634 | double byts = totalBytesWritten * 1.0 / 1024 /1024;
635 | double total = totalBytesExpectedToWrite * 1.0 / 1024 /1024;
636 | NSString *text = [NSString stringWithFormat:@"%.1lfMB/%.1lfMB",byts,total];
637 |
638 | CGFloat progress = 1.0 * byts / total;
639 |
640 | downloadModel.statusText = text;
641 | downloadModel.progress = progress;
642 | }
643 |
644 | /**
645 | * 请求完毕 下载成功 | 失败
646 | */
647 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
648 | {
649 | HJDownloadModel *downloadModel = task.downloadModel;
650 | [downloadModel.stream close];
651 | downloadModel.stream = nil;
652 | task = nil;
653 | }
654 | @end
655 |
656 |
--------------------------------------------------------------------------------
/HJDownloadManager/HJDownloadClass/HJDownloadModel.h:
--------------------------------------------------------------------------------
1 | //
2 | // HJDownloadModel.h
3 | // HJNetworkService
4 | //
5 | // Created by WHJ on 16/7/5.
6 | // Copyright © 2016年 WHJ. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @class HJDownloadOperation;
13 | @class HJDownloadModel;
14 |
15 |
16 | typedef NS_ENUM(NSUInteger, HJDownloadStatus) {
17 | kHJDownloadStatus_None = 0,
18 | kHJDownloadStatus_Running = 1,
19 | kHJDownloadStatus_Suspended = 2,
20 | kHJDownloadStatus_Completed = 3, // 下载完成
21 | kHJDownloadStatus_Failed = 4, // 下载失败
22 | kHJDownloadStatus_Waiting = 5, // 等待下载
23 | kHJDownloadStatus_Cancel = 6, // 取消下载
24 | };
25 |
26 |
27 | typedef void(^DownloadStatusChanged)(HJDownloadModel *downloadModel);
28 |
29 | typedef void(^DownloadProgressChanged)(HJDownloadModel *downloadModel);
30 |
31 |
32 |
33 | @interface HJDownloadModel : NSObject
34 |
35 |
36 | @property (nonatomic ,copy) NSString * urlString;
37 |
38 | @property (nonatomic, copy) NSString * downloadDesc;//下载描述信息
39 |
40 | @property (nonatomic, copy) NSString * fileName;
41 |
42 | @property (nonatomic, copy) NSString * fileFormat;
43 |
44 | @property (nonatomic, copy) NSString * destinationPath;//文件存放地址
45 |
46 | @property (nonatomic, strong) HJDownloadOperation * operation;//下载操作
47 |
48 | @property (nonatomic, assign) CGFloat progress;
49 |
50 | @property (nonatomic, assign) HJDownloadStatus status;
51 |
52 | @property (nonatomic, copy) NSString * statusText;
53 |
54 | @property (nonatomic, copy) NSString * completeTime;//下载完成时间
55 |
56 | @property (nonatomic, copy) DownloadStatusChanged statusChanged;//状态改变回调
57 |
58 | @property (nonatomic, copy) DownloadProgressChanged progressChanged;//进度改变回调
59 |
60 | @property (nonatomic, assign) BOOL isLast;//数组最后一个模型
61 |
62 |
63 |
64 | /** 文件总大小 */
65 | @property (nonatomic, assign) NSInteger fileTotalSize;
66 | /** 已下载文件大小 */
67 | @property (nonatomic, assign) NSInteger fileDownloadSize;
68 | /** 输出流 */
69 | @property (nonatomic, strong) NSOutputStream *stream;
70 | /** 下载完成 */
71 | @property (nonatomic, assign) BOOL isFinished;
72 |
73 |
74 | @end
75 |
--------------------------------------------------------------------------------
/HJDownloadManager/HJDownloadClass/HJDownloadModel.m:
--------------------------------------------------------------------------------
1 | //
2 | // HJDownloadModel.m
3 | // HJNetworkService
4 | //
5 | // Created by WHJ on 16/7/5.
6 | // Copyright © 2016年 WHJ. All rights reserved.
7 | //
8 |
9 | #import "HJDownloadModel.h"
10 | #import "HJDownloadHeaders.h"
11 |
12 | @implementation HJDownloadModel
13 |
14 | MJCodingImplementation
15 |
16 |
17 | - (NSString *)destinationPath{
18 | _destinationPath = [[HJCachesDirectory stringByAppendingString:self.fileName] stringByAppendingString:self.fileFormat];
19 | return _destinationPath;
20 | }
21 |
22 |
23 | - (NSString *)fileName{
24 | if (!_fileName) {
25 | NSTimeInterval timeInterval = [[NSDate date]timeIntervalSince1970];
26 | //解决多个任务同时开始时 文件重名问题
27 | NSString *timeStr = [NSString stringWithFormat:@"%.6f",timeInterval];
28 | timeStr = [timeStr stringByReplacingOccurrencesOfString:@"." withString:@"_"];
29 | _fileName = [NSString stringWithFormat:@"%@",timeStr];
30 | }
31 | return _fileName;
32 | }
33 |
34 | - (NSString *)fileFormat{
35 | if (!_fileFormat && _urlString) {
36 | NSArray *urlArr = [_urlString componentsSeparatedByString:@"."];
37 | if (urlArr && urlArr.count>1) {
38 | self.fileFormat = [@"." stringByAppendingString:[urlArr lastObject]];
39 | }
40 | }
41 | return _fileFormat;
42 | }
43 |
44 |
45 | - (void)setProgress:(CGFloat)progress{
46 | if (_progress != progress) {
47 | _progress = progress;
48 | }
49 |
50 | if ([kHJDownloadManager enableProgressLog]) {
51 | NSLog(@"%@%@==%@==%.1f%%",self.fileName,self.fileFormat,self.statusText,progress*100*1.0);
52 | }
53 |
54 | if (self.progressChanged) {
55 | self.progressChanged(self);
56 | }
57 | }
58 |
59 |
60 | - (void)setStatus:(HJDownloadStatus)status{
61 |
62 | if (_status != status) {
63 | _status = status;
64 | [self setStatusTextWith:_status];
65 |
66 | if (self.statusChanged) {
67 | self.statusChanged(self);
68 | }
69 | }
70 | }
71 |
72 |
73 | - (void)setUrlString:(NSString *)urlString{
74 | _urlString = urlString;
75 |
76 | NSArray *urlArr = [_urlString componentsSeparatedByString:@"."];
77 | if (urlArr && urlArr.count>1) {
78 | self.fileFormat = [@"." stringByAppendingString:[urlArr lastObject]];
79 | }
80 | }
81 |
82 | - (void)setCompleteTime:(NSString *)completeTime{
83 | NSDateFormatter *fomatter = [[NSDateFormatter alloc]init];
84 | _completeTime = [fomatter stringFromDate:[NSDate date]];
85 | }
86 |
87 |
88 | - (void)setStatusTextWith:(HJDownloadStatus)status{
89 | _status = status;
90 |
91 | switch (status) {
92 | case kHJDownloadStatus_Running:
93 | self.statusText = @"正在下载";
94 | break;
95 | case kHJDownloadStatus_Suspended:
96 | self.statusText = @"暂停下载";
97 | break;
98 | case kHJDownloadStatus_Failed:
99 | self.statusText = @"下载失败";
100 | break;
101 | case kHJDownloadStatus_Cancel:
102 | self.statusText = @"取消下载";
103 | break;
104 | case kHJDownloadStatus_Waiting:
105 | self.statusText = @"等待下载";
106 | break;
107 | case kHJDownloadStatus_Completed:
108 | self.statusText = @"下载完成";
109 | break;
110 | default:
111 | break;
112 | }
113 |
114 | NSLog(@"%@%@==%@",self.fileName,self.fileFormat,self.statusText);
115 | }
116 |
117 |
118 |
119 |
120 |
121 | + (NSArray *)mj_ignoredCodingPropertyNames{
122 |
123 | return @[@"statusChanged",@"progressChanged",@"stream",@"operation"];
124 | }
125 |
126 |
127 |
128 | - (NSInteger)fileDownloadSize{
129 | // 获取文件下载长度
130 | NSInteger fileDownloadSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:self.destinationPath error:nil][NSFileSize] integerValue];
131 | _fileDownloadSize = fileDownloadSize;
132 | return _fileDownloadSize;
133 | }
134 |
135 |
136 |
137 | - (NSOutputStream *)stream{
138 | if (!_stream) {
139 | _stream = [NSOutputStream outputStreamToFileAtPath:self.destinationPath append:YES];
140 | }
141 | return _stream;
142 | }
143 |
144 | - (BOOL)isFinished{
145 |
146 | return (self.fileTotalSize == self.fileDownloadSize) && (self.fileTotalSize != 0);
147 | }
148 |
149 |
150 |
151 | @end
152 |
--------------------------------------------------------------------------------
/HJDownloadManager/HJDownloadClass/HJDownloadOperation.h:
--------------------------------------------------------------------------------
1 | //
2 | // HJDownloadOperation.h
3 | // HJNetworkService
4 | //
5 | // Created by WHJ on 16/7/5.
6 | // Copyright © 2016年 WHJ. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | typedef void(^DownloadStatusChangedBlock)(void);
12 |
13 | @class HJDownloadModel;
14 |
15 | @interface HJDownloadOperation : NSOperation
16 |
17 |
18 | @property (nonatomic, weak) HJDownloadModel * downloadModel;
19 |
20 | @property (nonatomic, strong) NSURLSessionDataTask * downloadTask;
21 |
22 | @property (nonatomic ,weak) NSURLSession *session;
23 |
24 | /** 下载状态改变回调 */
25 | @property (nonatomic, copy) DownloadStatusChangedBlock downloadStatusChangedBlock ;
26 |
27 | - (instancetype)initWithDownloadModel:(HJDownloadModel *)downloadModel andSession:(NSURLSession *)session;
28 |
29 |
30 | - (void)suspend;
31 | - (void)resume;
32 |
33 |
34 | @end
35 |
--------------------------------------------------------------------------------
/HJDownloadManager/HJDownloadClass/HJDownloadOperation.m:
--------------------------------------------------------------------------------
1 | //
2 | // HJDownloadOperation.m
3 | // HJNetworkService
4 | //
5 | // Created by WHJ on 16/7/5.
6 | // Copyright © 2016年 WHJ. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "HJDownloadHeaders.h"
11 |
12 | #define kKVOBlock(KEYPATH,BLOCK)\
13 | [self willChangeValueForKey:KEYPATH];\
14 | BLOCK();\
15 | [self didChangeValueForKey:KEYPATH];
16 |
17 |
18 | @interface HJDownloadOperation (){
19 |
20 | BOOL _executing;
21 | BOOL _finished;
22 | }
23 |
24 | @property (nonatomic, strong) NSLock *lock;
25 |
26 | @property (nonatomic, assign) BOOL taskIsFinished;
27 |
28 | @end
29 |
30 |
31 | static const NSTimeInterval kTimeoutInterval = 60;
32 |
33 | static NSString * const kIsExecuting = @"isExecuting";
34 |
35 | static NSString * const kIsCancelled = @"isCancelled";
36 |
37 | static NSString * const kIsFinished = @"isFinished";
38 |
39 | @implementation HJDownloadOperation
40 |
41 | MJCodingImplementation
42 |
43 | - (instancetype)initWithDownloadModel:(HJDownloadModel *)downloadModel andSession:(NSURLSession *)session{
44 | self = [super init];
45 | if (self) {
46 | self.downloadModel = downloadModel;
47 | self.session = session;
48 | self.downloadModel.status = kHJDownloadStatus_Waiting;
49 | }
50 | return self;
51 | }
52 |
53 |
54 | - (void)dealloc{
55 | NSLog(@"任务已销毁");
56 | }
57 |
58 |
59 | #pragma mark - Public Method
60 | - (void)startRequest{
61 |
62 | //已下载完成 || 任务未就绪 --> 则直接返回
63 | if (self.downloadModel.isFinished || !self.isReady) {
64 | return;
65 | }
66 | // 创建请求
67 | NSURL *url = [NSURL URLWithString:self.downloadModel.urlString];
68 | NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:kTimeoutInterval];
69 |
70 | // 设置请求头
71 | NSString *range = [NSString stringWithFormat:@"bytes=%zd-", self.downloadModel.fileDownloadSize];
72 | [request setValue:range forHTTPHeaderField:@"Range"];
73 |
74 | if(!self.downloadTask){
75 | self.downloadTask = [self.session dataTaskWithRequest:request];
76 | }
77 |
78 | self.downloadTask.downloadModel = self.downloadModel;
79 | [self addObserver];
80 |
81 | [self.downloadTask resume];
82 | }
83 |
84 | // 进行检索获取Key
85 | - (BOOL)observerKeyPath:(NSString *)key observer:(id )observer{
86 |
87 | id info = self.downloadTask.observationInfo;
88 | NSArray *array = [info valueForKey:@"_observances"];
89 | for (id objc in array) {
90 | id Properties = [objc valueForKeyPath:@"_property"];
91 | id newObserver = [objc valueForKeyPath:@"_observer"];
92 |
93 | NSString *keyPath = [Properties valueForKeyPath:@"_keyPath"];
94 | if ([key isEqualToString:keyPath] && [newObserver isEqual:observer]) {
95 | return YES;
96 | }
97 | }
98 | return NO;
99 | }
100 |
101 | - (void)addObserver{
102 |
103 | if (![self observerKeyPath:@"state" observer:self]) {
104 | [self.downloadTask addObserver:self
105 | forKeyPath:@"state"
106 | options:NSKeyValueObservingOptionNew
107 | context:nil];
108 | }
109 | }
110 |
111 | - (void)removeObserver{
112 |
113 | if ([self observerKeyPath:@"state" observer:self]){
114 | [self.downloadTask removeObserver:self forKeyPath:@"state"];
115 | }
116 | }
117 |
118 | /** 挂起任务 */
119 | - (void)suspend{
120 |
121 | NSLog(@"%@: currentThread = %@", NSStringFromSelector(_cmd), [NSThread currentThread]);
122 |
123 | kKVOBlock(kIsExecuting, ^{
124 | [self.downloadTask suspend];
125 | _executing = NO;
126 | });
127 | }
128 |
129 | /** 开始执行任务 */
130 | - (void)startExcuting{
131 |
132 | NSLog(@"%@: currentThread = %@", NSStringFromSelector(_cmd), [NSThread currentThread]);
133 |
134 | kKVOBlock(kIsExecuting, ^{
135 | [self startRequest];
136 | _executing = YES;
137 | });
138 | }
139 |
140 |
141 | /** 开始执行任务 */
142 | - (void)resume{
143 |
144 | //等待中的任务交给队列调度
145 | if (self.downloadModel.status == kHJDownloadStatus_Waiting)
146 | return;
147 |
148 | kKVOBlock(kIsExecuting, ^{
149 | [self startRequest];
150 | _executing = YES;
151 | });
152 | }
153 |
154 |
155 | /** 任务已完成 */
156 | - (void)completeOperation{
157 |
158 | [self willChangeValueForKey:kIsFinished];
159 | [self willChangeValueForKey:kIsExecuting];
160 |
161 | _executing = NO;
162 | _finished = YES;
163 |
164 | [self didChangeValueForKey:kIsExecuting];
165 | [self didChangeValueForKey:kIsFinished];
166 | }
167 |
168 |
169 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
170 |
171 | if ([keyPath isEqualToString:@"state"]) {
172 |
173 | NSInteger newState = [[change objectForKey:@"new"] integerValue];
174 | NSInteger oldState = [[change objectForKey:@"old"] integerValue];
175 |
176 | switch (newState) {
177 | case NSURLSessionTaskStateSuspended:
178 | self.downloadModel.status = kHJDownloadStatus_Suspended;
179 | //为进行任务管理 暂停任务后 直接取消
180 | [self cancel];
181 | break;
182 | case NSURLSessionTaskStateCompleted:{
183 | if (self.downloadModel.isFinished) {
184 | self.downloadModel.status = kHJDownloadStatus_Completed;
185 | [self cancel];
186 | }else{
187 | if (self.downloadModel.status == kHJDownloadStatus_Suspended) {
188 | }else{// 下载失败
189 | self.downloadModel.status = kHJDownloadStatus_Failed;
190 | }
191 | }
192 | }break;
193 | case NSURLSessionTaskStateRunning:
194 | self.downloadModel.status = kHJDownloadStatus_Running;
195 | break;
196 | case NSURLSessionTaskStateCanceling:
197 | self.taskIsFinished = YES;
198 | break;
199 | default:
200 | break;
201 | }
202 |
203 | if (newState != oldState) {
204 | if (self.downloadStatusChangedBlock) {
205 | self.downloadStatusChangedBlock();
206 | }
207 | }
208 | }
209 | }
210 |
211 |
212 |
213 | #pragma mark - Override Methods
214 | - (void)start{
215 |
216 | self.lock = [[NSLock alloc] init];
217 | [self.lock lock];
218 | //重写start方法时,要做好isCannelled的判断
219 | if ([self isCancelled]){
220 | //若已取消则设置状态已完成
221 | kKVOBlock(kIsFinished, ^{
222 | _finished = YES;
223 | });
224 | return;
225 | }
226 |
227 | kKVOBlock(kIsExecuting, ^{
228 | _executing = YES;
229 | });
230 |
231 | //未取消则调用main方法来执行任务
232 | //经测试 加入operationQueue中后会自动开启新的线程执行 无需手动开启
233 | [NSThread currentThread].name = self.downloadModel.downloadDesc;
234 | [NSThread mainThread].name = @"主线程";
235 | [self main];
236 | [self.lock unlock];
237 | }
238 |
239 |
240 | - (void)main{
241 |
242 | @try {
243 | // 必须为自定义的 operation 提供 autorelease pool,因为 operation 完成后需要销毁。
244 | @autoreleasepool {
245 | // 提供一个变量标识,来表示需要执行的操作是否完成了,当然,没开始执行之前,为NO
246 | _taskIsFinished = NO;
247 |
248 | //只有当没有执行完成和没有被取消,才执行自定义的相应操作
249 | if (self.taskIsFinished == NO && [self isCancelled] == NO) {
250 | [self startExcuting];
251 | }
252 |
253 | }
254 | }@catch (NSException * e) {
255 | NSLog(@"Exception %@", e);
256 | }
257 | }
258 |
259 | - (BOOL)isExecuting{
260 | return _executing;
261 | }
262 |
263 |
264 | - (BOOL)isFinished{
265 | return _finished;
266 | }
267 |
268 | - (BOOL)isAsynchronous{
269 | return YES;
270 | }
271 | /**
272 | * 1.cancel方法调用后 该operation将会取消并从queue中移除
273 | * 2.若队列中有等待中的任务,将会自动执行
274 | */
275 | - (void)cancel{
276 |
277 | BOOL isWaiting = self.downloadModel.status == kHJDownloadStatus_Waiting;
278 | [self.downloadTask cancel];
279 | [self removeObserver];
280 | [super cancel];
281 | //等待状态下取消时 无需将isFinished设置为已完成 等调用start方法时检测canceled来设置
282 | //参考:https://blog.csdn.net/loggerhuang/article/details/50015573
283 | if (!isWaiting) {
284 | [self completeOperation];
285 | }
286 | }
287 | #pragma mark - Private Methods
288 | @end
289 |
290 |
--------------------------------------------------------------------------------
/HJDownloadManager/HJDownloadClass/HJUncaughtExceptionHandler.h:
--------------------------------------------------------------------------------
1 | //
2 | // HJUncaughtExceptionHandler.h
3 | // HJDownloadManager
4 | //
5 | // Created by WHJ on 2018/3/27.
6 | // Copyright © 2018年 WHJ. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 |
12 | static NSString * const kNotificationUncaughtException = @"kNotificationUncaughtException";
13 |
14 | @interface HJUncaughtExceptionHandler : NSObject
15 |
16 | + (void)setDefaultHandler;
17 | + (NSUncaughtExceptionHandler *)getHandler;
18 | + (void)TakeException:(NSException *) exception;
19 |
20 | @end
21 |
--------------------------------------------------------------------------------
/HJDownloadManager/HJDownloadClass/HJUncaughtExceptionHandler.m:
--------------------------------------------------------------------------------
1 | //
2 | // HJUncaughtExceptionHandler.m
3 | // HJDownloadManager
4 | //
5 | // Created by WHJ on 2018/3/27.
6 | // Copyright © 2018年 WHJ. All rights reserved.
7 | //
8 |
9 | #import "HJUncaughtExceptionHandler.h"
10 | #import
11 | // 沙盒的地址
12 | NSString * applicationDocumentsDirectory() {
13 | return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
14 | }
15 |
16 | // 崩溃时的回调函数
17 | void UncaughtExceptionHandler(NSException * exception) {
18 | NSArray * arr = [exception callStackSymbols];
19 | NSString * reason = [exception reason]; // // 崩溃的原因 可以有崩溃的原因(数组越界,字典nil,调用未知方法...) 崩溃的控制器以及方法
20 | NSString * name = [exception name];
21 | NSString * url = [NSString stringWithFormat:@"========异常错误报告========\nname:%@\nreason:\n%@\ncallStackSymbols:\n%@",name,reason,[arr componentsJoinedByString:@"\n"]];
22 | NSString * path = [applicationDocumentsDirectory() stringByAppendingPathComponent:@"Exception.txt"];
23 | // 将一个txt文件写入沙盒
24 | [url writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:nil];
25 |
26 | [[NSNotificationCenter defaultCenter] postNotificationName:kNotificationUncaughtException object:nil];
27 |
28 | }
29 |
30 |
31 | @implementation HJUncaughtExceptionHandler
32 |
33 | // 沙盒地址
34 | - (NSString *)applicationDocumentsDirectory {
35 | return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
36 | }
37 |
38 | + (void)setDefaultHandler {
39 | NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler);
40 | }
41 |
42 | + (NSUncaughtExceptionHandler *)getHandler {
43 | return NSGetUncaughtExceptionHandler();
44 | }
45 |
46 | + (void)TakeException:(NSException *)exception {
47 | NSArray * arr = [exception callStackSymbols];
48 | NSString * reason = [exception reason];
49 | NSString * name = [exception name];
50 | NSString * url = [NSString stringWithFormat:@"========异常错误报告========\nname:%@\nreason:\n%@\ncallStackSymbols:\n%@",name,reason,[arr componentsJoinedByString:@"\n"]];
51 | NSString * path = [applicationDocumentsDirectory() stringByAppendingPathComponent:@"Exception.txt"];
52 | [url writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:nil];
53 | }
54 |
55 | @end
56 |
--------------------------------------------------------------------------------
/HJDownloadManager/HJDownloadClass/NSURLSessionTask+HJModel.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSURLSessionTask+HJModel.h
3 | // HJNetworkService
4 | //
5 | // Created by WHJ on 16/7/5.
6 | // Copyright © 2016年 WHJ. All rights reserved.
7 | //
8 |
9 | #import
10 | @class HJDownloadModel;
11 |
12 | @interface NSURLSessionTask (HJModel)
13 |
14 | @property (nonatomic, weak)HJDownloadModel * downloadModel;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/HJDownloadManager/HJDownloadClass/NSURLSessionTask+HJModel.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSURLSessionTask+HJModel.m
3 | // HJNetworkService
4 | //
5 | // Created by WHJ on 16/7/5.
6 | // Copyright © 2016年 WHJ. All rights reserved.
7 | //
8 |
9 | #import "NSURLSessionTask+HJModel.h"
10 | #import
11 | #import "HJDownloadModel.h"
12 | #import "MJExtension.h"
13 |
14 |
15 | @implementation NSURLSessionTask (HJModel)
16 |
17 | /**
18 | * 添加downloadModel属性
19 | */
20 |
21 | static const void *hj_downloadModelKey = @"downloadModelKey";
22 |
23 | - (void)setDownloadModel:(HJDownloadModel *)downloadModel{
24 |
25 | objc_setAssociatedObject(self, &hj_downloadModelKey, downloadModel, OBJC_ASSOCIATION_ASSIGN);
26 | }
27 |
28 |
29 | - (HJDownloadModel *)downloadModel{
30 |
31 | return objc_getAssociatedObject(self, &hj_downloadModelKey);
32 | }
33 |
34 |
35 | @end
36 |
--------------------------------------------------------------------------------
/HJDownloadManager/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | NSAppTransportSecurity
24 |
25 | NSAllowsArbitraryLoads
26 |
27 |
28 | UILaunchStoryboardName
29 | LaunchScreen
30 | UIRequiredDeviceCapabilities
31 |
32 | armv7
33 |
34 | UISupportedInterfaceOrientations
35 |
36 | UIInterfaceOrientationPortrait
37 | UIInterfaceOrientationLandscapeLeft
38 | UIInterfaceOrientationLandscapeRight
39 |
40 | UISupportedInterfaceOrientations~ipad
41 |
42 | UIInterfaceOrientationPortrait
43 | UIInterfaceOrientationPortraitUpsideDown
44 | UIInterfaceOrientationLandscapeLeft
45 | UIInterfaceOrientationLandscapeRight
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/HJDownloadManager/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // HJDownloadManager
4 | //
5 | // Created by WHJ on 2018/2/21.
6 | // Copyright © 2018年 WHJ. 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 |
--------------------------------------------------------------------------------
/HJDownloadManagerTests/HJDownloadManagerTests.m:
--------------------------------------------------------------------------------
1 | //
2 | // HJDownloadManagerTests.m
3 | // HJDownloadManagerTests
4 | //
5 | // Created by WHJ on 2018/2/21.
6 | // Copyright © 2018年 WHJ. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface HJDownloadManagerTests : XCTestCase
12 |
13 | @end
14 |
15 | @implementation HJDownloadManagerTests
16 |
17 | - (void)setUp {
18 | [super setUp];
19 | // Put setup code here. This method is called before the invocation of each test method in the class.
20 | }
21 |
22 | - (void)tearDown {
23 | // Put teardown code here. This method is called after the invocation of each test method in the class.
24 | [super tearDown];
25 | }
26 |
27 | - (void)testExample {
28 | // This is an example of a functional test case.
29 | // Use XCTAssert and related functions to verify your tests produce the correct results.
30 | }
31 |
32 | - (void)testPerformanceExample {
33 | // This is an example of a performance test case.
34 | [self measureBlock:^{
35 | // Put the code you want to measure the time of here.
36 | }];
37 | }
38 |
39 | @end
40 |
--------------------------------------------------------------------------------
/HJDownloadManagerTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/HJDownloadManagerUITests/HJDownloadManagerUITests.m:
--------------------------------------------------------------------------------
1 | //
2 | // HJDownloadManagerUITests.m
3 | // HJDownloadManagerUITests
4 | //
5 | // Created by WHJ on 2018/2/21.
6 | // Copyright © 2018年 WHJ. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface HJDownloadManagerUITests : XCTestCase
12 |
13 | @end
14 |
15 | @implementation HJDownloadManagerUITests
16 |
17 | - (void)setUp {
18 | [super setUp];
19 |
20 | // Put setup code here. This method is called before the invocation of each test method in the class.
21 |
22 | // In UI tests it is usually best to stop immediately when a failure occurs.
23 | self.continueAfterFailure = NO;
24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
25 | [[[XCUIApplication alloc] init] launch];
26 |
27 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
28 | }
29 |
30 | - (void)tearDown {
31 | // Put teardown code here. This method is called after the invocation of each test method in the class.
32 | [super tearDown];
33 | }
34 |
35 | - (void)testExample {
36 | // Use recording to get started writing UI tests.
37 | // Use XCTAssert and related functions to verify your tests produce the correct results.
38 | }
39 |
40 | @end
41 |
--------------------------------------------------------------------------------
/HJDownloadManagerUITests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - MJExtension (3.0.13)
3 |
4 | DEPENDENCIES:
5 | - MJExtension (~> 3.0.13)
6 |
7 | SPEC CHECKSUMS:
8 | MJExtension: 5932755f451458eefa24239358817f8d291240c7
9 |
10 | PODFILE CHECKSUM: 6e750c1223c98f93978cd39ce2ef24209811a01f
11 |
12 | COCOAPODS: 1.4.0.beta.2
13 |
--------------------------------------------------------------------------------
/Pods/Headers/Private/MJExtension/MJExtension.h:
--------------------------------------------------------------------------------
1 | ../../../MJExtension/MJExtension/MJExtension.h
--------------------------------------------------------------------------------
/Pods/Headers/Private/MJExtension/MJExtensionConst.h:
--------------------------------------------------------------------------------
1 | ../../../MJExtension/MJExtension/MJExtensionConst.h
--------------------------------------------------------------------------------
/Pods/Headers/Private/MJExtension/MJFoundation.h:
--------------------------------------------------------------------------------
1 | ../../../MJExtension/MJExtension/MJFoundation.h
--------------------------------------------------------------------------------
/Pods/Headers/Private/MJExtension/MJProperty.h:
--------------------------------------------------------------------------------
1 | ../../../MJExtension/MJExtension/MJProperty.h
--------------------------------------------------------------------------------
/Pods/Headers/Private/MJExtension/MJPropertyKey.h:
--------------------------------------------------------------------------------
1 | ../../../MJExtension/MJExtension/MJPropertyKey.h
--------------------------------------------------------------------------------
/Pods/Headers/Private/MJExtension/MJPropertyType.h:
--------------------------------------------------------------------------------
1 | ../../../MJExtension/MJExtension/MJPropertyType.h
--------------------------------------------------------------------------------
/Pods/Headers/Private/MJExtension/NSObject+MJClass.h:
--------------------------------------------------------------------------------
1 | ../../../MJExtension/MJExtension/NSObject+MJClass.h
--------------------------------------------------------------------------------
/Pods/Headers/Private/MJExtension/NSObject+MJCoding.h:
--------------------------------------------------------------------------------
1 | ../../../MJExtension/MJExtension/NSObject+MJCoding.h
--------------------------------------------------------------------------------
/Pods/Headers/Private/MJExtension/NSObject+MJKeyValue.h:
--------------------------------------------------------------------------------
1 | ../../../MJExtension/MJExtension/NSObject+MJKeyValue.h
--------------------------------------------------------------------------------
/Pods/Headers/Private/MJExtension/NSObject+MJProperty.h:
--------------------------------------------------------------------------------
1 | ../../../MJExtension/MJExtension/NSObject+MJProperty.h
--------------------------------------------------------------------------------
/Pods/Headers/Private/MJExtension/NSString+MJExtension.h:
--------------------------------------------------------------------------------
1 | ../../../MJExtension/MJExtension/NSString+MJExtension.h
--------------------------------------------------------------------------------
/Pods/Headers/Public/MJExtension/MJExtension.h:
--------------------------------------------------------------------------------
1 | ../../../MJExtension/MJExtension/MJExtension.h
--------------------------------------------------------------------------------
/Pods/Headers/Public/MJExtension/MJExtensionConst.h:
--------------------------------------------------------------------------------
1 | ../../../MJExtension/MJExtension/MJExtensionConst.h
--------------------------------------------------------------------------------
/Pods/Headers/Public/MJExtension/MJFoundation.h:
--------------------------------------------------------------------------------
1 | ../../../MJExtension/MJExtension/MJFoundation.h
--------------------------------------------------------------------------------
/Pods/Headers/Public/MJExtension/MJProperty.h:
--------------------------------------------------------------------------------
1 | ../../../MJExtension/MJExtension/MJProperty.h
--------------------------------------------------------------------------------
/Pods/Headers/Public/MJExtension/MJPropertyKey.h:
--------------------------------------------------------------------------------
1 | ../../../MJExtension/MJExtension/MJPropertyKey.h
--------------------------------------------------------------------------------
/Pods/Headers/Public/MJExtension/MJPropertyType.h:
--------------------------------------------------------------------------------
1 | ../../../MJExtension/MJExtension/MJPropertyType.h
--------------------------------------------------------------------------------
/Pods/Headers/Public/MJExtension/NSObject+MJClass.h:
--------------------------------------------------------------------------------
1 | ../../../MJExtension/MJExtension/NSObject+MJClass.h
--------------------------------------------------------------------------------
/Pods/Headers/Public/MJExtension/NSObject+MJCoding.h:
--------------------------------------------------------------------------------
1 | ../../../MJExtension/MJExtension/NSObject+MJCoding.h
--------------------------------------------------------------------------------
/Pods/Headers/Public/MJExtension/NSObject+MJKeyValue.h:
--------------------------------------------------------------------------------
1 | ../../../MJExtension/MJExtension/NSObject+MJKeyValue.h
--------------------------------------------------------------------------------
/Pods/Headers/Public/MJExtension/NSObject+MJProperty.h:
--------------------------------------------------------------------------------
1 | ../../../MJExtension/MJExtension/NSObject+MJProperty.h
--------------------------------------------------------------------------------
/Pods/Headers/Public/MJExtension/NSString+MJExtension.h:
--------------------------------------------------------------------------------
1 | ../../../MJExtension/MJExtension/NSString+MJExtension.h
--------------------------------------------------------------------------------
/Pods/MJExtension/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013-2015 MJExtension (https://github.com/CoderMJLee/MJExtension)
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/Pods/MJExtension/MJExtension/MJExtension.h:
--------------------------------------------------------------------------------
1 | //
2 | // MJExtension.h
3 | // MJExtension
4 | //
5 | // Created by mj on 14-1-15.
6 | // Copyright (c) 2014年 小码哥. All rights reserved.
7 | // 代码地址:https://github.com/CoderMJLee/MJExtension
8 | // 代码地址:http://code4app.com/ios/%E5%AD%97%E5%85%B8-JSON-%E4%B8%8E%E6%A8%A1%E5%9E%8B%E7%9A%84%E8%BD%AC%E6%8D%A2/5339992a933bf062608b4c57
9 |
10 | #import "NSObject+MJCoding.h"
11 | #import "NSObject+MJProperty.h"
12 | #import "NSObject+MJClass.h"
13 | #import "NSObject+MJKeyValue.h"
14 | #import "NSString+MJExtension.h"
15 | #import "MJExtensionConst.h"
--------------------------------------------------------------------------------
/Pods/MJExtension/MJExtension/MJExtensionConst.h:
--------------------------------------------------------------------------------
1 |
2 | #ifndef __MJExtensionConst__H__
3 | #define __MJExtensionConst__H__
4 |
5 | #import
6 |
7 | // 过期
8 | #define MJExtensionDeprecated(instead) NS_DEPRECATED(2_0, 2_0, 2_0, 2_0, instead)
9 |
10 | // 构建错误
11 | #define MJExtensionBuildError(clazz, msg) \
12 | NSError *error = [NSError errorWithDomain:msg code:250 userInfo:nil]; \
13 | [clazz setMj_error:error];
14 |
15 | // 日志输出
16 | #ifdef DEBUG
17 | #define MJExtensionLog(...) NSLog(__VA_ARGS__)
18 | #else
19 | #define MJExtensionLog(...)
20 | #endif
21 |
22 | /**
23 | * 断言
24 | * @param condition 条件
25 | * @param returnValue 返回值
26 | */
27 | #define MJExtensionAssertError(condition, returnValue, clazz, msg) \
28 | [clazz setMj_error:nil]; \
29 | if ((condition) == NO) { \
30 | MJExtensionBuildError(clazz, msg); \
31 | return returnValue;\
32 | }
33 |
34 | #define MJExtensionAssert2(condition, returnValue) \
35 | if ((condition) == NO) return returnValue;
36 |
37 | /**
38 | * 断言
39 | * @param condition 条件
40 | */
41 | #define MJExtensionAssert(condition) MJExtensionAssert2(condition, )
42 |
43 | /**
44 | * 断言
45 | * @param param 参数
46 | * @param returnValue 返回值
47 | */
48 | #define MJExtensionAssertParamNotNil2(param, returnValue) \
49 | MJExtensionAssert2((param) != nil, returnValue)
50 |
51 | /**
52 | * 断言
53 | * @param param 参数
54 | */
55 | #define MJExtensionAssertParamNotNil(param) MJExtensionAssertParamNotNil2(param, )
56 |
57 | /**
58 | * 打印所有的属性
59 | */
60 | #define MJLogAllIvars \
61 | -(NSString *)description \
62 | { \
63 | return [self mj_keyValues].description; \
64 | }
65 | #define MJExtensionLogAllProperties MJLogAllIvars
66 |
67 | /**
68 | * 类型(属性类型)
69 | */
70 | extern NSString *const MJPropertyTypeInt;
71 | extern NSString *const MJPropertyTypeShort;
72 | extern NSString *const MJPropertyTypeFloat;
73 | extern NSString *const MJPropertyTypeDouble;
74 | extern NSString *const MJPropertyTypeLong;
75 | extern NSString *const MJPropertyTypeLongLong;
76 | extern NSString *const MJPropertyTypeChar;
77 | extern NSString *const MJPropertyTypeBOOL1;
78 | extern NSString *const MJPropertyTypeBOOL2;
79 | extern NSString *const MJPropertyTypePointer;
80 |
81 | extern NSString *const MJPropertyTypeIvar;
82 | extern NSString *const MJPropertyTypeMethod;
83 | extern NSString *const MJPropertyTypeBlock;
84 | extern NSString *const MJPropertyTypeClass;
85 | extern NSString *const MJPropertyTypeSEL;
86 | extern NSString *const MJPropertyTypeId;
87 |
88 | #endif
--------------------------------------------------------------------------------
/Pods/MJExtension/MJExtension/MJExtensionConst.m:
--------------------------------------------------------------------------------
1 | #ifndef __MJExtensionConst__M__
2 | #define __MJExtensionConst__M__
3 |
4 | #import
5 |
6 | /**
7 | * 成员变量类型(属性类型)
8 | */
9 | NSString *const MJPropertyTypeInt = @"i";
10 | NSString *const MJPropertyTypeShort = @"s";
11 | NSString *const MJPropertyTypeFloat = @"f";
12 | NSString *const MJPropertyTypeDouble = @"d";
13 | NSString *const MJPropertyTypeLong = @"l";
14 | NSString *const MJPropertyTypeLongLong = @"q";
15 | NSString *const MJPropertyTypeChar = @"c";
16 | NSString *const MJPropertyTypeBOOL1 = @"c";
17 | NSString *const MJPropertyTypeBOOL2 = @"b";
18 | NSString *const MJPropertyTypePointer = @"*";
19 |
20 | NSString *const MJPropertyTypeIvar = @"^{objc_ivar=}";
21 | NSString *const MJPropertyTypeMethod = @"^{objc_method=}";
22 | NSString *const MJPropertyTypeBlock = @"@?";
23 | NSString *const MJPropertyTypeClass = @"#";
24 | NSString *const MJPropertyTypeSEL = @":";
25 | NSString *const MJPropertyTypeId = @"@";
26 |
27 | #endif
--------------------------------------------------------------------------------
/Pods/MJExtension/MJExtension/MJFoundation.h:
--------------------------------------------------------------------------------
1 | //
2 | // MJFoundation.h
3 | // MJExtensionExample
4 | //
5 | // Created by MJ Lee on 14/7/16.
6 | // Copyright (c) 2014年 小码哥. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface MJFoundation : NSObject
12 | + (BOOL)isClassFromFoundation:(Class)c;
13 | @end
14 |
--------------------------------------------------------------------------------
/Pods/MJExtension/MJExtension/MJFoundation.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJFoundation.m
3 | // MJExtensionExample
4 | //
5 | // Created by MJ Lee on 14/7/16.
6 | // Copyright (c) 2014年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJFoundation.h"
10 | #import "MJExtensionConst.h"
11 | #import
12 |
13 | static NSSet *foundationClasses_;
14 |
15 | @implementation MJFoundation
16 |
17 | + (NSSet *)foundationClasses
18 | {
19 | if (foundationClasses_ == nil) {
20 | // 集合中没有NSObject,因为几乎所有的类都是继承自NSObject,具体是不是NSObject需要特殊判断
21 | foundationClasses_ = [NSSet setWithObjects:
22 | [NSURL class],
23 | [NSDate class],
24 | [NSValue class],
25 | [NSData class],
26 | [NSError class],
27 | [NSArray class],
28 | [NSDictionary class],
29 | [NSString class],
30 | [NSAttributedString class], nil];
31 | }
32 | return foundationClasses_;
33 | }
34 |
35 | + (BOOL)isClassFromFoundation:(Class)c
36 | {
37 | if (c == [NSObject class] || c == [NSManagedObject class]) return YES;
38 |
39 | __block BOOL result = NO;
40 | [[self foundationClasses] enumerateObjectsUsingBlock:^(Class foundationClass, BOOL *stop) {
41 | if ([c isSubclassOfClass:foundationClass]) {
42 | result = YES;
43 | *stop = YES;
44 | }
45 | }];
46 | return result;
47 | }
48 | @end
49 |
--------------------------------------------------------------------------------
/Pods/MJExtension/MJExtension/MJProperty.h:
--------------------------------------------------------------------------------
1 | //
2 | // MJProperty.h
3 | // MJExtensionExample
4 | //
5 | // Created by MJ Lee on 15/4/17.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | // 包装一个成员属性
8 |
9 | #import
10 | #import
11 | #import "MJPropertyType.h"
12 | #import "MJPropertyKey.h"
13 |
14 | /**
15 | * 包装一个成员
16 | */
17 | @interface MJProperty : NSObject
18 | /** 成员属性 */
19 | @property (nonatomic, assign) objc_property_t property;
20 | /** 成员属性的名字 */
21 | @property (nonatomic, readonly) NSString *name;
22 |
23 | /** 成员属性的类型 */
24 | @property (nonatomic, readonly) MJPropertyType *type;
25 | /** 成员属性来源于哪个类(可能是父类) */
26 | @property (nonatomic, assign) Class srcClass;
27 |
28 | /**** 同一个成员属性 - 父类和子类的行为可能不一致(originKey、propertyKeys、objectClassInArray) ****/
29 | /** 设置最原始的key */
30 | - (void)setOriginKey:(id)originKey forClass:(Class)c;
31 | /** 对应着字典中的多级key(里面存放的数组,数组里面都是MJPropertyKey对象) */
32 | - (NSArray *)propertyKeysForClass:(Class)c;
33 |
34 | /** 模型数组中的模型类型 */
35 | - (void)setObjectClassInArray:(Class)objectClass forClass:(Class)c;
36 | - (Class)objectClassInArrayForClass:(Class)c;
37 | /**** 同一个成员变量 - 父类和子类的行为可能不一致(key、keys、objectClassInArray) ****/
38 |
39 | /**
40 | * 设置object的成员变量值
41 | */
42 | - (void)setValue:(id)value forObject:(id)object;
43 | /**
44 | * 得到object的成员属性值
45 | */
46 | - (id)valueForObject:(id)object;
47 |
48 | /**
49 | * 初始化
50 | */
51 | + (instancetype)cachedPropertyWithProperty:(objc_property_t)property;
52 |
53 | @end
54 |
--------------------------------------------------------------------------------
/Pods/MJExtension/MJExtension/MJProperty.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJProperty.m
3 | // MJExtensionExample
4 | //
5 | // Created by MJ Lee on 15/4/17.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJProperty.h"
10 | #import "MJFoundation.h"
11 | #import "MJExtensionConst.h"
12 | #import
13 |
14 | @interface MJProperty()
15 | @property (strong, nonatomic) NSMutableDictionary *propertyKeysDict;
16 | @property (strong, nonatomic) NSMutableDictionary *objectClassInArrayDict;
17 | @end
18 |
19 | @implementation MJProperty
20 |
21 | #pragma mark - 初始化
22 | - (instancetype)init
23 | {
24 | if (self = [super init]) {
25 | _propertyKeysDict = [NSMutableDictionary dictionary];
26 | _objectClassInArrayDict = [NSMutableDictionary dictionary];
27 | }
28 | return self;
29 | }
30 |
31 | #pragma mark - 缓存
32 | + (instancetype)cachedPropertyWithProperty:(objc_property_t)property
33 | {
34 | MJProperty *propertyObj = objc_getAssociatedObject(self, property);
35 | if (propertyObj == nil) {
36 | propertyObj = [[self alloc] init];
37 | propertyObj.property = property;
38 | objc_setAssociatedObject(self, property, propertyObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
39 | }
40 | return propertyObj;
41 | }
42 |
43 | #pragma mark - 公共方法
44 | - (void)setProperty:(objc_property_t)property
45 | {
46 | _property = property;
47 |
48 | MJExtensionAssertParamNotNil(property);
49 |
50 | // 1.属性名
51 | _name = @(property_getName(property));
52 |
53 | // 2.成员类型
54 | NSString *attrs = @(property_getAttributes(property));
55 | NSUInteger dotLoc = [attrs rangeOfString:@","].location;
56 | NSString *code = nil;
57 | NSUInteger loc = 1;
58 | if (dotLoc == NSNotFound) { // 没有,
59 | code = [attrs substringFromIndex:loc];
60 | } else {
61 | code = [attrs substringWithRange:NSMakeRange(loc, dotLoc - loc)];
62 | }
63 | _type = [MJPropertyType cachedTypeWithCode:code];
64 | }
65 |
66 | /**
67 | * 获得成员变量的值
68 | */
69 | - (id)valueForObject:(id)object
70 | {
71 | if (self.type.KVCDisabled) return [NSNull null];
72 | return [object valueForKey:self.name];
73 | }
74 |
75 | /**
76 | * 设置成员变量的值
77 | */
78 | - (void)setValue:(id)value forObject:(id)object
79 | {
80 | if (self.type.KVCDisabled || value == nil) return;
81 | [object setValue:value forKey:self.name];
82 | }
83 |
84 | /**
85 | * 通过字符串key创建对应的keys
86 | */
87 | - (NSArray *)propertyKeysWithStringKey:(NSString *)stringKey
88 | {
89 | if (stringKey.length == 0) return nil;
90 |
91 | NSMutableArray *propertyKeys = [NSMutableArray array];
92 | // 如果有多级映射
93 | NSArray *oldKeys = [stringKey componentsSeparatedByString:@"."];
94 |
95 | for (NSString *oldKey in oldKeys) {
96 | NSUInteger start = [oldKey rangeOfString:@"["].location;
97 | if (start != NSNotFound) { // 有索引的key
98 | NSString *prefixKey = [oldKey substringToIndex:start];
99 | NSString *indexKey = prefixKey;
100 | if (prefixKey.length) {
101 | MJPropertyKey *propertyKey = [[MJPropertyKey alloc] init];
102 | propertyKey.name = prefixKey;
103 | [propertyKeys addObject:propertyKey];
104 |
105 | indexKey = [oldKey stringByReplacingOccurrencesOfString:prefixKey withString:@""];
106 | }
107 |
108 | /** 解析索引 **/
109 | // 元素
110 | NSArray *cmps = [[indexKey stringByReplacingOccurrencesOfString:@"[" withString:@""] componentsSeparatedByString:@"]"];
111 | for (NSInteger i = 0; i
10 |
11 | typedef enum {
12 | MJPropertyKeyTypeDictionary = 0, // 字典的key
13 | MJPropertyKeyTypeArray // 数组的key
14 | } MJPropertyKeyType;
15 |
16 | /**
17 | * 属性的key
18 | */
19 | @interface MJPropertyKey : NSObject
20 | /** key的名字 */
21 | @property (copy, nonatomic) NSString *name;
22 | /** key的种类,可能是@"10",可能是@"age" */
23 | @property (assign, nonatomic) MJPropertyKeyType type;
24 |
25 | /**
26 | * 根据当前的key,也就是name,从object(字典或者数组)中取值
27 | */
28 | - (id)valueInObject:(id)object;
29 |
30 | @end
31 |
--------------------------------------------------------------------------------
/Pods/MJExtension/MJExtension/MJPropertyKey.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJPropertyKey.m
3 | // MJExtensionExample
4 | //
5 | // Created by MJ Lee on 15/8/11.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJPropertyKey.h"
10 |
11 | @implementation MJPropertyKey
12 |
13 | - (id)valueInObject:(id)object
14 | {
15 | if ([object isKindOfClass:[NSDictionary class]] && self.type == MJPropertyKeyTypeDictionary) {
16 | return object[self.name];
17 | } else if ([object isKindOfClass:[NSArray class]] && self.type == MJPropertyKeyTypeArray) {
18 | NSArray *array = object;
19 | NSUInteger index = self.name.intValue;
20 | if (index < array.count) return array[index];
21 | return nil;
22 | }
23 | return nil;
24 | }
25 | @end
26 |
--------------------------------------------------------------------------------
/Pods/MJExtension/MJExtension/MJPropertyType.h:
--------------------------------------------------------------------------------
1 | //
2 | // MJPropertyType.h
3 | // MJExtension
4 | //
5 | // Created by mj on 14-1-15.
6 | // Copyright (c) 2014年 小码哥. All rights reserved.
7 | // 包装一种类型
8 |
9 | #import
10 |
11 | /**
12 | * 包装一种类型
13 | */
14 | @interface MJPropertyType : NSObject
15 | /** 类型标识符 */
16 | @property (nonatomic, copy) NSString *code;
17 |
18 | /** 是否为id类型 */
19 | @property (nonatomic, readonly, getter=isIdType) BOOL idType;
20 |
21 | /** 是否为基本数字类型:int、float等 */
22 | @property (nonatomic, readonly, getter=isNumberType) BOOL numberType;
23 |
24 | /** 是否为BOOL类型 */
25 | @property (nonatomic, readonly, getter=isBoolType) BOOL boolType;
26 |
27 | /** 对象类型(如果是基本数据类型,此值为nil) */
28 | @property (nonatomic, readonly) Class typeClass;
29 |
30 | /** 类型是否来自于Foundation框架,比如NSString、NSArray */
31 | @property (nonatomic, readonly, getter = isFromFoundation) BOOL fromFoundation;
32 | /** 类型是否不支持KVC */
33 | @property (nonatomic, readonly, getter = isKVCDisabled) BOOL KVCDisabled;
34 |
35 | /**
36 | * 获得缓存的类型对象
37 | */
38 | + (instancetype)cachedTypeWithCode:(NSString *)code;
39 | @end
--------------------------------------------------------------------------------
/Pods/MJExtension/MJExtension/MJPropertyType.m:
--------------------------------------------------------------------------------
1 | //
2 | // MJPropertyType.m
3 | // MJExtension
4 | //
5 | // Created by mj on 14-1-15.
6 | // Copyright (c) 2014年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "MJPropertyType.h"
10 | #import "MJExtension.h"
11 | #import "MJFoundation.h"
12 | #import "MJExtensionConst.h"
13 |
14 | @implementation MJPropertyType
15 |
16 | static NSMutableDictionary *types_;
17 | + (void)initialize
18 | {
19 | types_ = [NSMutableDictionary dictionary];
20 | }
21 |
22 | + (instancetype)cachedTypeWithCode:(NSString *)code
23 | {
24 | MJExtensionAssertParamNotNil2(code, nil);
25 | @synchronized (self) {
26 | MJPropertyType *type = types_[code];
27 | if (type == nil) {
28 | type = [[self alloc] init];
29 | type.code = code;
30 | types_[code] = type;
31 | }
32 | return type;
33 | }
34 | }
35 |
36 | #pragma mark - 公共方法
37 | - (void)setCode:(NSString *)code
38 | {
39 | _code = code;
40 |
41 | MJExtensionAssertParamNotNil(code);
42 |
43 | if ([code isEqualToString:MJPropertyTypeId]) {
44 | _idType = YES;
45 | } else if (code.length == 0) {
46 | _KVCDisabled = YES;
47 | } else if (code.length > 3 && [code hasPrefix:@"@\""]) {
48 | // 去掉@"和",截取中间的类型名称
49 | _code = [code substringWithRange:NSMakeRange(2, code.length - 3)];
50 | _typeClass = NSClassFromString(_code);
51 | _fromFoundation = [MJFoundation isClassFromFoundation:_typeClass];
52 | _numberType = [_typeClass isSubclassOfClass:[NSNumber class]];
53 |
54 | } else if ([code isEqualToString:MJPropertyTypeSEL] ||
55 | [code isEqualToString:MJPropertyTypeIvar] ||
56 | [code isEqualToString:MJPropertyTypeMethod]) {
57 | _KVCDisabled = YES;
58 | }
59 |
60 | // 是否为数字类型
61 | NSString *lowerCode = _code.lowercaseString;
62 | NSArray *numberTypes = @[MJPropertyTypeInt, MJPropertyTypeShort, MJPropertyTypeBOOL1, MJPropertyTypeBOOL2, MJPropertyTypeFloat, MJPropertyTypeDouble, MJPropertyTypeLong, MJPropertyTypeLongLong, MJPropertyTypeChar];
63 | if ([numberTypes containsObject:lowerCode]) {
64 | _numberType = YES;
65 |
66 | if ([lowerCode isEqualToString:MJPropertyTypeBOOL1]
67 | || [lowerCode isEqualToString:MJPropertyTypeBOOL2]) {
68 | _boolType = YES;
69 | }
70 | }
71 | }
72 | @end
73 |
--------------------------------------------------------------------------------
/Pods/MJExtension/MJExtension/NSObject+MJClass.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSObject+MJClass.h
3 | // MJExtensionExample
4 | //
5 | // Created by MJ Lee on 15/8/11.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | /**
12 | * 遍历所有类的block(父类)
13 | */
14 | typedef void (^MJClassesEnumeration)(Class c, BOOL *stop);
15 |
16 | /** 这个数组中的属性名才会进行字典和模型的转换 */
17 | typedef NSArray * (^MJAllowedPropertyNames)();
18 | /** 这个数组中的属性名才会进行归档 */
19 | typedef NSArray * (^MJAllowedCodingPropertyNames)();
20 |
21 | /** 这个数组中的属性名将会被忽略:不进行字典和模型的转换 */
22 | typedef NSArray * (^MJIgnoredPropertyNames)();
23 | /** 这个数组中的属性名将会被忽略:不进行归档 */
24 | typedef NSArray * (^MJIgnoredCodingPropertyNames)();
25 |
26 | /**
27 | * 类相关的扩展
28 | */
29 | @interface NSObject (MJClass)
30 | /**
31 | * 遍历所有的类
32 | */
33 | + (void)mj_enumerateClasses:(MJClassesEnumeration)enumeration;
34 | + (void)mj_enumerateAllClasses:(MJClassesEnumeration)enumeration;
35 |
36 | #pragma mark - 属性白名单配置
37 | /**
38 | * 这个数组中的属性名才会进行字典和模型的转换
39 | *
40 | * @param allowedPropertyNames 这个数组中的属性名才会进行字典和模型的转换
41 | */
42 | + (void)mj_setupAllowedPropertyNames:(MJAllowedPropertyNames)allowedPropertyNames;
43 |
44 | /**
45 | * 这个数组中的属性名才会进行字典和模型的转换
46 | */
47 | + (NSMutableArray *)mj_totalAllowedPropertyNames;
48 |
49 | #pragma mark - 属性黑名单配置
50 | /**
51 | * 这个数组中的属性名将会被忽略:不进行字典和模型的转换
52 | *
53 | * @param ignoredPropertyNames 这个数组中的属性名将会被忽略:不进行字典和模型的转换
54 | */
55 | + (void)mj_setupIgnoredPropertyNames:(MJIgnoredPropertyNames)ignoredPropertyNames;
56 |
57 | /**
58 | * 这个数组中的属性名将会被忽略:不进行字典和模型的转换
59 | */
60 | + (NSMutableArray *)mj_totalIgnoredPropertyNames;
61 |
62 | #pragma mark - 归档属性白名单配置
63 | /**
64 | * 这个数组中的属性名才会进行归档
65 | *
66 | * @param allowedCodingPropertyNames 这个数组中的属性名才会进行归档
67 | */
68 | + (void)mj_setupAllowedCodingPropertyNames:(MJAllowedCodingPropertyNames)allowedCodingPropertyNames;
69 |
70 | /**
71 | * 这个数组中的属性名才会进行字典和模型的转换
72 | */
73 | + (NSMutableArray *)mj_totalAllowedCodingPropertyNames;
74 |
75 | #pragma mark - 归档属性黑名单配置
76 | /**
77 | * 这个数组中的属性名将会被忽略:不进行归档
78 | *
79 | * @param ignoredCodingPropertyNames 这个数组中的属性名将会被忽略:不进行归档
80 | */
81 | + (void)mj_setupIgnoredCodingPropertyNames:(MJIgnoredCodingPropertyNames)ignoredCodingPropertyNames;
82 |
83 | /**
84 | * 这个数组中的属性名将会被忽略:不进行归档
85 | */
86 | + (NSMutableArray *)mj_totalIgnoredCodingPropertyNames;
87 |
88 | #pragma mark - 内部使用
89 | + (void)mj_setupBlockReturnValue:(id (^)())block key:(const char *)key;
90 | @end
91 |
--------------------------------------------------------------------------------
/Pods/MJExtension/MJExtension/NSObject+MJClass.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSObject+MJClass.m
3 | // MJExtensionExample
4 | //
5 | // Created by MJ Lee on 15/8/11.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "NSObject+MJClass.h"
10 | #import "NSObject+MJCoding.h"
11 | #import "NSObject+MJKeyValue.h"
12 | #import "MJFoundation.h"
13 | #import
14 |
15 | static const char MJAllowedPropertyNamesKey = '\0';
16 | static const char MJIgnoredPropertyNamesKey = '\0';
17 | static const char MJAllowedCodingPropertyNamesKey = '\0';
18 | static const char MJIgnoredCodingPropertyNamesKey = '\0';
19 |
20 | static NSMutableDictionary *allowedPropertyNamesDict_;
21 | static NSMutableDictionary *ignoredPropertyNamesDict_;
22 | static NSMutableDictionary *allowedCodingPropertyNamesDict_;
23 | static NSMutableDictionary *ignoredCodingPropertyNamesDict_;
24 |
25 | @implementation NSObject (MJClass)
26 |
27 | + (void)load
28 | {
29 | allowedPropertyNamesDict_ = [NSMutableDictionary dictionary];
30 | ignoredPropertyNamesDict_ = [NSMutableDictionary dictionary];
31 | allowedCodingPropertyNamesDict_ = [NSMutableDictionary dictionary];
32 | ignoredCodingPropertyNamesDict_ = [NSMutableDictionary dictionary];
33 | }
34 |
35 | + (NSMutableDictionary *)dictForKey:(const void *)key
36 | {
37 | @synchronized (self) {
38 | if (key == &MJAllowedPropertyNamesKey) return allowedPropertyNamesDict_;
39 | if (key == &MJIgnoredPropertyNamesKey) return ignoredPropertyNamesDict_;
40 | if (key == &MJAllowedCodingPropertyNamesKey) return allowedCodingPropertyNamesDict_;
41 | if (key == &MJIgnoredCodingPropertyNamesKey) return ignoredCodingPropertyNamesDict_;
42 | return nil;
43 | }
44 | }
45 |
46 | + (void)mj_enumerateClasses:(MJClassesEnumeration)enumeration
47 | {
48 | // 1.没有block就直接返回
49 | if (enumeration == nil) return;
50 |
51 | // 2.停止遍历的标记
52 | BOOL stop = NO;
53 |
54 | // 3.当前正在遍历的类
55 | Class c = self;
56 |
57 | // 4.开始遍历每一个类
58 | while (c && !stop) {
59 | // 4.1.执行操作
60 | enumeration(c, &stop);
61 |
62 | // 4.2.获得父类
63 | c = class_getSuperclass(c);
64 |
65 | if ([MJFoundation isClassFromFoundation:c]) break;
66 | }
67 | }
68 |
69 | + (void)mj_enumerateAllClasses:(MJClassesEnumeration)enumeration
70 | {
71 | // 1.没有block就直接返回
72 | if (enumeration == nil) return;
73 |
74 | // 2.停止遍历的标记
75 | BOOL stop = NO;
76 |
77 | // 3.当前正在遍历的类
78 | Class c = self;
79 |
80 | // 4.开始遍历每一个类
81 | while (c && !stop) {
82 | // 4.1.执行操作
83 | enumeration(c, &stop);
84 |
85 | // 4.2.获得父类
86 | c = class_getSuperclass(c);
87 | }
88 | }
89 |
90 | #pragma mark - 属性黑名单配置
91 | + (void)mj_setupIgnoredPropertyNames:(MJIgnoredPropertyNames)ignoredPropertyNames
92 | {
93 | [self mj_setupBlockReturnValue:ignoredPropertyNames key:&MJIgnoredPropertyNamesKey];
94 | }
95 |
96 | + (NSMutableArray *)mj_totalIgnoredPropertyNames
97 | {
98 | return [self mj_totalObjectsWithSelector:@selector(mj_ignoredPropertyNames) key:&MJIgnoredPropertyNamesKey];
99 | }
100 |
101 | #pragma mark - 归档属性黑名单配置
102 | + (void)mj_setupIgnoredCodingPropertyNames:(MJIgnoredCodingPropertyNames)ignoredCodingPropertyNames
103 | {
104 | [self mj_setupBlockReturnValue:ignoredCodingPropertyNames key:&MJIgnoredCodingPropertyNamesKey];
105 | }
106 |
107 | + (NSMutableArray *)mj_totalIgnoredCodingPropertyNames
108 | {
109 | return [self mj_totalObjectsWithSelector:@selector(mj_ignoredCodingPropertyNames) key:&MJIgnoredCodingPropertyNamesKey];
110 | }
111 |
112 | #pragma mark - 属性白名单配置
113 | + (void)mj_setupAllowedPropertyNames:(MJAllowedPropertyNames)allowedPropertyNames;
114 | {
115 | [self mj_setupBlockReturnValue:allowedPropertyNames key:&MJAllowedPropertyNamesKey];
116 | }
117 |
118 | + (NSMutableArray *)mj_totalAllowedPropertyNames
119 | {
120 | return [self mj_totalObjectsWithSelector:@selector(mj_allowedPropertyNames) key:&MJAllowedPropertyNamesKey];
121 | }
122 |
123 | #pragma mark - 归档属性白名单配置
124 | + (void)mj_setupAllowedCodingPropertyNames:(MJAllowedCodingPropertyNames)allowedCodingPropertyNames
125 | {
126 | [self mj_setupBlockReturnValue:allowedCodingPropertyNames key:&MJAllowedCodingPropertyNamesKey];
127 | }
128 |
129 | + (NSMutableArray *)mj_totalAllowedCodingPropertyNames
130 | {
131 | return [self mj_totalObjectsWithSelector:@selector(mj_allowedCodingPropertyNames) key:&MJAllowedCodingPropertyNamesKey];
132 | }
133 | #pragma mark - block和方法处理:存储block的返回值
134 | + (void)mj_setupBlockReturnValue:(id (^)())block key:(const char *)key
135 | {
136 | if (block) {
137 | objc_setAssociatedObject(self, key, block(), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
138 | } else {
139 | objc_setAssociatedObject(self, key, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
140 | }
141 |
142 | // 清空数据
143 | [[self dictForKey:key] removeAllObjects];
144 | }
145 |
146 | + (NSMutableArray *)mj_totalObjectsWithSelector:(SEL)selector key:(const char *)key
147 | {
148 | NSMutableArray *array = [self dictForKey:key][NSStringFromClass(self)];
149 | if (array) return array;
150 |
151 | // 创建、存储
152 | [self dictForKey:key][NSStringFromClass(self)] = array = [NSMutableArray array];
153 |
154 | if ([self respondsToSelector:selector]) {
155 | #pragma clang diagnostic push
156 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
157 | NSArray *subArray = [self performSelector:selector];
158 | #pragma clang diagnostic pop
159 | if (subArray) {
160 | [array addObjectsFromArray:subArray];
161 | }
162 | }
163 |
164 | [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) {
165 | NSArray *subArray = objc_getAssociatedObject(c, key);
166 | [array addObjectsFromArray:subArray];
167 | }];
168 | return array;
169 | }
170 | @end
171 |
--------------------------------------------------------------------------------
/Pods/MJExtension/MJExtension/NSObject+MJCoding.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSObject+MJCoding.h
3 | // MJExtension
4 | //
5 | // Created by mj on 14-1-15.
6 | // Copyright (c) 2014年 小码哥. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "MJExtensionConst.h"
11 |
12 | /**
13 | * Codeing协议
14 | */
15 | @protocol MJCoding
16 | @optional
17 | /**
18 | * 这个数组中的属性名才会进行归档
19 | */
20 | + (NSArray *)mj_allowedCodingPropertyNames;
21 | /**
22 | * 这个数组中的属性名将会被忽略:不进行归档
23 | */
24 | + (NSArray *)mj_ignoredCodingPropertyNames;
25 | @end
26 |
27 | @interface NSObject (MJCoding)
28 | /**
29 | * 解码(从文件中解析对象)
30 | */
31 | - (void)mj_decode:(NSCoder *)decoder;
32 | /**
33 | * 编码(将对象写入文件中)
34 | */
35 | - (void)mj_encode:(NSCoder *)encoder;
36 | @end
37 |
38 | /**
39 | 归档的实现
40 | */
41 | #define MJCodingImplementation \
42 | - (id)initWithCoder:(NSCoder *)decoder \
43 | { \
44 | if (self = [super init]) { \
45 | [self mj_decode:decoder]; \
46 | } \
47 | return self; \
48 | } \
49 | \
50 | - (void)encodeWithCoder:(NSCoder *)encoder \
51 | { \
52 | [self mj_encode:encoder]; \
53 | }
54 |
55 | #define MJExtensionCodingImplementation MJCodingImplementation
--------------------------------------------------------------------------------
/Pods/MJExtension/MJExtension/NSObject+MJCoding.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSObject+MJCoding.m
3 | // MJExtension
4 | //
5 | // Created by mj on 14-1-15.
6 | // Copyright (c) 2014年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "NSObject+MJCoding.h"
10 | #import "NSObject+MJClass.h"
11 | #import "NSObject+MJProperty.h"
12 | #import "MJProperty.h"
13 |
14 | @implementation NSObject (MJCoding)
15 |
16 | - (void)mj_encode:(NSCoder *)encoder
17 | {
18 | Class clazz = [self class];
19 |
20 | NSArray *allowedCodingPropertyNames = [clazz mj_totalAllowedCodingPropertyNames];
21 | NSArray *ignoredCodingPropertyNames = [clazz mj_totalIgnoredCodingPropertyNames];
22 |
23 | [clazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) {
24 | // 检测是否被忽略
25 | if (allowedCodingPropertyNames.count && ![allowedCodingPropertyNames containsObject:property.name]) return;
26 | if ([ignoredCodingPropertyNames containsObject:property.name]) return;
27 |
28 | id value = [property valueForObject:self];
29 | if (value == nil) return;
30 | [encoder encodeObject:value forKey:property.name];
31 | }];
32 | }
33 |
34 | - (void)mj_decode:(NSCoder *)decoder
35 | {
36 | Class clazz = [self class];
37 |
38 | NSArray *allowedCodingPropertyNames = [clazz mj_totalAllowedCodingPropertyNames];
39 | NSArray *ignoredCodingPropertyNames = [clazz mj_totalIgnoredCodingPropertyNames];
40 |
41 | [clazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) {
42 | // 检测是否被忽略
43 | if (allowedCodingPropertyNames.count && ![allowedCodingPropertyNames containsObject:property.name]) return;
44 | if ([ignoredCodingPropertyNames containsObject:property.name]) return;
45 |
46 | id value = [decoder decodeObjectForKey:property.name];
47 | if (value == nil) { // 兼容以前的MJExtension版本
48 | value = [decoder decodeObjectForKey:[@"_" stringByAppendingString:property.name]];
49 | }
50 | if (value == nil) return;
51 | [property setValue:value forObject:self];
52 | }];
53 | }
54 | @end
55 |
--------------------------------------------------------------------------------
/Pods/MJExtension/MJExtension/NSObject+MJKeyValue.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSObject+MJKeyValue.h
3 | // MJExtension
4 | //
5 | // Created by mj on 13-8-24.
6 | // Copyright (c) 2013年 小码哥. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "MJExtensionConst.h"
11 | #import
12 | #import "MJProperty.h"
13 |
14 | /**
15 | * KeyValue协议
16 | */
17 | @protocol MJKeyValue
18 | @optional
19 | /**
20 | * 只有这个数组中的属性名才允许进行字典和模型的转换
21 | */
22 | + (NSArray *)mj_allowedPropertyNames;
23 |
24 | /**
25 | * 这个数组中的属性名将会被忽略:不进行字典和模型的转换
26 | */
27 | + (NSArray *)mj_ignoredPropertyNames;
28 |
29 | /**
30 | * 将属性名换为其他key去字典中取值
31 | *
32 | * @return 字典中的key是属性名,value是从字典中取值用的key
33 | */
34 | + (NSDictionary *)mj_replacedKeyFromPropertyName;
35 |
36 | /**
37 | * 将属性名换为其他key去字典中取值
38 | *
39 | * @return 从字典中取值用的key
40 | */
41 | + (id)mj_replacedKeyFromPropertyName121:(NSString *)propertyName;
42 |
43 | /**
44 | * 数组中需要转换的模型类
45 | *
46 | * @return 字典中的key是数组属性名,value是数组中存放模型的Class(Class类型或者NSString类型)
47 | */
48 | + (NSDictionary *)mj_objectClassInArray;
49 |
50 | /**
51 | * 旧值换新值,用于过滤字典中的值
52 | *
53 | * @param oldValue 旧值
54 | *
55 | * @return 新值
56 | */
57 | - (id)mj_newValueFromOldValue:(id)oldValue property:(MJProperty *)property;
58 |
59 | /**
60 | * 当字典转模型完毕时调用
61 | */
62 | - (void)mj_keyValuesDidFinishConvertingToObject;
63 |
64 | /**
65 | * 当模型转字典完毕时调用
66 | */
67 | - (void)mj_objectDidFinishConvertingToKeyValues;
68 | @end
69 |
70 | @interface NSObject (MJKeyValue)
71 | #pragma mark - 类方法
72 | /**
73 | * 字典转模型过程中遇到的错误
74 | */
75 | + (NSError *)mj_error;
76 |
77 | /**
78 | * 模型转字典时,字典的key是否参考replacedKeyFromPropertyName等方法(父类设置了,子类也会继承下来)
79 | */
80 | + (void)mj_referenceReplacedKeyWhenCreatingKeyValues:(BOOL)reference;
81 |
82 | #pragma mark - 对象方法
83 | /**
84 | * 将字典的键值对转成模型属性
85 | * @param keyValues 字典(可以是NSDictionary、NSData、NSString)
86 | */
87 | - (instancetype)mj_setKeyValues:(id)keyValues;
88 |
89 | /**
90 | * 将字典的键值对转成模型属性
91 | * @param keyValues 字典(可以是NSDictionary、NSData、NSString)
92 | * @param context CoreData上下文
93 | */
94 | - (instancetype)mj_setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context;
95 |
96 | /**
97 | * 将模型转成字典
98 | * @return 字典
99 | */
100 | - (NSMutableDictionary *)mj_keyValues;
101 | - (NSMutableDictionary *)mj_keyValuesWithKeys:(NSArray *)keys;
102 | - (NSMutableDictionary *)mj_keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys;
103 |
104 | /**
105 | * 通过模型数组来创建一个字典数组
106 | * @param objectArray 模型数组
107 | * @return 字典数组
108 | */
109 | + (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray;
110 | + (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys;
111 | + (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys;
112 |
113 | #pragma mark - 字典转模型
114 | /**
115 | * 通过字典来创建一个模型
116 | * @param keyValues 字典(可以是NSDictionary、NSData、NSString)
117 | * @return 新建的对象
118 | */
119 | + (instancetype)mj_objectWithKeyValues:(id)keyValues;
120 |
121 | /**
122 | * 通过字典来创建一个CoreData模型
123 | * @param keyValues 字典(可以是NSDictionary、NSData、NSString)
124 | * @param context CoreData上下文
125 | * @return 新建的对象
126 | */
127 | + (instancetype)mj_objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context;
128 |
129 | /**
130 | * 通过plist来创建一个模型
131 | * @param filename 文件名(仅限于mainBundle中的文件)
132 | * @return 新建的对象
133 | */
134 | + (instancetype)mj_objectWithFilename:(NSString *)filename;
135 |
136 | /**
137 | * 通过plist来创建一个模型
138 | * @param file 文件全路径
139 | * @return 新建的对象
140 | */
141 | + (instancetype)mj_objectWithFile:(NSString *)file;
142 |
143 | #pragma mark - 字典数组转模型数组
144 | /**
145 | * 通过字典数组来创建一个模型数组
146 | * @param keyValuesArray 字典数组(可以是NSDictionary、NSData、NSString)
147 | * @return 模型数组
148 | */
149 | + (NSMutableArray *)mj_objectArrayWithKeyValuesArray:(id)keyValuesArray;
150 |
151 | /**
152 | * 通过字典数组来创建一个模型数组
153 | * @param keyValuesArray 字典数组(可以是NSDictionary、NSData、NSString)
154 | * @param context CoreData上下文
155 | * @return 模型数组
156 | */
157 | + (NSMutableArray *)mj_objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context;
158 |
159 | /**
160 | * 通过plist来创建一个模型数组
161 | * @param filename 文件名(仅限于mainBundle中的文件)
162 | * @return 模型数组
163 | */
164 | + (NSMutableArray *)mj_objectArrayWithFilename:(NSString *)filename;
165 |
166 | /**
167 | * 通过plist来创建一个模型数组
168 | * @param file 文件全路径
169 | * @return 模型数组
170 | */
171 | + (NSMutableArray *)mj_objectArrayWithFile:(NSString *)file;
172 |
173 | #pragma mark - 转换为JSON
174 | /**
175 | * 转换为JSON Data
176 | */
177 | - (NSData *)mj_JSONData;
178 | /**
179 | * 转换为字典或者数组
180 | */
181 | - (id)mj_JSONObject;
182 | /**
183 | * 转换为JSON 字符串
184 | */
185 | - (NSString *)mj_JSONString;
186 | @end
187 |
188 | @interface NSObject (MJKeyValueDeprecated_v_2_5_16)
189 | - (instancetype)setKeyValues:(id)keyValue MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
190 | - (instancetype)setKeyValues:(id)keyValues error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
191 | - (instancetype)setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
192 | - (instancetype)setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
193 | + (void)referenceReplacedKeyWhenCreatingKeyValues:(BOOL)reference MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
194 | - (NSMutableDictionary *)keyValues MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
195 | - (NSMutableDictionary *)keyValuesWithError:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
196 | - (NSMutableDictionary *)keyValuesWithKeys:(NSArray *)keys MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
197 | - (NSMutableDictionary *)keyValuesWithKeys:(NSArray *)keys error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
198 | - (NSMutableDictionary *)keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
199 | - (NSMutableDictionary *)keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
200 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
201 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
202 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
203 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
204 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
205 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
206 | + (instancetype)objectWithKeyValues:(id)keyValues MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
207 | + (instancetype)objectWithKeyValues:(id)keyValues error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
208 | + (instancetype)objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
209 | + (instancetype)objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
210 | + (instancetype)objectWithFilename:(NSString *)filename MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
211 | + (instancetype)objectWithFilename:(NSString *)filename error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
212 | + (instancetype)objectWithFile:(NSString *)file MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
213 | + (instancetype)objectWithFile:(NSString *)file error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
214 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
215 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
216 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
217 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
218 | + (NSMutableArray *)objectArrayWithFilename:(NSString *)filename MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
219 | + (NSMutableArray *)objectArrayWithFilename:(NSString *)filename error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
220 | + (NSMutableArray *)objectArrayWithFile:(NSString *)file MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
221 | + (NSMutableArray *)objectArrayWithFile:(NSString *)file error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
222 | - (NSData *)JSONData MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
223 | - (id)JSONObject MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
224 | - (NSString *)JSONString MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
225 | @end
226 |
--------------------------------------------------------------------------------
/Pods/MJExtension/MJExtension/NSObject+MJProperty.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSObject+MJProperty.h
3 | // MJExtensionExample
4 | //
5 | // Created by MJ Lee on 15/4/17.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "MJExtensionConst.h"
11 |
12 | @class MJProperty;
13 |
14 | /**
15 | * 遍历成员变量用的block
16 | *
17 | * @param property 成员的包装对象
18 | * @param stop YES代表停止遍历,NO代表继续遍历
19 | */
20 | typedef void (^MJPropertiesEnumeration)(MJProperty *property, BOOL *stop);
21 |
22 | /** 将属性名换为其他key去字典中取值 */
23 | typedef NSDictionary * (^MJReplacedKeyFromPropertyName)();
24 | typedef id (^MJReplacedKeyFromPropertyName121)(NSString *propertyName);
25 | /** 数组中需要转换的模型类 */
26 | typedef NSDictionary * (^MJObjectClassInArray)();
27 | /** 用于过滤字典中的值 */
28 | typedef id (^MJNewValueFromOldValue)(id object, id oldValue, MJProperty *property);
29 |
30 | /**
31 | * 成员属性相关的扩展
32 | */
33 | @interface NSObject (MJProperty)
34 | #pragma mark - 遍历
35 | /**
36 | * 遍历所有的成员
37 | */
38 | + (void)mj_enumerateProperties:(MJPropertiesEnumeration)enumeration;
39 |
40 | #pragma mark - 新值配置
41 | /**
42 | * 用于过滤字典中的值
43 | *
44 | * @param newValueFormOldValue 用于过滤字典中的值
45 | */
46 | + (void)mj_setupNewValueFromOldValue:(MJNewValueFromOldValue)newValueFormOldValue;
47 | + (id)mj_getNewValueFromObject:(__unsafe_unretained id)object oldValue:(__unsafe_unretained id)oldValue property:(__unsafe_unretained MJProperty *)property;
48 |
49 | #pragma mark - key配置
50 | /**
51 | * 将属性名换为其他key去字典中取值
52 | *
53 | * @param replacedKeyFromPropertyName 将属性名换为其他key去字典中取值
54 | */
55 | + (void)mj_setupReplacedKeyFromPropertyName:(MJReplacedKeyFromPropertyName)replacedKeyFromPropertyName;
56 | /**
57 | * 将属性名换为其他key去字典中取值
58 | *
59 | * @param replacedKeyFromPropertyName121 将属性名换为其他key去字典中取值
60 | */
61 | + (void)mj_setupReplacedKeyFromPropertyName121:(MJReplacedKeyFromPropertyName121)replacedKeyFromPropertyName121;
62 |
63 | #pragma mark - array model class配置
64 | /**
65 | * 数组中需要转换的模型类
66 | *
67 | * @param objectClassInArray 数组中需要转换的模型类
68 | */
69 | + (void)mj_setupObjectClassInArray:(MJObjectClassInArray)objectClassInArray;
70 | @end
71 |
72 | @interface NSObject (MJPropertyDeprecated_v_2_5_16)
73 | + (void)enumerateProperties:(MJPropertiesEnumeration)enumeration MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
74 | + (void)setupNewValueFromOldValue:(MJNewValueFromOldValue)newValueFormOldValue MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
75 | + (id)getNewValueFromObject:(__unsafe_unretained id)object oldValue:(__unsafe_unretained id)oldValue property:(__unsafe_unretained MJProperty *)property MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
76 | + (void)setupReplacedKeyFromPropertyName:(MJReplacedKeyFromPropertyName)replacedKeyFromPropertyName MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
77 | + (void)setupReplacedKeyFromPropertyName121:(MJReplacedKeyFromPropertyName121)replacedKeyFromPropertyName121 MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
78 | + (void)setupObjectClassInArray:(MJObjectClassInArray)objectClassInArray MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
79 | @end
--------------------------------------------------------------------------------
/Pods/MJExtension/MJExtension/NSObject+MJProperty.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSObject+MJProperty.m
3 | // MJExtensionExample
4 | //
5 | // Created by MJ Lee on 15/4/17.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "NSObject+MJProperty.h"
10 | #import "NSObject+MJKeyValue.h"
11 | #import "NSObject+MJCoding.h"
12 | #import "NSObject+MJClass.h"
13 | #import "MJProperty.h"
14 | #import "MJFoundation.h"
15 | #import
16 |
17 | #pragma clang diagnostic push
18 | #pragma clang diagnostic ignored "-Wundeclared-selector"
19 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
20 |
21 | static const char MJReplacedKeyFromPropertyNameKey = '\0';
22 | static const char MJReplacedKeyFromPropertyName121Key = '\0';
23 | static const char MJNewValueFromOldValueKey = '\0';
24 | static const char MJObjectClassInArrayKey = '\0';
25 |
26 | static const char MJCachedPropertiesKey = '\0';
27 |
28 | @implementation NSObject (Property)
29 |
30 | static NSMutableDictionary *replacedKeyFromPropertyNameDict_;
31 | static NSMutableDictionary *replacedKeyFromPropertyName121Dict_;
32 | static NSMutableDictionary *newValueFromOldValueDict_;
33 | static NSMutableDictionary *objectClassInArrayDict_;
34 | static NSMutableDictionary *cachedPropertiesDict_;
35 |
36 | + (void)load
37 | {
38 | replacedKeyFromPropertyNameDict_ = [NSMutableDictionary dictionary];
39 | replacedKeyFromPropertyName121Dict_ = [NSMutableDictionary dictionary];
40 | newValueFromOldValueDict_ = [NSMutableDictionary dictionary];
41 | objectClassInArrayDict_ = [NSMutableDictionary dictionary];
42 | cachedPropertiesDict_ = [NSMutableDictionary dictionary];
43 | }
44 |
45 | + (NSMutableDictionary *)dictForKey:(const void *)key
46 | {
47 | @synchronized (self) {
48 | if (key == &MJReplacedKeyFromPropertyNameKey) return replacedKeyFromPropertyNameDict_;
49 | if (key == &MJReplacedKeyFromPropertyName121Key) return replacedKeyFromPropertyName121Dict_;
50 | if (key == &MJNewValueFromOldValueKey) return newValueFromOldValueDict_;
51 | if (key == &MJObjectClassInArrayKey) return objectClassInArrayDict_;
52 | if (key == &MJCachedPropertiesKey) return cachedPropertiesDict_;
53 | return nil;
54 | }
55 | }
56 |
57 | #pragma mark - --私有方法--
58 | + (id)propertyKey:(NSString *)propertyName
59 | {
60 | MJExtensionAssertParamNotNil2(propertyName, nil);
61 |
62 | __block id key = nil;
63 | // 查看有没有需要替换的key
64 | if ([self respondsToSelector:@selector(mj_replacedKeyFromPropertyName121:)]) {
65 | key = [self mj_replacedKeyFromPropertyName121:propertyName];
66 | }
67 | // 兼容旧版本
68 | if ([self respondsToSelector:@selector(replacedKeyFromPropertyName121:)]) {
69 | key = [self performSelector:@selector(replacedKeyFromPropertyName121) withObject:propertyName];
70 | }
71 |
72 | // 调用block
73 | if (!key) {
74 | [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) {
75 | MJReplacedKeyFromPropertyName121 block = objc_getAssociatedObject(c, &MJReplacedKeyFromPropertyName121Key);
76 | if (block) {
77 | key = block(propertyName);
78 | }
79 | if (key) *stop = YES;
80 | }];
81 | }
82 |
83 | // 查看有没有需要替换的key
84 | if ((!key || [key isEqual:propertyName]) && [self respondsToSelector:@selector(mj_replacedKeyFromPropertyName)]) {
85 | key = [self mj_replacedKeyFromPropertyName][propertyName];
86 | }
87 | // 兼容旧版本
88 | if ((!key || [key isEqual:propertyName]) && [self respondsToSelector:@selector(replacedKeyFromPropertyName)]) {
89 | key = [self performSelector:@selector(replacedKeyFromPropertyName)][propertyName];
90 | }
91 |
92 | if (!key || [key isEqual:propertyName]) {
93 | [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) {
94 | NSDictionary *dict = objc_getAssociatedObject(c, &MJReplacedKeyFromPropertyNameKey);
95 | if (dict) {
96 | key = dict[propertyName];
97 | }
98 | if (key && ![key isEqual:propertyName]) *stop = YES;
99 | }];
100 | }
101 |
102 | // 2.用属性名作为key
103 | if (!key) key = propertyName;
104 |
105 | return key;
106 | }
107 |
108 | + (Class)propertyObjectClassInArray:(NSString *)propertyName
109 | {
110 | __block id clazz = nil;
111 | if ([self respondsToSelector:@selector(mj_objectClassInArray)]) {
112 | clazz = [self mj_objectClassInArray][propertyName];
113 | }
114 | // 兼容旧版本
115 | if ([self respondsToSelector:@selector(objectClassInArray)]) {
116 | clazz = [self performSelector:@selector(objectClassInArray)][propertyName];
117 | }
118 |
119 | if (!clazz) {
120 | [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) {
121 | NSDictionary *dict = objc_getAssociatedObject(c, &MJObjectClassInArrayKey);
122 | if (dict) {
123 | clazz = dict[propertyName];
124 | }
125 | if (clazz) *stop = YES;
126 | }];
127 | }
128 |
129 | // 如果是NSString类型
130 | if ([clazz isKindOfClass:[NSString class]]) {
131 | clazz = NSClassFromString(clazz);
132 | }
133 | return clazz;
134 | }
135 |
136 | #pragma mark - --公共方法--
137 | + (void)mj_enumerateProperties:(MJPropertiesEnumeration)enumeration
138 | {
139 | // 获得成员变量
140 | NSArray *cachedProperties = [self properties];
141 |
142 | // 遍历成员变量
143 | BOOL stop = NO;
144 | for (MJProperty *property in cachedProperties) {
145 | enumeration(property, &stop);
146 | if (stop) break;
147 | }
148 | }
149 |
150 | #pragma mark - 公共方法
151 | + (NSMutableArray *)properties
152 | {
153 | NSMutableArray *cachedProperties = [self dictForKey:&MJCachedPropertiesKey][NSStringFromClass(self)];
154 |
155 | if (cachedProperties == nil) {
156 | cachedProperties = [NSMutableArray array];
157 |
158 | [self mj_enumerateClasses:^(__unsafe_unretained Class c, BOOL *stop) {
159 | // 1.获得所有的成员变量
160 | unsigned int outCount = 0;
161 | objc_property_t *properties = class_copyPropertyList(c, &outCount);
162 |
163 | // 2.遍历每一个成员变量
164 | for (unsigned int i = 0; i
10 | #import "MJExtensionConst.h"
11 |
12 | @interface NSString (MJExtension)
13 | /**
14 | * 驼峰转下划线(loveYou -> love_you)
15 | */
16 | - (NSString *)mj_underlineFromCamel;
17 | /**
18 | * 下划线转驼峰(love_you -> loveYou)
19 | */
20 | - (NSString *)mj_camelFromUnderline;
21 | /**
22 | * 首字母变大写
23 | */
24 | - (NSString *)mj_firstCharUpper;
25 | /**
26 | * 首字母变小写
27 | */
28 | - (NSString *)mj_firstCharLower;
29 |
30 | - (BOOL)mj_isPureInt;
31 |
32 | - (NSURL *)mj_url;
33 | @end
34 |
35 | @interface NSString (MJExtensionDeprecated_v_2_5_16)
36 | - (NSString *)underlineFromCamel MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
37 | - (NSString *)camelFromUnderline MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
38 | - (NSString *)firstCharUpper MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
39 | - (NSString *)firstCharLower MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
40 | - (BOOL)isPureInt MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
41 | - (NSURL *)url MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***");
42 | @end
43 |
--------------------------------------------------------------------------------
/Pods/MJExtension/MJExtension/NSString+MJExtension.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSString+MJExtension.m
3 | // MJExtensionExample
4 | //
5 | // Created by MJ Lee on 15/6/7.
6 | // Copyright (c) 2015年 小码哥. All rights reserved.
7 | //
8 |
9 | #import "NSString+MJExtension.h"
10 |
11 | @implementation NSString (MJExtension)
12 | - (NSString *)mj_underlineFromCamel
13 | {
14 | if (self.length == 0) return self;
15 | NSMutableString *string = [NSMutableString string];
16 | for (NSUInteger i = 0; i= 2) [string appendString:[cmp substringFromIndex:1]];
40 | } else {
41 | [string appendString:cmp];
42 | }
43 | }
44 | return string;
45 | }
46 |
47 | - (NSString *)mj_firstCharLower
48 | {
49 | if (self.length == 0) return self;
50 | NSMutableString *string = [NSMutableString string];
51 | [string appendString:[NSString stringWithFormat:@"%c", [self characterAtIndex:0]].lowercaseString];
52 | if (self.length >= 2) [string appendString:[self substringFromIndex:1]];
53 | return string;
54 | }
55 |
56 | - (NSString *)mj_firstCharUpper
57 | {
58 | if (self.length == 0) return self;
59 | NSMutableString *string = [NSMutableString string];
60 | [string appendString:[NSString stringWithFormat:@"%c", [self characterAtIndex:0]].uppercaseString];
61 | if (self.length >= 2) [string appendString:[self substringFromIndex:1]];
62 | return string;
63 | }
64 |
65 | - (BOOL)mj_isPureInt
66 | {
67 | NSScanner *scan = [NSScanner scannerWithString:self];
68 | int val;
69 | return [scan scanInt:&val] && [scan isAtEnd];
70 | }
71 |
72 | - (NSURL *)mj_url
73 | {
74 | // [self stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet characterSetWithCharactersInString:@"!$&'()*+,-./:;=?@_~%#[]"]];
75 | #pragma clang diagnostic push
76 | #pragma clang diagnostic ignored"-Wdeprecated-declarations"
77 | return [NSURL URLWithString:(NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)self, (CFStringRef)@"!$&'()*+,-./:;=?@_~%#[]", NULL,kCFStringEncodingUTF8))];
78 | #pragma clang diagnostic pop
79 | }
80 | @end
81 |
82 | @implementation NSString (MJExtensionDeprecated_v_2_5_16)
83 | - (NSString *)underlineFromCamel
84 | {
85 | return self.mj_underlineFromCamel;
86 | }
87 |
88 | - (NSString *)camelFromUnderline
89 | {
90 | return self.mj_camelFromUnderline;
91 | }
92 |
93 | - (NSString *)firstCharLower
94 | {
95 | return self.mj_firstCharLower;
96 | }
97 |
98 | - (NSString *)firstCharUpper
99 | {
100 | return self.mj_firstCharUpper;
101 | }
102 |
103 | - (BOOL)isPureInt
104 | {
105 | return self.mj_isPureInt;
106 | }
107 |
108 | - (NSURL *)url
109 | {
110 | return self.mj_url;
111 | }
112 | @end
113 |
--------------------------------------------------------------------------------
/Pods/MJExtension/README.md:
--------------------------------------------------------------------------------
1 |
2 | 
3 | MJExtension
4 | ===
5 | - A fast, convenient and nonintrusive conversion between JSON and model.
6 | - 转换速度快、使用简单方便的字典转模型框架
7 |
8 | GitHub:[CoderMJLee](https://github.com/CoderMJLee) | Blog:[mjios(Chinese)](http://www.cnblogs.com/mjios) | PR is welcome,or [feedback](mailto:richermj123go@vip.qq.com)
9 |
10 |
11 | ## Contents
12 | * [Getting Started 【开始使用】](#Getting_Started)
13 | * [Features 【能做什么】](#Features)
14 | * [Installation 【安装】](#Installation)
15 | * [Examples 【示例】](#Examples)
16 | * [JSON -> Model](#JSON_Model)
17 | * [JSONString -> Model](#JSONString_Model)
18 | * [Model contains model](#Model_contains_model)
19 | * [Model contains model-array](#Model_contains_model_array)
20 | * [Model name - JSON key mapping](#Model_name_JSON_key_mapping)
21 | * [JSON array -> model array](#JSON_array_model_array)
22 | * [Model -> JSON](#Model_JSON)
23 | * [Model array -> JSON array](#Model_array_JSON_array)
24 | * [Core Data](#Core_Data)
25 | * [Coding](#Coding)
26 | * [Camel -> underline](#Camel_underline)
27 | * [NSString -> NSDate, nil -> @""](#NSString_NSDate)
28 | * [More use cases](#More_use_cases)
29 |
30 | ---
31 |
32 | # Getting Started【开始使用】
33 |
34 | ## Features【能做什么】
35 | - MJExtension是一套字典和模型之间互相转换的超轻量级框架
36 | * `JSON` --> `Model`、`Core Data Model`
37 | * `JSONString` --> `Model`、`Core Data Model`
38 | * `Model`、`Core Data Model` --> `JSON`
39 | * `JSON Array` --> `Model Array`、`Core Data Model Array`
40 | * `JSONString` --> `Model Array`、`Core Data Model Array`
41 | * `Model Array`、`Core Data Model Array` --> `JSON Array`
42 | * Coding all properties of model in one line code.
43 | * 只需要一行代码,就能实现模型的所有属性进行Coding(归档和解档)
44 |
45 | ## Installation【安装】
46 |
47 | ### From CocoaPods【使用CocoaPods】
48 |
49 | ```ruby
50 | pod 'MJExtension'
51 | ```
52 |
53 | ### Manually【手动导入】
54 | - Drag all source files under floder `MJExtension` to your project.【将`MJExtension`文件夹中的所有源代码拽入项目中】
55 | - Import the main header file:`#import "MJExtension.h"`【导入主头文件:`#import "MJExtension.h"`】
56 |
57 | ```objc
58 | MJExtension.h
59 | MJConst.h MJConst.m
60 | MJFoundation.h MJFoundation.m
61 | MJProperty.h MJProperty.m
62 | MJType.h MJType.m
63 | NSObject+MJCoding.h NSObject+MJCoding.m
64 | NSObject+MJProperty.h NSObject+MJProperty.m
65 | NSObject+MJKeyValue.h NSObject+MJKeyValue.m
66 | ```
67 |
68 | # Examples【示例】
69 |
70 | ### The most simple JSON -> Model【最简单的字典转模型】
71 |
72 | ```objc
73 | typedef enum {
74 | SexMale,
75 | SexFemale
76 | } Sex;
77 |
78 | @interface User : NSObject
79 | @property (copy, nonatomic) NSString *name;
80 | @property (copy, nonatomic) NSString *icon;
81 | @property (assign, nonatomic) unsigned int age;
82 | @property (copy, nonatomic) NSString *height;
83 | @property (strong, nonatomic) NSNumber *money;
84 | @property (assign, nonatomic) Sex sex;
85 | @property (assign, nonatomic, getter=isGay) BOOL gay;
86 | @end
87 |
88 | /***********************************************/
89 |
90 | NSDictionary *dict = @{
91 | @"name" : @"Jack",
92 | @"icon" : @"lufy.png",
93 | @"age" : @20,
94 | @"height" : @"1.55",
95 | @"money" : @100.9,
96 | @"sex" : @(SexFemale),
97 | @"gay" : @"true"
98 | // @"gay" : @"1"
99 | // @"gay" : @"NO"
100 | };
101 |
102 | // JSON -> User
103 | User *user = [User mj_objectWithKeyValues:dict];
104 |
105 | NSLog(@"name=%@, icon=%@, age=%zd, height=%@, money=%@, sex=%d, gay=%d", user.name, user.icon, user.age, user.height, user.money, user.sex, user.gay);
106 | // name=Jack, icon=lufy.png, age=20, height=1.550000, money=100.9, sex=1
107 | ```
108 |
109 | ### JSONString -> Model【JSON字符串转模型】
110 |
111 | ```objc
112 | // 1.Define a JSONString
113 | NSString *jsonString = @"{\"name\":\"Jack\", \"icon\":\"lufy.png\", \"age\":20}";
114 |
115 | // 2.JSONString -> User
116 | User *user = [User mj_objectWithKeyValues:jsonString];
117 |
118 | // 3.Print user's properties
119 | NSLog(@"name=%@, icon=%@, age=%d", user.name, user.icon, user.age);
120 | // name=Jack, icon=lufy.png, age=20
121 | ```
122 |
123 | ### Model contains model【模型中嵌套模型】
124 |
125 | ```objc
126 | @interface Status : NSObject
127 | @property (copy, nonatomic) NSString *text;
128 | @property (strong, nonatomic) User *user;
129 | @property (strong, nonatomic) Status *retweetedStatus;
130 | @end
131 |
132 | /***********************************************/
133 |
134 | NSDictionary *dict = @{
135 | @"text" : @"Agree!Nice weather!",
136 | @"user" : @{
137 | @"name" : @"Jack",
138 | @"icon" : @"lufy.png"
139 | },
140 | @"retweetedStatus" : @{
141 | @"text" : @"Nice weather!",
142 | @"user" : @{
143 | @"name" : @"Rose",
144 | @"icon" : @"nami.png"
145 | }
146 | }
147 | };
148 |
149 | // JSON -> Status
150 | Status *status = [Status mj_objectWithKeyValues:dict];
151 |
152 | NSString *text = status.text;
153 | NSString *name = status.user.name;
154 | NSString *icon = status.user.icon;
155 | NSLog(@"text=%@, name=%@, icon=%@", text, name, icon);
156 | // text=Agree!Nice weather!, name=Jack, icon=lufy.png
157 |
158 | NSString *text2 = status.retweetedStatus.text;
159 | NSString *name2 = status.retweetedStatus.user.name;
160 | NSString *icon2 = status.retweetedStatus.user.icon;
161 | NSLog(@"text2=%@, name2=%@, icon2=%@", text2, name2, icon2);
162 | // text2=Nice weather!, name2=Rose, icon2=nami.png
163 | ```
164 |
165 | ### Model contains model-array【模型中有个数组属性,数组里面又要装着其他模型】
166 |
167 | ```objc
168 | @interface Ad : NSObject
169 | @property (copy, nonatomic) NSString *image;
170 | @property (copy, nonatomic) NSString *url;
171 | @end
172 |
173 | @interface StatusResult : NSObject
174 | /** Contatins status model */
175 | @property (strong, nonatomic) NSMutableArray *statuses;
176 | /** Contatins ad model */
177 | @property (strong, nonatomic) NSArray *ads;
178 | @property (strong, nonatomic) NSNumber *totalNumber;
179 | @end
180 |
181 | /***********************************************/
182 |
183 | // Tell MJExtension what type model will be contained in statuses and ads.
184 | [StatusResult mj_setupObjectClassInArray:^NSDictionary *{
185 | return @{
186 | @"statuses" : @"Status",
187 | // @"statuses" : [Status class],
188 | @"ads" : @"Ad"
189 | // @"ads" : [Ad class]
190 | };
191 | }];
192 | // Equals: StatusResult.m implements +mj_objectClassInArray method.
193 |
194 | NSDictionary *dict = @{
195 | @"statuses" : @[
196 | @{
197 | @"text" : @"Nice weather!",
198 | @"user" : @{
199 | @"name" : @"Rose",
200 | @"icon" : @"nami.png"
201 | }
202 | },
203 | @{
204 | @"text" : @"Go camping tomorrow!",
205 | @"user" : @{
206 | @"name" : @"Jack",
207 | @"icon" : @"lufy.png"
208 | }
209 | }
210 | ],
211 | @"ads" : @[
212 | @{
213 | @"image" : @"ad01.png",
214 | @"url" : @"http://www.ad01.com"
215 | },
216 | @{
217 | @"image" : @"ad02.png",
218 | @"url" : @"http://www.ad02.com"
219 | }
220 | ],
221 | @"totalNumber" : @"2014"
222 | };
223 |
224 | // JSON -> StatusResult
225 | StatusResult *result = [StatusResult mj_objectWithKeyValues:dict];
226 |
227 | NSLog(@"totalNumber=%@", result.totalNumber);
228 | // totalNumber=2014
229 |
230 | // Printing
231 | for (Status *status in result.statuses) {
232 | NSString *text = status.text;
233 | NSString *name = status.user.name;
234 | NSString *icon = status.user.icon;
235 | NSLog(@"text=%@, name=%@, icon=%@", text, name, icon);
236 | }
237 | // text=Nice weather!, name=Rose, icon=nami.png
238 | // text=Go camping tomorrow!, name=Jack, icon=lufy.png
239 |
240 | // Printing
241 | for (Ad *ad in result.ads) {
242 | NSLog(@"image=%@, url=%@", ad.image, ad.url);
243 | }
244 | // image=ad01.png, url=http://www.ad01.com
245 | // image=ad02.png, url=http://www.ad02.com
246 | ```
247 |
248 | ### Model name - JSON key mapping【模型中的属性名和字典中的key不相同(或者需要多级映射)】
249 |
250 | ```objc
251 | @interface Bag : NSObject
252 | @property (copy, nonatomic) NSString *name;
253 | @property (assign, nonatomic) double price;
254 | @end
255 |
256 | @interface Student : NSObject
257 | @property (copy, nonatomic) NSString *ID;
258 | @property (copy, nonatomic) NSString *desc;
259 | @property (copy, nonatomic) NSString *nowName;
260 | @property (copy, nonatomic) NSString *oldName;
261 | @property (copy, nonatomic) NSString *nameChangedTime;
262 | @property (strong, nonatomic) Bag *bag;
263 | @end
264 |
265 | /***********************************************/
266 |
267 | // How to map
268 | [Student mj_setupReplacedKeyFromPropertyName:^NSDictionary *{
269 | return @{
270 | @"ID" : @"id",
271 | @"desc" : @"desciption",
272 | @"oldName" : @"name.oldName",
273 | @"nowName" : @"name.newName",
274 | @"nameChangedTime" : @"name.info[1].nameChangedTime",
275 | @"bag" : @"other.bag"
276 | };
277 | }];
278 | // Equals: Student.m implements +mj_replacedKeyFromPropertyName method.
279 |
280 | NSDictionary *dict = @{
281 | @"id" : @"20",
282 | @"desciption" : @"kids",
283 | @"name" : @{
284 | @"newName" : @"lufy",
285 | @"oldName" : @"kitty",
286 | @"info" : @[
287 | @"test-data",
288 | @{
289 | @"nameChangedTime" : @"2013-08"
290 | }
291 | ]
292 | },
293 | @"other" : @{
294 | @"bag" : @{
295 | @"name" : @"a red bag",
296 | @"price" : @100.7
297 | }
298 | }
299 | };
300 |
301 | // JSON -> Student
302 | Student *stu = [Student mj_objectWithKeyValues:dict];
303 |
304 | // Printing
305 | NSLog(@"ID=%@, desc=%@, oldName=%@, nowName=%@, nameChangedTime=%@",
306 | stu.ID, stu.desc, stu.oldName, stu.nowName, stu.nameChangedTime);
307 | // ID=20, desc=kids, oldName=kitty, nowName=lufy, nameChangedTime=2013-08
308 | NSLog(@"bagName=%@, bagPrice=%f", stu.bag.name, stu.bag.price);
309 | // bagName=a red bag, bagPrice=100.700000
310 | ```
311 |
312 |
313 | ### JSON array -> model array【将一个字典数组转成模型数组】
314 |
315 | ```objc
316 | NSArray *dictArray = @[
317 | @{
318 | @"name" : @"Jack",
319 | @"icon" : @"lufy.png"
320 | },
321 | @{
322 | @"name" : @"Rose",
323 | @"icon" : @"nami.png"
324 | }
325 | ];
326 |
327 | // JSON array -> User array
328 | NSArray *userArray = [User mj_objectArrayWithKeyValuesArray:dictArray];
329 |
330 | // Printing
331 | for (User *user in userArray) {
332 | NSLog(@"name=%@, icon=%@", user.name, user.icon);
333 | }
334 | // name=Jack, icon=lufy.png
335 | // name=Rose, icon=nami.png
336 | ```
337 |
338 | ### Model -> JSON【将一个模型转成字典】
339 | ```objc
340 | // New model
341 | User *user = [[User alloc] init];
342 | user.name = @"Jack";
343 | user.icon = @"lufy.png";
344 |
345 | Status *status = [[Status alloc] init];
346 | status.user = user;
347 | status.text = @"Nice mood!";
348 |
349 | // Status -> JSON
350 | NSDictionary *statusDict = status.mj_keyValues;
351 | NSLog(@"%@", statusDict);
352 | /*
353 | {
354 | text = "Nice mood!";
355 | user = {
356 | icon = "lufy.png";
357 | name = Jack;
358 | };
359 | }
360 | */
361 |
362 | // More complex situation
363 | Student *stu = [[Student alloc] init];
364 | stu.ID = @"123";
365 | stu.oldName = @"rose";
366 | stu.nowName = @"jack";
367 | stu.desc = @"handsome";
368 | stu.nameChangedTime = @"2018-09-08";
369 |
370 | Bag *bag = [[Bag alloc] init];
371 | bag.name = @"a red bag";
372 | bag.price = 205;
373 | stu.bag = bag;
374 |
375 | NSDictionary *stuDict = stu.mj_keyValues;
376 | NSLog(@"%@", stuDict);
377 | /*
378 | {
379 | ID = 123;
380 | bag = {
381 | name = "\U5c0f\U4e66\U5305";
382 | price = 205;
383 | };
384 | desc = handsome;
385 | nameChangedTime = "2018-09-08";
386 | nowName = jack;
387 | oldName = rose;
388 | }
389 | */
390 | ```
391 |
392 | ### Model array -> JSON array【将一个模型数组转成字典数组】
393 |
394 | ```objc
395 | // New model array
396 | User *user1 = [[User alloc] init];
397 | user1.name = @"Jack";
398 | user1.icon = @"lufy.png";
399 |
400 | User *user2 = [[User alloc] init];
401 | user2.name = @"Rose";
402 | user2.icon = @"nami.png";
403 |
404 | NSArray *userArray = @[user1, user2];
405 |
406 | // Model array -> JSON array
407 | NSArray *dictArray = [User mj_keyValuesArrayWithObjectArray:userArray];
408 | NSLog(@"%@", dictArray);
409 | /*
410 | (
411 | {
412 | icon = "lufy.png";
413 | name = Jack;
414 | },
415 | {
416 | icon = "nami.png";
417 | name = Rose;
418 | }
419 | )
420 | */
421 | ```
422 |
423 | ### Core Data
424 |
425 | ```objc
426 | NSDictionary *dict = @{
427 | @"name" : @"Jack",
428 | @"icon" : @"lufy.png",
429 | @"age" : @20,
430 | @"height" : @1.55,
431 | @"money" : @"100.9",
432 | @"sex" : @(SexFemale),
433 | @"gay" : @"true"
434 | };
435 |
436 | // This demo just provide simple steps
437 | NSManagedObjectContext *context = nil;
438 | User *user = [User mj_objectWithKeyValues:dict context:context];
439 |
440 | [context save:nil];
441 | ```
442 |
443 | ### Coding
444 |
445 | ```objc
446 | #import "MJExtension.h"
447 |
448 | @implementation Bag
449 | // NSCoding Implementation
450 | MJExtensionCodingImplementation
451 | @end
452 |
453 | /***********************************************/
454 |
455 | // what properties not to be coded
456 | [Bag mj_setupIgnoredCodingPropertyNames:^NSArray *{
457 | return @[@"name"];
458 | }];
459 | // Equals: Bag.m implements +mj_ignoredCodingPropertyNames method.
460 |
461 | // Create model
462 | Bag *bag = [[Bag alloc] init];
463 | bag.name = @"Red bag";
464 | bag.price = 200.8;
465 |
466 | NSString *file = [NSHomeDirectory() stringByAppendingPathComponent:@"Desktop/bag.data"];
467 | // Encoding
468 | [NSKeyedArchiver archiveRootObject:bag toFile:file];
469 |
470 | // Decoding
471 | Bag *decodedBag = [NSKeyedUnarchiver unarchiveObjectWithFile:file];
472 | NSLog(@"name=%@, price=%f", decodedBag.name, decodedBag.price);
473 | // name=(null), price=200.800000
474 | ```
475 |
476 | ### Camel -> underline【统一转换属性名(比如驼峰转下划线)】
477 | ```objc
478 | // Dog
479 | #import "MJExtension.h"
480 |
481 | @implementation Dog
482 | + (NSString *)mj_replacedKeyFromPropertyName121:(NSString *)propertyName
483 | {
484 | // nickName -> nick_name
485 | return [propertyName mj_underlineFromCamel];
486 | }
487 | @end
488 |
489 | // NSDictionary
490 | NSDictionary *dict = @{
491 | @"nick_name" : @"旺财",
492 | @"sale_price" : @"10.5",
493 | @"run_speed" : @"100.9"
494 | };
495 | // NSDictionary -> Dog
496 | Dog *dog = [Dog mj_objectWithKeyValues:dict];
497 |
498 | // printing
499 | NSLog(@"nickName=%@, scalePrice=%f runSpeed=%f", dog.nickName, dog.salePrice, dog.runSpeed);
500 | ```
501 |
502 | ### NSString -> NSDate, nil -> @""【过滤字典的值(比如字符串日期处理为NSDate、字符串nil处理为@"")】
503 | ```objc
504 | // Book
505 | #import "MJExtension.h"
506 |
507 | @implementation Book
508 | - (id)mj_newValueFromOldValue:(id)oldValue property:(MJProperty *)property
509 | {
510 | if ([property.name isEqualToString:@"publisher"]) {
511 | if (oldValue == nil) return @"";
512 | } else if (property.type.typeClass == [NSDate class]) {
513 | NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
514 | fmt.dateFormat = @"yyyy-MM-dd";
515 | return [fmt dateFromString:oldValue];
516 | }
517 |
518 | return oldValue;
519 | }
520 | @end
521 |
522 | // NSDictionary
523 | NSDictionary *dict = @{
524 | @"name" : @"5分钟突破iOS开发",
525 | @"publishedTime" : @"2011-09-10"
526 | };
527 | // NSDictionary -> Book
528 | Book *book = [Book mj_objectWithKeyValues:dict];
529 |
530 | // printing
531 | NSLog(@"name=%@, publisher=%@, publishedTime=%@", book.name, book.publisher, book.publishedTime);
532 | ```
533 |
534 | ### More use cases【更多用法】
535 | - Please reference to `NSObject+MJKeyValue.h` and `NSObject+MJCoding.h`
536 |
537 |
538 | ## 期待
539 | * 如果在使用过程中遇到BUG,希望你能Issues我,谢谢(或者尝试下载最新的框架代码看看BUG修复没有)
540 | * 如果在使用过程中发现功能不够用,希望你能Issues我,我非常想为这个框架增加更多好用的功能,谢谢
541 | * 如果你想为MJExtension输出代码,请拼命Pull Requests我
542 |
--------------------------------------------------------------------------------
/Pods/Manifest.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - MJExtension (3.0.13)
3 |
4 | DEPENDENCIES:
5 | - MJExtension (~> 3.0.13)
6 |
7 | SPEC CHECKSUMS:
8 | MJExtension: 5932755f451458eefa24239358817f8d291240c7
9 |
10 | PODFILE CHECKSUM: 6e750c1223c98f93978cd39ce2ef24209811a01f
11 |
12 | COCOAPODS: 1.4.0.beta.2
13 |
--------------------------------------------------------------------------------
/Pods/Pods.xcodeproj/xcuserdata/whj.xcuserdatad/xcschemes/MJExtension.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
45 |
46 |
52 |
53 |
55 |
56 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/Pods/Pods.xcodeproj/xcuserdata/whj.xcuserdatad/xcschemes/Pods-HJDownloadManager.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
45 |
46 |
52 |
53 |
55 |
56 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/Pods/Pods.xcodeproj/xcuserdata/whj.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | MJExtension.xcscheme
8 |
9 | isShown
10 |
11 | orderHint
12 | 0
13 |
14 | Pods-HJDownloadManager.xcscheme
15 |
16 | isShown
17 |
18 | orderHint
19 | 1
20 |
21 |
22 | SuppressBuildableAutocreation
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/MJExtension/MJExtension-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_MJExtension : NSObject
3 | @end
4 | @implementation PodsDummy_MJExtension
5 | @end
6 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/MJExtension/MJExtension-prefix.pch:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/MJExtension/MJExtension.xcconfig:
--------------------------------------------------------------------------------
1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MJExtension
2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/MJExtension" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/MJExtension"
4 | PODS_BUILD_DIR = ${BUILD_DIR}
5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
6 | PODS_ROOT = ${SRCROOT}
7 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/MJExtension
8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
9 | SKIP_INSTALL = YES
10 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-HJDownloadManager/Pods-HJDownloadManager-acknowledgements.markdown:
--------------------------------------------------------------------------------
1 | # Acknowledgements
2 | This application makes use of the following third party libraries:
3 |
4 | ## MJExtension
5 |
6 | Copyright (c) 2013-2015 MJExtension (https://github.com/CoderMJLee/MJExtension)
7 |
8 | Permission is hereby granted, free of charge, to any person obtaining a copy
9 | of this software and associated documentation files (the "Software"), to deal
10 | in the Software without restriction, including without limitation the rights
11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | copies of the Software, and to permit persons to whom the Software is
13 | furnished to do so, subject to the following conditions:
14 |
15 | The above copyright notice and this permission notice shall be included in
16 | all copies or substantial portions of the Software.
17 |
18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | THE SOFTWARE.
25 |
26 | Generated by CocoaPods - https://cocoapods.org
27 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-HJDownloadManager/Pods-HJDownloadManager-acknowledgements.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreferenceSpecifiers
6 |
7 |
8 | FooterText
9 | This application makes use of the following third party libraries:
10 | Title
11 | Acknowledgements
12 | Type
13 | PSGroupSpecifier
14 |
15 |
16 | FooterText
17 | Copyright (c) 2013-2015 MJExtension (https://github.com/CoderMJLee/MJExtension)
18 |
19 | Permission is hereby granted, free of charge, to any person obtaining a copy
20 | of this software and associated documentation files (the "Software"), to deal
21 | in the Software without restriction, including without limitation the rights
22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23 | copies of the Software, and to permit persons to whom the Software is
24 | furnished to do so, subject to the following conditions:
25 |
26 | The above copyright notice and this permission notice shall be included in
27 | all copies or substantial portions of the Software.
28 |
29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
35 | THE SOFTWARE.
36 |
37 | License
38 | MIT
39 | Title
40 | MJExtension
41 | Type
42 | PSGroupSpecifier
43 |
44 |
45 | FooterText
46 | Generated by CocoaPods - https://cocoapods.org
47 | Title
48 |
49 | Type
50 | PSGroupSpecifier
51 |
52 |
53 | StringsTable
54 | Acknowledgements
55 | Title
56 | Acknowledgements
57 |
58 |
59 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-HJDownloadManager/Pods-HJDownloadManager-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_Pods_HJDownloadManager : NSObject
3 | @end
4 | @implementation PodsDummy_Pods_HJDownloadManager
5 | @end
6 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-HJDownloadManager/Pods-HJDownloadManager-frameworks.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
6 |
7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
8 |
9 | # This protects against multiple targets copying the same framework dependency at the same time. The solution
10 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
11 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
12 |
13 | install_framework()
14 | {
15 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then
16 | local source="${BUILT_PRODUCTS_DIR}/$1"
17 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then
18 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")"
19 | elif [ -r "$1" ]; then
20 | local source="$1"
21 | fi
22 |
23 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
24 |
25 | if [ -L "${source}" ]; then
26 | echo "Symlinked..."
27 | source="$(readlink "${source}")"
28 | fi
29 |
30 | # Use filter instead of exclude so missing patterns don't throw errors.
31 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\""
32 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"
33 |
34 | local basename
35 | basename="$(basename -s .framework "$1")"
36 | binary="${destination}/${basename}.framework/${basename}"
37 | if ! [ -r "$binary" ]; then
38 | binary="${destination}/${basename}"
39 | fi
40 |
41 | # Strip invalid architectures so "fat" simulator / device frameworks work on device
42 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then
43 | strip_invalid_archs "$binary"
44 | fi
45 |
46 | # Resign the code if required by the build settings to avoid unstable apps
47 | code_sign_if_enabled "${destination}/$(basename "$1")"
48 |
49 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7.
50 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then
51 | local swift_runtime_libs
52 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]})
53 | for lib in $swift_runtime_libs; do
54 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\""
55 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"
56 | code_sign_if_enabled "${destination}/${lib}"
57 | done
58 | fi
59 | }
60 |
61 | # Copies the dSYM of a vendored framework
62 | install_dsym() {
63 | local source="$1"
64 | if [ -r "$source" ]; then
65 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DWARF_DSYM_FOLDER_PATH}\""
66 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DWARF_DSYM_FOLDER_PATH}"
67 | fi
68 |
69 | local basename
70 | basename="$(basename -s .framework.dSYM "$source")"
71 | binary="${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}"
72 |
73 | # Strip invalid architectures so "fat" simulator / device frameworks work on device
74 | if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then
75 | strip_invalid_archs "$binary"
76 | fi
77 | }
78 |
79 | # Signs a framework with the provided identity
80 | code_sign_if_enabled() {
81 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then
82 | # Use the current code_sign_identitiy
83 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
84 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'"
85 |
86 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
87 | code_sign_cmd="$code_sign_cmd &"
88 | fi
89 | echo "$code_sign_cmd"
90 | eval "$code_sign_cmd"
91 | fi
92 | }
93 |
94 | # Strip invalid architectures
95 | strip_invalid_archs() {
96 | binary="$1"
97 | # Get architectures for current file
98 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)"
99 | stripped=""
100 | for arch in $archs; do
101 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then
102 | # Strip non-valid architectures in-place
103 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1
104 | stripped="$stripped $arch"
105 | fi
106 | done
107 | if [[ "$stripped" ]]; then
108 | echo "Stripped $binary of architectures:$stripped"
109 | fi
110 | }
111 |
112 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
113 | wait
114 | fi
115 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-HJDownloadManager/Pods-HJDownloadManager-resources.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
5 |
6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt
7 | > "$RESOURCES_TO_COPY"
8 |
9 | XCASSET_FILES=()
10 |
11 | # This protects against multiple targets copying the same framework dependency at the same time. The solution
12 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
13 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
14 |
15 | case "${TARGETED_DEVICE_FAMILY}" in
16 | 1,2)
17 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone"
18 | ;;
19 | 1)
20 | TARGET_DEVICE_ARGS="--target-device iphone"
21 | ;;
22 | 2)
23 | TARGET_DEVICE_ARGS="--target-device ipad"
24 | ;;
25 | 3)
26 | TARGET_DEVICE_ARGS="--target-device tv"
27 | ;;
28 | 4)
29 | TARGET_DEVICE_ARGS="--target-device watch"
30 | ;;
31 | *)
32 | TARGET_DEVICE_ARGS="--target-device mac"
33 | ;;
34 | esac
35 |
36 | install_resource()
37 | {
38 | if [[ "$1" = /* ]] ; then
39 | RESOURCE_PATH="$1"
40 | else
41 | RESOURCE_PATH="${PODS_ROOT}/$1"
42 | fi
43 | if [[ ! -e "$RESOURCE_PATH" ]] ; then
44 | cat << EOM
45 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script.
46 | EOM
47 | exit 1
48 | fi
49 | case $RESOURCE_PATH in
50 | *.storyboard)
51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true
52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
53 | ;;
54 | *.xib)
55 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true
56 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
57 | ;;
58 | *.framework)
59 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true
60 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
61 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true
62 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
63 | ;;
64 | *.xcdatamodel)
65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true
66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom"
67 | ;;
68 | *.xcdatamodeld)
69 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true
70 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd"
71 | ;;
72 | *.xcmappingmodel)
73 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true
74 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm"
75 | ;;
76 | *.xcassets)
77 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH"
78 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE")
79 | ;;
80 | *)
81 | echo "$RESOURCE_PATH" || true
82 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY"
83 | ;;
84 | esac
85 | }
86 |
87 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
88 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
89 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then
90 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
91 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
92 | fi
93 | rm -f "$RESOURCES_TO_COPY"
94 |
95 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ]
96 | then
97 | # Find all other xcassets (this unfortunately includes those of path pods and other targets).
98 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d)
99 | while read line; do
100 | if [[ $line != "${PODS_ROOT}*" ]]; then
101 | XCASSET_FILES+=("$line")
102 | fi
103 | done <<<"$OTHER_XCASSETS"
104 |
105 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
106 | fi
107 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-HJDownloadManager/Pods-HJDownloadManager.debug.xcconfig:
--------------------------------------------------------------------------------
1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
2 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/MJExtension"
3 | LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MJExtension"
4 | OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/MJExtension"
5 | OTHER_LDFLAGS = $(inherited) -ObjC -l"MJExtension"
6 | PODS_BUILD_DIR = ${BUILD_DIR}
7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
8 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
9 | PODS_ROOT = ${SRCROOT}/Pods
10 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-HJDownloadManager/Pods-HJDownloadManager.release.xcconfig:
--------------------------------------------------------------------------------
1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
2 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/MJExtension"
3 | LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MJExtension"
4 | OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/MJExtension"
5 | OTHER_LDFLAGS = $(inherited) -ObjC -l"MJExtension"
6 | PODS_BUILD_DIR = ${BUILD_DIR}
7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
8 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
9 | PODS_ROOT = ${SRCROOT}/Pods
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # HJDownloadManager
2 |
3 | 简介:
4 | >该框架是一个支持单任务下载和批量下载的下载框架,实现方式比较抽象,如果大家有好的思路,可以分享交流一下,生命不止,学习不息。轰 轰 轰~
5 |
6 | >>1.该下载框架采用自定义NSOperation来封装下载任务,并手动管理下载任务和operation的生命周期
7 | >>2.然后将operation添加到NSOperationQueue中进行操作的并发管理
8 | >>3.下载时以流的方式来保存下载数据,避免数据丢失!
9 | >>4.使用NSGetUncaughtExceptionHandler来进行crash获取,并发送通知来保存下载信息
10 |
11 | 功能:
12 |
13 | >1.支持单个任务的开始、暂停、恢复和删除等常用功能
14 | 2.支持多个任务的各项常用批量操作(开始、暂停、恢复和删除)
15 | 3.支持后台下载(程序退到后台继续下载)
16 | 4.支持程序强关或闪退后,重新启动程序时恢复下载任务
17 | 5.支持设置最大并发数(当一个任务完成或移除时,自动下载等待中的任务)
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/podfile:
--------------------------------------------------------------------------------
1 | platform :ios, '7.0'
2 |
3 | target :HJDownloadManager do
4 |
5 |
6 | pod 'MJExtension', '~> 3.0.13'
7 |
8 |
9 | end
--------------------------------------------------------------------------------