├── 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 | ![Logo](http://images.cnitblog.com/blog2015/497279/201505/051004316736641.png) 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 --------------------------------------------------------------------------------