├── fmd数组.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── jglz.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── jglz.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── fmd数组.xcscheme │ └── xcschememanagement.plist ├── fmd数组 ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── Lib │ ├── MJExtension │ │ ├── MJDictionaryCache.h │ │ ├── MJDictionaryCache.m │ │ ├── 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 │ ├── Store │ │ ├── MVVMStore.h │ │ └── MVVMStore.m │ ├── Utils │ │ ├── Foundation+Log.m │ │ └── HMSingleton.h │ ├── YTKKeyValueStore │ │ ├── YTKKeyValueStore.h │ │ └── YTKKeyValueStore.m │ └── fmdb │ │ ├── FMDB.h │ │ ├── FMDatabase.h │ │ ├── FMDatabase.m │ │ ├── FMDatabaseAdditions.h │ │ ├── FMDatabaseAdditions.m │ │ ├── FMDatabasePool.h │ │ ├── FMDatabasePool.m │ │ ├── FMDatabaseQueue.h │ │ ├── FMDatabaseQueue.m │ │ ├── FMResultSet.h │ │ └── FMResultSet.m ├── People.h ├── People.m ├── ViewController.h ├── ViewController.m └── main.m ├── fmd数组Tests ├── Info.plist └── fmd__Tests.m └── fmd数组UITests ├── Info.plist └── fmd__UITests.m /fmd数组.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /fmd数组.xcodeproj/project.xcworkspace/xcuserdata/jglz.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jglz/fmd-/3d3d9905b9faa16410d0209db054da9cd8cc6bab/fmd数组.xcodeproj/project.xcworkspace/xcuserdata/jglz.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /fmd数组.xcodeproj/xcuserdata/jglz.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 20 | 21 | 22 | 24 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /fmd数组.xcodeproj/xcuserdata/jglz.xcuserdatad/xcschemes/fmd数组.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /fmd数组.xcodeproj/xcuserdata/jglz.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | fmd数组.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 69BB90841CD2F3E400471662 16 | 17 | primary 18 | 19 | 20 | 69BB909D1CD2F3E400471662 21 | 22 | primary 23 | 24 | 25 | 69BB90A81CD2F3E400471662 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /fmd数组/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // fmd数组 4 | // 5 | // Created by jglz on 16/4/29. 6 | // Copyright © 2016年 yxb. 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 | -------------------------------------------------------------------------------- /fmd数组/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // fmd数组 4 | // 5 | // Created by jglz on 16/4/29. 6 | // Copyright © 2016年 yxb. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /fmd数组/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /fmd数组/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /fmd数组/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /fmd数组/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 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 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /fmd数组/Lib/MJExtension/MJDictionaryCache.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJDictionaryCache.h 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/8/22. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MJDictionaryCache : NSObject 12 | /** 13 | * 缓存数据 14 | * 15 | * @param dictId 字典标识 16 | * 17 | * @return 缓存的字典 18 | */ 19 | + (id)setObject:(id)object forKey:(id)key forDictId:(const void *)dictId; 20 | 21 | /** 22 | * 获得缓存的数据 23 | * 24 | * @param dictId 字典标识 25 | */ 26 | + (id)objectForKey:(id)key forDictId:(const void *)dictId; 27 | 28 | /** 29 | * 获得缓存的字典 30 | * 31 | * @param dictId 字典标识 32 | */ 33 | + (id)dictWithDictId:(const void *)dictId; 34 | @end 35 | -------------------------------------------------------------------------------- /fmd数组/Lib/MJExtension/MJDictionaryCache.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJDictionaryCache.m 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/8/22. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJDictionaryCache.h" 10 | #import 11 | 12 | @implementation MJDictionaryCache 13 | + (id)setObject:(id)object forKey:(id)key forDictId:(const void *)dictId 14 | { 15 | // 获得字典 16 | NSMutableDictionary *dict = [self dictWithDictId:dictId]; 17 | if (dict == nil) { 18 | dict = [NSMutableDictionary dictionary]; 19 | objc_setAssociatedObject(self, dictId, dict, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 20 | } 21 | 22 | // 存储数据 23 | dict[key] = object; 24 | 25 | return dict; 26 | } 27 | 28 | + (id)objectForKey:(id)key forDictId:(const void *)dictId 29 | { 30 | return [self dictWithDictId:dictId][key]; 31 | } 32 | 33 | + (id)dictWithDictId:(const void *)dictId 34 | { 35 | return objc_getAssociatedObject(self, dictId); 36 | } 37 | @end -------------------------------------------------------------------------------- /fmd数组/Lib/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 | -------------------------------------------------------------------------------- /fmd数组/Lib/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(error, msg) \ 12 | if (error) *error = [NSError errorWithDomain:msg code:250 userInfo:nil]; 13 | 14 | /** 15 | * 断言 16 | * @param condition 条件 17 | * @param returnValue 返回值 18 | */ 19 | #define MJExtensionAssertError(condition, returnValue, error, msg) \ 20 | if ((condition) == NO) { \ 21 | MJExtensionBuildError(error, msg); \ 22 | return returnValue;\ 23 | } 24 | 25 | #define MJExtensionAssert2(condition, returnValue) \ 26 | if ((condition) == NO) return returnValue; 27 | 28 | /** 29 | * 断言 30 | * @param condition 条件 31 | */ 32 | #define MJExtensionAssert(condition) MJExtensionAssert2(condition, ) 33 | 34 | /** 35 | * 断言 36 | * @param param 参数 37 | * @param returnValue 返回值 38 | */ 39 | #define MJExtensionAssertParamNotNil2(param, returnValue) \ 40 | MJExtensionAssert2((param) != nil, returnValue) 41 | 42 | /** 43 | * 断言 44 | * @param param 参数 45 | */ 46 | #define MJExtensionAssertParamNotNil(param) MJExtensionAssertParamNotNil2(param, ) 47 | 48 | /** 49 | * 打印所有的属性 50 | */ 51 | #define MJLogAllIvars \ 52 | -(NSString *)description \ 53 | { \ 54 | return [self keyValues].description; \ 55 | } 56 | #define MJExtensionLogAllProperties MJLogAllIvars 57 | 58 | /** 59 | * 类型(属性类型) 60 | */ 61 | extern NSString *const MJPropertyTypeInt; 62 | extern NSString *const MJPropertyTypeShort; 63 | extern NSString *const MJPropertyTypeFloat; 64 | extern NSString *const MJPropertyTypeDouble; 65 | extern NSString *const MJPropertyTypeLong; 66 | extern NSString *const MJPropertyTypeLongLong; 67 | extern NSString *const MJPropertyTypeChar; 68 | extern NSString *const MJPropertyTypeBOOL1; 69 | extern NSString *const MJPropertyTypeBOOL2; 70 | extern NSString *const MJPropertyTypePointer; 71 | 72 | extern NSString *const MJPropertyTypeIvar; 73 | extern NSString *const MJPropertyTypeMethod; 74 | extern NSString *const MJPropertyTypeBlock; 75 | extern NSString *const MJPropertyTypeClass; 76 | extern NSString *const MJPropertyTypeSEL; 77 | extern NSString *const MJPropertyTypeId; 78 | 79 | #endif -------------------------------------------------------------------------------- /fmd数组/Lib/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 -------------------------------------------------------------------------------- /fmd数组/Lib/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 | -------------------------------------------------------------------------------- /fmd数组/Lib/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 | -------------------------------------------------------------------------------- /fmd数组/Lib/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 | -------------------------------------------------------------------------------- /fmd数组/Lib/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 | 13 | @interface MJProperty() 14 | @property (strong, nonatomic) NSMutableDictionary *propertyKeysDict; 15 | @property (strong, nonatomic) NSMutableDictionary *objectClassInArrayDict; 16 | @end 17 | 18 | @implementation MJProperty 19 | 20 | #pragma mark - 懒加载 21 | - (NSMutableDictionary *)propertyKeysDict 22 | { 23 | if (!_propertyKeysDict) { 24 | _propertyKeysDict = [NSMutableDictionary dictionary]; 25 | } 26 | return _propertyKeysDict; 27 | } 28 | 29 | - (NSMutableDictionary *)objectClassInArrayDict 30 | { 31 | if (!_objectClassInArrayDict) { 32 | _objectClassInArrayDict = [NSMutableDictionary dictionary]; 33 | } 34 | return _objectClassInArrayDict; 35 | } 36 | 37 | #pragma mark - 缓存 38 | + (instancetype)cachedPropertyWithProperty:(objc_property_t)property 39 | { 40 | MJProperty *propertyObj = objc_getAssociatedObject(self, property); 41 | if (propertyObj == nil) { 42 | propertyObj = [[self alloc] init]; 43 | propertyObj.property = property; 44 | objc_setAssociatedObject(self, property, propertyObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 45 | } 46 | return propertyObj; 47 | } 48 | 49 | #pragma mark - 公共方法 50 | - (void)setProperty:(objc_property_t)property 51 | { 52 | _property = property; 53 | 54 | MJExtensionAssertParamNotNil(property); 55 | 56 | // 1.属性名 57 | _name = @(property_getName(property)); 58 | 59 | // 2.成员类型 60 | NSString *attrs = @(property_getAttributes(property)); 61 | NSUInteger dotLoc = [attrs rangeOfString:@","].location; 62 | NSString *code = nil; 63 | NSUInteger loc = 1; 64 | if (dotLoc == NSNotFound) { // 没有, 65 | code = [attrs substringFromIndex:loc]; 66 | } else { 67 | code = [attrs substringWithRange:NSMakeRange(loc, dotLoc - loc)]; 68 | } 69 | _type = [MJPropertyType cachedTypeWithCode:code]; 70 | } 71 | 72 | /** 73 | * 获得成员变量的值 74 | */ 75 | - (id)valueForObject:(id)object 76 | { 77 | if (self.type.KVCDisabled) return [NSNull null]; 78 | return [object valueForKey:self.name]; 79 | } 80 | 81 | /** 82 | * 设置成员变量的值 83 | */ 84 | - (void)setValue:(id)value forObject:(id)object 85 | { 86 | if (self.type.KVCDisabled || value == nil) return; 87 | [object setValue:value forKey:self.name]; 88 | } 89 | 90 | /** 91 | * 通过字符串key创建对应的keys 92 | */ 93 | - (NSArray *)propertyKeysWithStringKey:(NSString *)stringKey 94 | { 95 | if (stringKey.length == 0) return nil; 96 | 97 | NSMutableArray *propertyKeys = [NSMutableArray array]; 98 | // 如果有多级映射 99 | NSArray *oldKeys = [stringKey componentsSeparatedByString:@"."]; 100 | 101 | for (NSString *oldKey in oldKeys) { 102 | NSUInteger start = [oldKey rangeOfString:@"["].location; 103 | if (start != NSNotFound) { // 有索引的key 104 | NSString *prefixKey = [oldKey substringToIndex:start]; 105 | NSString *indexKey = prefixKey; 106 | if (prefixKey.length) { 107 | MJPropertyKey *propertyKey = [[MJPropertyKey alloc] init]; 108 | propertyKey.name = prefixKey; 109 | [propertyKeys addObject:propertyKey]; 110 | 111 | indexKey = [oldKey stringByReplacingOccurrencesOfString:prefixKey withString:@""]; 112 | } 113 | 114 | /** 解析索引 **/ 115 | // 元素 116 | NSArray *cmps = [[indexKey stringByReplacingOccurrencesOfString:@"[" withString:@""] componentsSeparatedByString:@"]"]; 117 | 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 | -------------------------------------------------------------------------------- /fmd数组/Lib/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 | return ((NSArray *)object).count ? object[self.name.intValue] : nil; 19 | } 20 | return nil; 21 | } 22 | @end 23 | -------------------------------------------------------------------------------- /fmd数组/Lib/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 -------------------------------------------------------------------------------- /fmd数组/Lib/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 | #import "MJDictionaryCache.h" 14 | 15 | @implementation MJPropertyType 16 | 17 | + (instancetype)cachedTypeWithCode:(NSString *)code 18 | { 19 | MJExtensionAssertParamNotNil2(code, nil); 20 | 21 | static const char MJCachedTypesKey = '\0'; 22 | 23 | MJPropertyType *type = [MJDictionaryCache objectForKey:code forDictId:&MJCachedTypesKey]; 24 | if (type == nil) { 25 | type = [[self alloc] init]; 26 | type.code = code; 27 | [MJDictionaryCache setObject:type forKey:code forDictId:&MJCachedTypesKey]; 28 | } 29 | return type; 30 | } 31 | 32 | #pragma mark - 公共方法 33 | - (void)setCode:(NSString *)code 34 | { 35 | _code = code; 36 | 37 | MJExtensionAssertParamNotNil(code); 38 | 39 | if ([code isEqualToString:MJPropertyTypeId]) { 40 | _idType = YES; 41 | } else if (code.length == 0) { 42 | _KVCDisabled = YES; 43 | } else if (code.length > 3 && [code hasPrefix:@"@\""]) { 44 | // 去掉@"和",截取中间的类型名称 45 | _code = [code substringWithRange:NSMakeRange(2, code.length - 3)]; 46 | _typeClass = NSClassFromString(_code); 47 | _fromFoundation = [MJFoundation isClassFromFoundation:_typeClass]; 48 | _numberType = [_typeClass isSubclassOfClass:[NSNumber class]]; 49 | 50 | } else if ([code isEqualToString:MJPropertyTypeSEL] || 51 | [code isEqualToString:MJPropertyTypeIvar] || 52 | [code isEqualToString:MJPropertyTypeMethod]) { 53 | _KVCDisabled = YES; 54 | } 55 | 56 | // 是否为数字类型 57 | NSString *lowerCode = _code.lowercaseString; 58 | NSArray *numberTypes = @[MJPropertyTypeInt, MJPropertyTypeShort, MJPropertyTypeBOOL1, MJPropertyTypeBOOL2, MJPropertyTypeFloat, MJPropertyTypeDouble, MJPropertyTypeLong, MJPropertyTypeLongLong, MJPropertyTypeChar]; 59 | if ([numberTypes containsObject:lowerCode]) { 60 | _numberType = YES; 61 | 62 | if ([lowerCode isEqualToString:MJPropertyTypeBOOL1] 63 | || [lowerCode isEqualToString:MJPropertyTypeBOOL2]) { 64 | _boolType = YES; 65 | } 66 | } 67 | } 68 | @end 69 | -------------------------------------------------------------------------------- /fmd数组/Lib/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)enumerateClasses:(MJClassesEnumeration)enumeration; 34 | + (void)enumerateAllClasses:(MJClassesEnumeration)enumeration; 35 | 36 | #pragma mark - 属性白名单配置 37 | /** 38 | * 这个数组中的属性名才会进行字典和模型的转换 39 | * 40 | * @param allowedPropertyNames 这个数组中的属性名才会进行字典和模型的转换 41 | */ 42 | + (void)setupAllowedPropertyNames:(MJAllowedPropertyNames)allowedPropertyNames; 43 | 44 | /** 45 | * 这个数组中的属性名才会进行字典和模型的转换 46 | */ 47 | + (NSMutableArray *)totalAllowedPropertyNames; 48 | 49 | #pragma mark - 属性黑名单配置 50 | /** 51 | * 这个数组中的属性名将会被忽略:不进行字典和模型的转换 52 | * 53 | * @param ignoredPropertyNames 这个数组中的属性名将会被忽略:不进行字典和模型的转换 54 | */ 55 | + (void)setupIgnoredPropertyNames:(MJIgnoredPropertyNames)ignoredPropertyNames; 56 | 57 | /** 58 | * 这个数组中的属性名将会被忽略:不进行字典和模型的转换 59 | */ 60 | + (NSMutableArray *)totalIgnoredPropertyNames; 61 | 62 | #pragma mark - 归档属性白名单配置 63 | /** 64 | * 这个数组中的属性名才会进行归档 65 | * 66 | * @param allowedCodingPropertyNames 这个数组中的属性名才会进行归档 67 | */ 68 | + (void)setupAllowedCodingPropertyNames:(MJAllowedCodingPropertyNames)allowedCodingPropertyNames; 69 | 70 | /** 71 | * 这个数组中的属性名才会进行字典和模型的转换 72 | */ 73 | + (NSMutableArray *)totalAllowedCodingPropertyNames; 74 | 75 | #pragma mark - 归档属性黑名单配置 76 | /** 77 | * 这个数组中的属性名将会被忽略:不进行归档 78 | * 79 | * @param ignoredCodingPropertyNames 这个数组中的属性名将会被忽略:不进行归档 80 | */ 81 | + (void)setupIgnoredCodingPropertyNames:(MJIgnoredCodingPropertyNames)ignoredCodingPropertyNames; 82 | 83 | /** 84 | * 这个数组中的属性名将会被忽略:不进行归档 85 | */ 86 | + (NSMutableArray *)totalIgnoredCodingPropertyNames; 87 | 88 | #pragma mark - 内部使用 89 | + (void)setupBlockReturnValue:(id (^)())block key:(const char *)key; 90 | @end 91 | -------------------------------------------------------------------------------- /fmd数组/Lib/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 | #import "MJDictionaryCache.h" 15 | 16 | static const char MJAllowedPropertyNamesKey = '\0'; 17 | static const char MJIgnoredPropertyNamesKey = '\0'; 18 | static const char MJAllowedCodingPropertyNamesKey = '\0'; 19 | static const char MJIgnoredCodingPropertyNamesKey = '\0'; 20 | 21 | @implementation NSObject (MJClass) 22 | 23 | + (void)enumerateClasses:(MJClassesEnumeration)enumeration 24 | { 25 | // 1.没有block就直接返回 26 | if (enumeration == nil) return; 27 | 28 | // 2.停止遍历的标记 29 | BOOL stop = NO; 30 | 31 | // 3.当前正在遍历的类 32 | Class c = self; 33 | 34 | // 4.开始遍历每一个类 35 | while (c && !stop) { 36 | // 4.1.执行操作 37 | enumeration(c, &stop); 38 | 39 | // 4.2.获得父类 40 | c = class_getSuperclass(c); 41 | 42 | if ([MJFoundation isClassFromFoundation:c]) break; 43 | } 44 | } 45 | 46 | + (void)enumerateAllClasses:(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 | } 66 | 67 | #pragma mark - 属性黑名单配置 68 | + (void)setupIgnoredPropertyNames:(MJIgnoredPropertyNames)ignoredPropertyNames 69 | { 70 | [self setupBlockReturnValue:ignoredPropertyNames key:&MJIgnoredPropertyNamesKey]; 71 | } 72 | 73 | + (NSMutableArray *)totalIgnoredPropertyNames 74 | { 75 | return [self totalObjectsWithSelector:@selector(ignoredPropertyNames) key:&MJIgnoredPropertyNamesKey]; 76 | } 77 | 78 | #pragma mark - 归档属性黑名单配置 79 | + (void)setupIgnoredCodingPropertyNames:(MJIgnoredCodingPropertyNames)ignoredCodingPropertyNames 80 | { 81 | [self setupBlockReturnValue:ignoredCodingPropertyNames key:&MJIgnoredCodingPropertyNamesKey]; 82 | } 83 | 84 | + (NSMutableArray *)totalIgnoredCodingPropertyNames 85 | { 86 | return [self totalObjectsWithSelector:@selector(ignoredCodingPropertyNames) key:&MJIgnoredCodingPropertyNamesKey]; 87 | } 88 | 89 | #pragma mark - 属性白名单配置 90 | + (void)setupAllowedPropertyNames:(MJAllowedPropertyNames)allowedPropertyNames; 91 | { 92 | [self setupBlockReturnValue:allowedPropertyNames key:&MJAllowedPropertyNamesKey]; 93 | } 94 | 95 | + (NSMutableArray *)totalAllowedPropertyNames 96 | { 97 | return [self totalObjectsWithSelector:@selector(allowedPropertyNames) key:&MJAllowedPropertyNamesKey]; 98 | } 99 | 100 | #pragma mark - 归档属性白名单配置 101 | + (void)setupAllowedCodingPropertyNames:(MJAllowedCodingPropertyNames)allowedCodingPropertyNames 102 | { 103 | [self setupBlockReturnValue:allowedCodingPropertyNames key:&MJAllowedCodingPropertyNamesKey]; 104 | } 105 | 106 | + (NSMutableArray *)totalAllowedCodingPropertyNames 107 | { 108 | return [self totalObjectsWithSelector:@selector(allowedCodingPropertyNames) key:&MJAllowedCodingPropertyNamesKey]; 109 | } 110 | #pragma mark - block和方法处理:存储block的返回值 111 | + (void)setupBlockReturnValue:(id (^)())block key:(const char *)key 112 | { 113 | if (block) { 114 | objc_setAssociatedObject(self, key, block(), OBJC_ASSOCIATION_RETAIN_NONATOMIC); 115 | } else { 116 | objc_setAssociatedObject(self, key, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 117 | } 118 | 119 | // 清空数据 120 | [[MJDictionaryCache dictWithDictId:key] removeAllObjects]; 121 | } 122 | 123 | + (NSMutableArray *)totalObjectsWithSelector:(SEL)selector key:(const char *)key 124 | { 125 | NSMutableArray *array = [MJDictionaryCache objectForKey:NSStringFromClass(self) forDictId:key]; 126 | if (array) return array; 127 | 128 | // 创建、存储 129 | [MJDictionaryCache setObject:array = [NSMutableArray array] forKey:NSStringFromClass(self) forDictId:key]; 130 | 131 | if ([self respondsToSelector:selector]) { 132 | #pragma clang diagnostic push 133 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 134 | NSArray *subArray = [self performSelector:selector]; 135 | #pragma clang diagnostic pop 136 | if (subArray) { 137 | [array addObjectsFromArray:subArray]; 138 | } 139 | } 140 | 141 | [self enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { 142 | NSArray *subArray = objc_getAssociatedObject(c, key); 143 | [array addObjectsFromArray:subArray]; 144 | }]; 145 | return array; 146 | } 147 | @end 148 | -------------------------------------------------------------------------------- /fmd数组/Lib/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 | /** 11 | * Codeing协议 12 | */ 13 | @protocol MJCoding 14 | @optional 15 | /** 16 | * 这个数组中的属性名才会进行归档 17 | */ 18 | + (NSArray *)allowedCodingPropertyNames; 19 | /** 20 | * 这个数组中的属性名将会被忽略:不进行归档 21 | */ 22 | + (NSArray *)ignoredCodingPropertyNames; 23 | @end 24 | 25 | @interface NSObject (MJCoding) 26 | /** 27 | * 解码(从文件中解析对象) 28 | */ 29 | - (void)decode:(NSCoder *)decoder; 30 | /** 31 | * 编码(将对象写入文件中) 32 | */ 33 | - (void)encode:(NSCoder *)encoder; 34 | @end 35 | 36 | /** 37 | 归档的实现 38 | */ 39 | #define MJCodingImplementation \ 40 | - (id)initWithCoder:(NSCoder *)decoder \ 41 | { \ 42 | if (self = [super init]) { \ 43 | [self decode:decoder]; \ 44 | } \ 45 | return self; \ 46 | } \ 47 | \ 48 | - (void)encodeWithCoder:(NSCoder *)encoder \ 49 | { \ 50 | [self encode:encoder]; \ 51 | } 52 | 53 | #define MJExtensionCodingImplementation MJCodingImplementation -------------------------------------------------------------------------------- /fmd数组/Lib/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)encode:(NSCoder *)encoder 17 | { 18 | Class aClass = [self class]; 19 | 20 | NSArray *allowedCodingPropertyNames = [aClass totalAllowedCodingPropertyNames]; 21 | NSArray *ignoredCodingPropertyNames = [aClass totalIgnoredCodingPropertyNames]; 22 | 23 | [aClass 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)decode:(NSCoder *)decoder 35 | { 36 | Class aClass = [self class]; 37 | 38 | NSArray *allowedCodingPropertyNames = [aClass totalAllowedCodingPropertyNames]; 39 | NSArray *ignoredCodingPropertyNames = [aClass totalIgnoredCodingPropertyNames]; 40 | 41 | [aClass 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 | -------------------------------------------------------------------------------- /fmd数组/Lib/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 *)allowedPropertyNames; 23 | 24 | /** 25 | * 这个数组中的属性名将会被忽略:不进行字典和模型的转换 26 | */ 27 | + (NSArray *)ignoredPropertyNames; 28 | 29 | /** 30 | * 将属性名换为其他key去字典中取值 31 | * 32 | * @return 字典中的key是属性名,value是从字典中取值用的key 33 | */ 34 | + (NSDictionary *)replacedKeyFromPropertyName; 35 | 36 | /** 37 | * 将属性名换为其他key去字典中取值 38 | * 39 | * @return 从字典中取值用的key 40 | */ 41 | + (NSString *)replacedKeyFromPropertyName121:(NSString *)propertyName; 42 | 43 | /** 44 | * 数组中需要转换的模型类 45 | * 46 | * @return 字典中的key是数组属性名,value是数组中存放模型的Class(Class类型或者NSString类型) 47 | */ 48 | + (NSDictionary *)objectClassInArray; 49 | 50 | /** 51 | * 旧值换新值,用于过滤字典中的值 52 | * 53 | * @param oldValue 旧值 54 | * 55 | * @return 新值 56 | */ 57 | - (id)newValueFromOldValue:(id)oldValue property:(MJProperty *)property; 58 | 59 | /** 60 | * 当字典转模型完毕时调用 61 | */ 62 | - (void)keyValuesDidFinishConvertingToObject; 63 | 64 | /** 65 | * 当模型转字典完毕时调用 66 | */ 67 | - (void)objectDidFinishConvertingToKeyValues; 68 | @end 69 | 70 | @interface NSObject (MJKeyValue) 71 | /** 72 | * 将字典的键值对转成模型属性 73 | * @param keyValues 字典(可以是NSDictionary、NSData、NSString) 74 | */ 75 | - (instancetype)setKeyValues:(id)keyValues; 76 | - (instancetype)setKeyValues:(id)keyValues error:(NSError **)error; 77 | 78 | /** 79 | * 将字典的键值对转成模型属性 80 | * @param keyValues 字典(可以是NSDictionary、NSData、NSString) 81 | * @param context CoreData上下文 82 | */ 83 | - (instancetype)setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context; 84 | - (instancetype)setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context error:(NSError **)error; 85 | 86 | /** 87 | * 模型转字典时,字典的key是否参考replacedKeyFromPropertyName等方法(父类设置了,子类也会继承下来) 88 | */ 89 | + (void)referenceReplacedKeyWhenCreatingKeyValues:(BOOL)reference; 90 | 91 | /** 92 | * 将模型转成字典 93 | * @return 字典 94 | */ 95 | - (NSMutableDictionary *)keyValues; 96 | - (NSMutableDictionary *)keyValuesWithError:(NSError **)error; 97 | - (NSMutableDictionary *)keyValuesWithKeys:(NSArray *)keys; 98 | - (NSMutableDictionary *)keyValuesWithKeys:(NSArray *)keys error:(NSError **)error; 99 | - (NSMutableDictionary *)keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys; 100 | - (NSMutableDictionary *)keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys error:(NSError **)error; 101 | 102 | /** 103 | * 通过模型数组来创建一个字典数组 104 | * @param objectArray 模型数组 105 | * @return 字典数组 106 | */ 107 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray; 108 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray error:(NSError **)error; 109 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys; 110 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys error:(NSError **)error; 111 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys; 112 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys error:(NSError **)error; 113 | 114 | #pragma mark - 字典转模型 115 | /** 116 | * 通过字典来创建一个模型 117 | * @param keyValues 字典(可以是NSDictionary、NSData、NSString) 118 | * @return 新建的对象 119 | */ 120 | + (instancetype)objectWithKeyValues:(id)keyValues; 121 | + (instancetype)objectWithKeyValues:(id)keyValues error:(NSError **)error; 122 | 123 | /** 124 | * 通过字典来创建一个CoreData模型 125 | * @param keyValues 字典(可以是NSDictionary、NSData、NSString) 126 | * @param context CoreData上下文 127 | * @return 新建的对象 128 | */ 129 | + (instancetype)objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context; 130 | + (instancetype)objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context error:(NSError **)error; 131 | 132 | /** 133 | * 通过plist来创建一个模型 134 | * @param filename 文件名(仅限于mainBundle中的文件) 135 | * @return 新建的对象 136 | */ 137 | + (instancetype)objectWithFilename:(NSString *)filename; 138 | + (instancetype)objectWithFilename:(NSString *)filename error:(NSError **)error; 139 | 140 | /** 141 | * 通过plist来创建一个模型 142 | * @param file 文件全路径 143 | * @return 新建的对象 144 | */ 145 | + (instancetype)objectWithFile:(NSString *)file; 146 | + (instancetype)objectWithFile:(NSString *)file error:(NSError **)error; 147 | 148 | #pragma mark - 字典数组转模型数组 149 | /** 150 | * 通过字典数组来创建一个模型数组 151 | * @param keyValuesArray 字典数组(可以是NSDictionary、NSData、NSString) 152 | * @return 模型数组 153 | */ 154 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray; 155 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray error:(NSError **)error; 156 | 157 | /** 158 | * 通过字典数组来创建一个模型数组 159 | * @param keyValuesArray 字典数组(可以是NSDictionary、NSData、NSString) 160 | * @param context CoreData上下文 161 | * @return 模型数组 162 | */ 163 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context; 164 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context error:(NSError **)error; 165 | 166 | /** 167 | * 通过plist来创建一个模型数组 168 | * @param filename 文件名(仅限于mainBundle中的文件) 169 | * @return 模型数组 170 | */ 171 | + (NSMutableArray *)objectArrayWithFilename:(NSString *)filename; 172 | + (NSMutableArray *)objectArrayWithFilename:(NSString *)filename error:(NSError **)error; 173 | 174 | /** 175 | * 通过plist来创建一个模型数组 176 | * @param file 文件全路径 177 | * @return 模型数组 178 | */ 179 | + (NSMutableArray *)objectArrayWithFile:(NSString *)file; 180 | + (NSMutableArray *)objectArrayWithFile:(NSString *)file error:(NSError **)error; 181 | 182 | #pragma mark - 转换为JSON 183 | /** 184 | * 转换为JSON Data 185 | */ 186 | - (NSData *)JSONData; 187 | /** 188 | * 转换为字典或者数组 189 | */ 190 | - (id)JSONObject; 191 | /** 192 | * 转换为JSON 字符串 193 | */ 194 | - (NSString *)JSONString; 195 | @end 196 | -------------------------------------------------------------------------------- /fmd数组/Lib/MJExtension/NSObject+MJKeyValue.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+MJKeyValue.m 3 | // MJExtension 4 | // 5 | // Created by mj on 13-8-24. 6 | // Copyright (c) 2013年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "NSObject+MJKeyValue.h" 10 | #import "NSObject+MJProperty.h" 11 | #import "NSString+MJExtension.h" 12 | #import "MJProperty.h" 13 | #import "MJPropertyType.h" 14 | #import "MJExtensionConst.h" 15 | #import "MJFoundation.h" 16 | #import "NSString+MJExtension.h" 17 | #import "NSObject+MJClass.h" 18 | 19 | @implementation NSObject (MJKeyValue) 20 | 21 | #pragma mark - 模型 -> 字典时的参考 22 | /** 模型转字典时,字典的key是否参考replacedKeyFromPropertyName等方法(父类设置了,子类也会继承下来) */ 23 | static const char MJReferenceReplacedKeyWhenCreatingKeyValuesKey = '\0'; 24 | 25 | + (void)referenceReplacedKeyWhenCreatingKeyValues:(BOOL)reference 26 | { 27 | objc_setAssociatedObject(self, &MJReferenceReplacedKeyWhenCreatingKeyValuesKey, @(reference), OBJC_ASSOCIATION_ASSIGN); 28 | } 29 | 30 | + (BOOL)isReferenceReplacedKeyWhenCreatingKeyValues 31 | { 32 | __block id value = objc_getAssociatedObject(self, &MJReferenceReplacedKeyWhenCreatingKeyValuesKey); 33 | if (!value) { 34 | [self enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { 35 | value = objc_getAssociatedObject(c, &MJReferenceReplacedKeyWhenCreatingKeyValuesKey); 36 | 37 | if (value) *stop = YES; 38 | }]; 39 | } 40 | return [value boolValue]; 41 | } 42 | 43 | #pragma mark - --常用的对象-- 44 | static NSNumberFormatter *numberFormatter_; 45 | + (void)load 46 | { 47 | numberFormatter_ = [[NSNumberFormatter alloc] init]; 48 | 49 | // 默认设置 50 | [self referenceReplacedKeyWhenCreatingKeyValues:YES]; 51 | } 52 | 53 | #pragma mark - --公共方法-- 54 | #pragma mark - 字典 -> 模型 55 | - (instancetype)setKeyValues:(id)keyValues 56 | { 57 | return [self setKeyValues:keyValues error:nil]; 58 | } 59 | 60 | - (instancetype)setKeyValues:(id)keyValues error:(NSError *__autoreleasing *)error 61 | { 62 | return [self setKeyValues:keyValues context:nil error:error]; 63 | } 64 | 65 | - (instancetype)setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context 66 | { 67 | return [self setKeyValues:keyValues context:context error:nil]; 68 | } 69 | 70 | /** 71 | 核心代码: 72 | */ 73 | - (instancetype)setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context error:(NSError *__autoreleasing *)error 74 | { 75 | // 获得JSON对象 76 | keyValues = [keyValues JSONObject]; 77 | 78 | MJExtensionAssertError([keyValues isKindOfClass:[NSDictionary class]], self, error, @"keyValues参数不是一个字典"); 79 | 80 | Class aClass = [self class]; 81 | NSArray *allowedPropertyNames = [aClass totalAllowedPropertyNames]; 82 | NSArray *ignoredPropertyNames = [aClass totalIgnoredPropertyNames]; 83 | 84 | //通过封装的方法回调一个通过运行时编写的,用于返回属性列表的方法。 85 | [aClass enumerateProperties:^(MJProperty *property, BOOL *stop) { 86 | @try { 87 | // 0.检测是否被忽略 88 | if (allowedPropertyNames.count && ![allowedPropertyNames containsObject:property.name]) return; 89 | if ([ignoredPropertyNames containsObject:property.name]) return; 90 | 91 | // 1.取出属性值 92 | id value; 93 | NSArray *propertyKeyses = [property propertyKeysForClass:aClass]; 94 | for (NSArray *propertyKeys in propertyKeyses) { 95 | value = keyValues; 96 | for (MJPropertyKey *propertyKey in propertyKeys) { 97 | value = [propertyKey valueInObject:value]; 98 | } 99 | if (value) break; 100 | } 101 | 102 | // 值的过滤 103 | value = [aClass getNewValueFromObject:self oldValue:value property:property]; 104 | 105 | // 如果没有值,就直接返回 106 | if (!value || value == [NSNull null]) return; 107 | 108 | // 2.如果是模型属性 109 | MJPropertyType *type = property.type; 110 | Class typeClass = type.typeClass; 111 | Class objectClass = [property objectClassInArrayForClass:[self class]]; 112 | if (!type.isFromFoundation && typeClass) { 113 | value = [typeClass objectWithKeyValues:value context:context error:error]; 114 | } else if (objectClass) { 115 | // string array -> url array 116 | if (objectClass == [NSURL class] && [value isKindOfClass:[NSArray class]]) { 117 | NSMutableArray *urlArray = [NSMutableArray array]; 118 | for (NSString *string in value) { 119 | if (![string isKindOfClass:[NSString class]]) continue; 120 | [urlArray addObject:string.url]; 121 | } 122 | value = urlArray; 123 | } else { 124 | // 3.字典数组-->模型数组 125 | value = [objectClass objectArrayWithKeyValuesArray:value context:context error:error]; 126 | } 127 | } else if (typeClass == [NSString class]) { 128 | if ([value isKindOfClass:[NSNumber class]]) { 129 | // NSNumber -> NSString 130 | value = [value description]; 131 | } else if ([value isKindOfClass:[NSURL class]]) { 132 | // NSURL -> NSString 133 | value = [value absoluteString]; 134 | } 135 | } else if ([value isKindOfClass:[NSString class]]) { 136 | if (typeClass == [NSURL class]) { 137 | // NSString -> NSURL 138 | // 字符串转码 139 | value = [value url]; 140 | } else if (type.isNumberType) { 141 | NSString *oldValue = value; 142 | 143 | // NSString -> NSNumber 144 | value = [numberFormatter_ numberFromString:oldValue]; 145 | 146 | // 如果是BOOL 147 | if (type.isBoolType) { 148 | // 字符串转BOOL(字符串没有charValue方法) 149 | // 系统会调用字符串的charValue转为BOOL类型 150 | NSString *lower = [oldValue lowercaseString]; 151 | if ([lower isEqualToString:@"yes"] || [lower isEqualToString:@"true"]) { 152 | value = @YES; 153 | } else if ([lower isEqualToString:@"no"] || [lower isEqualToString:@"false"]) { 154 | value = @NO; 155 | } 156 | } 157 | } 158 | } 159 | 160 | // 4.赋值 161 | [property setValue:value forObject:self]; 162 | } @catch (NSException *exception) { 163 | MJExtensionBuildError(error, exception.reason); 164 | NSLog(@"%@", exception); 165 | } 166 | }]; 167 | 168 | // 转换完毕 169 | if ([self respondsToSelector:@selector(keyValuesDidFinishConvertingToObject)]) { 170 | [self keyValuesDidFinishConvertingToObject]; 171 | } 172 | return self; 173 | } 174 | 175 | + (instancetype)objectWithKeyValues:(id)keyValues 176 | { 177 | return [self objectWithKeyValues:keyValues error:nil]; 178 | } 179 | 180 | + (instancetype)objectWithKeyValues:(id)keyValues error:(NSError *__autoreleasing *)error 181 | { 182 | return [self objectWithKeyValues:keyValues context:nil error:error]; 183 | } 184 | 185 | + (instancetype)objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context 186 | { 187 | return [self objectWithKeyValues:keyValues context:context error:nil]; 188 | } 189 | 190 | + (instancetype)objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context error:(NSError *__autoreleasing *)error 191 | { 192 | if (keyValues == nil) return nil; 193 | if ([self isSubclassOfClass:[NSManagedObject class]] && context) { 194 | return [[NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass(self) inManagedObjectContext:context] setKeyValues:keyValues context:context error:error]; 195 | } 196 | return [[[self alloc] init] setKeyValues:keyValues error:error]; 197 | } 198 | 199 | + (instancetype)objectWithFilename:(NSString *)filename 200 | { 201 | return [self objectWithFilename:filename error:nil]; 202 | } 203 | 204 | + (instancetype)objectWithFilename:(NSString *)filename error:(NSError *__autoreleasing *)error 205 | { 206 | MJExtensionAssertError(filename != nil, nil, error, @"filename参数为nil"); 207 | 208 | return [self objectWithFile:[[NSBundle mainBundle] pathForResource:filename ofType:nil] error:error]; 209 | } 210 | 211 | + (instancetype)objectWithFile:(NSString *)file 212 | { 213 | return [self objectWithFile:file error:nil]; 214 | } 215 | 216 | + (instancetype)objectWithFile:(NSString *)file error:(NSError *__autoreleasing *)error 217 | { 218 | MJExtensionAssertError(file != nil, nil, error, @"file参数为nil"); 219 | 220 | return [self objectWithKeyValues:[NSDictionary dictionaryWithContentsOfFile:file] error:error]; 221 | } 222 | 223 | #pragma mark - 字典数组 -> 模型数组 224 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(NSArray *)keyValuesArray 225 | { 226 | return [self objectArrayWithKeyValuesArray:keyValuesArray error:nil]; 227 | } 228 | 229 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(NSArray *)keyValuesArray error:(NSError *__autoreleasing *)error 230 | { 231 | return [self objectArrayWithKeyValuesArray:keyValuesArray context:nil error:error]; 232 | } 233 | 234 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context 235 | { 236 | return [self objectArrayWithKeyValuesArray:keyValuesArray context:context error:nil]; 237 | } 238 | 239 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context error:(NSError *__autoreleasing *)error 240 | { 241 | // 如果数组里面放的是NSString、NSNumber等数据 242 | if ([MJFoundation isClassFromFoundation:self]) return [NSMutableArray arrayWithArray:keyValuesArray]; 243 | 244 | // 如果是JSON字符串 245 | keyValuesArray = [keyValuesArray JSONObject]; 246 | 247 | // 1.判断真实性 248 | MJExtensionAssertError([keyValuesArray isKindOfClass:[NSArray class]], nil, error, @"keyValuesArray参数不是一个数组"); 249 | 250 | // 2.创建数组 251 | NSMutableArray *modelArray = [NSMutableArray array]; 252 | 253 | // 3.遍历 254 | for (NSDictionary *keyValues in keyValuesArray) { 255 | if ([keyValues isKindOfClass:[NSArray class]]){ 256 | [modelArray addObject:[self objectArrayWithKeyValuesArray:keyValues context:context error:error]]; 257 | } else { 258 | id model = [self objectWithKeyValues:keyValues context:context error:error]; 259 | if (model) [modelArray addObject:model]; 260 | } 261 | } 262 | 263 | return modelArray; 264 | } 265 | 266 | + (NSMutableArray *)objectArrayWithFilename:(NSString *)filename 267 | { 268 | return [self objectArrayWithFilename:filename error:nil]; 269 | } 270 | 271 | + (NSMutableArray *)objectArrayWithFilename:(NSString *)filename error:(NSError *__autoreleasing *)error 272 | { 273 | MJExtensionAssertError(filename != nil, nil, error, @"filename参数为nil"); 274 | 275 | return [self objectArrayWithFile:[[NSBundle mainBundle] pathForResource:filename ofType:nil] error:error]; 276 | } 277 | 278 | + (NSMutableArray *)objectArrayWithFile:(NSString *)file 279 | { 280 | return [self objectArrayWithFile:file error:nil]; 281 | } 282 | 283 | + (NSMutableArray *)objectArrayWithFile:(NSString *)file error:(NSError *__autoreleasing *)error 284 | { 285 | MJExtensionAssertError(file != nil, nil, error, @"file参数为nil"); 286 | 287 | return [self objectArrayWithKeyValuesArray:[NSArray arrayWithContentsOfFile:file] error:error]; 288 | } 289 | 290 | #pragma mark - 模型 -> 字典 291 | - (NSMutableDictionary *)keyValues 292 | { 293 | return [self keyValuesWithError:nil]; 294 | } 295 | 296 | - (NSMutableDictionary *)keyValuesWithError:(NSError *__autoreleasing *)error 297 | { 298 | return [self keyValuesWithIgnoredKeys:nil error:error]; 299 | } 300 | 301 | - (NSMutableDictionary *)keyValuesWithKeys:(NSArray *)keys 302 | { 303 | return [self keyValuesWithKeys:keys error:nil]; 304 | } 305 | 306 | - (NSMutableDictionary *)keyValuesWithKeys:(NSArray *)keys error:(NSError *__autoreleasing *)error 307 | { 308 | return [self keyValuesWithKeys:keys ignoredKeys:nil error:error]; 309 | } 310 | 311 | - (NSMutableDictionary *)keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys 312 | { 313 | return [self keyValuesWithIgnoredKeys:ignoredKeys error:nil]; 314 | } 315 | 316 | - (NSMutableDictionary *)keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys error:(NSError *__autoreleasing *)error 317 | { 318 | return [self keyValuesWithKeys:nil ignoredKeys:ignoredKeys error:error]; 319 | } 320 | 321 | - (NSMutableDictionary *)keyValuesWithKeys:(NSArray *)keys ignoredKeys:(NSArray *)ignoredKeys error:(NSError *__autoreleasing *)error 322 | { 323 | // 如果自己不是模型类 324 | if ([MJFoundation isClassFromFoundation:[self class]]) return (NSMutableDictionary *)self; 325 | 326 | id keyValues = [NSMutableDictionary dictionary]; 327 | 328 | Class aClass = [self class]; 329 | NSArray *allowedPropertyNames = [aClass totalAllowedPropertyNames]; 330 | NSArray *ignoredPropertyNames = [aClass totalIgnoredPropertyNames]; 331 | 332 | [aClass enumerateProperties:^(MJProperty *property, BOOL *stop) { 333 | @try { 334 | // 0.检测是否被忽略 335 | if (allowedPropertyNames.count && ![allowedPropertyNames containsObject:property.name]) return; 336 | if ([ignoredPropertyNames containsObject:property.name]) return; 337 | if (keys.count && ![keys containsObject:property.name]) return; 338 | if ([ignoredKeys containsObject:property.name]) return; 339 | 340 | // 1.取出属性值 341 | id value = [property valueForObject:self]; 342 | if (!value) return; 343 | 344 | // 2.如果是模型属性 345 | MJPropertyType *type = property.type; 346 | Class typeClass = type.typeClass; 347 | if (!type.isFromFoundation && typeClass) { 348 | value = [value keyValues]; 349 | } else if ([value isKindOfClass:[NSArray class]]) { 350 | // 3.处理数组里面有模型的情况 351 | value = [NSObject keyValuesArrayWithObjectArray:value]; 352 | } else if (typeClass == [NSURL class]) { 353 | value = [value absoluteString]; 354 | } 355 | 356 | // 4.赋值 357 | if ([aClass isReferenceReplacedKeyWhenCreatingKeyValues]) { 358 | NSArray *propertyKeys = [[property propertyKeysForClass:aClass] firstObject]; 359 | NSUInteger keyCount = propertyKeys.count; 360 | // 创建字典 361 | __block id innerContainer = keyValues; 362 | [propertyKeys enumerateObjectsUsingBlock:^(MJPropertyKey *propertyKey, NSUInteger idx, BOOL *stop) { 363 | // 下一个属性 364 | MJPropertyKey *nextPropertyKey = nil; 365 | if (idx != keyCount - 1) { 366 | nextPropertyKey = propertyKeys[idx + 1]; 367 | } 368 | 369 | if (nextPropertyKey) { // 不是最后一个key 370 | // 当前propertyKey对应的字典或者数组 371 | id tempInnerContainer = [propertyKey valueInObject:innerContainer]; 372 | if (tempInnerContainer == nil || [tempInnerContainer isKindOfClass:[NSNull class]]) { 373 | if (nextPropertyKey.type == MJPropertyKeyTypeDictionary) { 374 | tempInnerContainer = [NSMutableDictionary dictionary]; 375 | } else { 376 | tempInnerContainer = [NSMutableArray array]; 377 | } 378 | if (propertyKey.type == MJPropertyKeyTypeDictionary) { 379 | innerContainer[propertyKey.name] = tempInnerContainer; 380 | } else { 381 | innerContainer[propertyKey.name.intValue] = tempInnerContainer; 382 | } 383 | } 384 | 385 | if ([tempInnerContainer isKindOfClass:[NSMutableArray class]]) { 386 | int index = nextPropertyKey.name.intValue; 387 | while ([tempInnerContainer count] < index + 1) { 388 | [tempInnerContainer addObject:[NSNull null]]; 389 | } 390 | } 391 | 392 | innerContainer = tempInnerContainer; 393 | } else { // 最后一个key 394 | if (propertyKey.type == MJPropertyKeyTypeDictionary) { 395 | innerContainer[propertyKey.name] = value; 396 | } else { 397 | innerContainer[propertyKey.name.intValue] = value; 398 | } 399 | } 400 | }]; 401 | } else { 402 | keyValues[property.name] = value; 403 | } 404 | } @catch (NSException *exception) { 405 | MJExtensionBuildError(error, exception.reason); 406 | NSLog(@"%@", exception); 407 | } 408 | }]; 409 | 410 | // 转换完毕 411 | if ([self respondsToSelector:@selector(objectDidFinishConvertingToKeyValues)]) { 412 | [self objectDidFinishConvertingToKeyValues]; 413 | } 414 | 415 | return keyValues; 416 | } 417 | #pragma mark - 模型数组 -> 字典数组 418 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray 419 | { 420 | return [self keyValuesArrayWithObjectArray:objectArray error:nil]; 421 | } 422 | 423 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray error:(NSError *__autoreleasing *)error 424 | { 425 | return [self keyValuesArrayWithObjectArray:objectArray ignoredKeys:nil error:error]; 426 | } 427 | 428 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys 429 | { 430 | return [self keyValuesArrayWithObjectArray:objectArray keys:keys error:nil]; 431 | } 432 | 433 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys 434 | { 435 | return [self keyValuesArrayWithObjectArray:objectArray ignoredKeys:ignoredKeys error:nil]; 436 | } 437 | 438 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys error:(NSError *__autoreleasing *)error 439 | { 440 | return [self keyValuesArrayWithObjectArray:objectArray keys:keys ignoredKeys:nil error:error]; 441 | } 442 | 443 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys error:(NSError *__autoreleasing *)error 444 | { 445 | return [self keyValuesArrayWithObjectArray:objectArray keys:nil ignoredKeys:ignoredKeys error:error]; 446 | } 447 | 448 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys ignoredKeys:(NSArray *)ignoredKeys error:(NSError *__autoreleasing *)error 449 | { 450 | // 0.判断真实性 451 | MJExtensionAssertError([objectArray isKindOfClass:[NSArray class]], nil, error, @"objectArray参数不是一个数组"); 452 | 453 | // 1.创建数组 454 | NSMutableArray *keyValuesArray = [NSMutableArray array]; 455 | for (id object in objectArray) { 456 | if (keys) { 457 | [keyValuesArray addObject:[object keyValuesWithKeys:keys error:error]]; 458 | } else { 459 | [keyValuesArray addObject:[object keyValuesWithIgnoredKeys:ignoredKeys error:error]]; 460 | } 461 | } 462 | return keyValuesArray; 463 | } 464 | 465 | #pragma mark - 转换为JSON 466 | - (NSData *)JSONData 467 | { 468 | if ([self isKindOfClass:[NSString class]]) { 469 | return [((NSString *)self) dataUsingEncoding:NSUTF8StringEncoding]; 470 | } else if ([self isKindOfClass:[NSData class]]) { 471 | return (NSData *)self; 472 | } 473 | 474 | return [NSJSONSerialization dataWithJSONObject:[self JSONObject] options:kNilOptions error:nil]; 475 | } 476 | 477 | - (id)JSONObject 478 | { 479 | if ([self isKindOfClass:[NSString class]]) { 480 | return [NSJSONSerialization JSONObjectWithData:[((NSString *)self) dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:nil]; 481 | } else if ([self isKindOfClass:[NSData class]]) { 482 | return [NSJSONSerialization JSONObjectWithData:(NSData *)self options:kNilOptions error:nil]; 483 | } 484 | 485 | return self.keyValues; 486 | } 487 | 488 | - (NSString *)JSONString 489 | { 490 | if ([self isKindOfClass:[NSString class]]) { 491 | return (NSString *)self; 492 | } else if ([self isKindOfClass:[NSData class]]) { 493 | return [[NSString alloc] initWithData:(NSData *)self encoding:NSUTF8StringEncoding]; 494 | } 495 | 496 | return [[NSString alloc] initWithData:[self JSONData] encoding:NSUTF8StringEncoding]; 497 | } 498 | @end 499 | -------------------------------------------------------------------------------- /fmd数组/Lib/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 | 11 | @class MJProperty; 12 | 13 | /** 14 | * 遍历成员变量用的block 15 | * 16 | * @param property 成员的包装对象 17 | * @param stop YES代表停止遍历,NO代表继续遍历 18 | */ 19 | typedef void (^MJPropertiesEnumeration)(MJProperty *property, BOOL *stop); 20 | 21 | /** 将属性名换为其他key去字典中取值 */ 22 | typedef NSDictionary * (^MJReplacedKeyFromPropertyName)(); 23 | typedef NSString * (^MJReplacedKeyFromPropertyName121)(NSString *propertyName); 24 | /** 数组中需要转换的模型类 */ 25 | typedef NSDictionary * (^MJObjectClassInArray)(); 26 | /** 用于过滤字典中的值 */ 27 | typedef id (^MJNewValueFromOldValue)(id object, id oldValue, MJProperty *property); 28 | 29 | /** 30 | * 成员属性相关的扩展 31 | */ 32 | @interface NSObject (MJProperty) 33 | #pragma mark - 遍历 34 | /** 35 | * 遍历所有的成员 36 | */ 37 | + (void)enumerateProperties:(MJPropertiesEnumeration)enumeration; 38 | 39 | #pragma mark - 新值配置 40 | /** 41 | * 用于过滤字典中的值 42 | * 43 | * @param newValueFormOldValue 用于过滤字典中的值 44 | */ 45 | + (void)setupNewValueFromOldValue:(MJNewValueFromOldValue)newValueFormOldValue; 46 | + (id)getNewValueFromObject:(__weak id)object oldValue:(__weak id)oldValue property:(__weak MJProperty *)property; 47 | 48 | #pragma mark - key配置 49 | /** 50 | * 将属性名换为其他key去字典中取值 51 | * 52 | * @param replacedKeyFromPropertyName 将属性名换为其他key去字典中取值 53 | */ 54 | + (void)setupReplacedKeyFromPropertyName:(MJReplacedKeyFromPropertyName)replacedKeyFromPropertyName; 55 | /** 56 | * 将属性名换为其他key去字典中取值 57 | * 58 | * @param replacedKeyFromPropertyName121 将属性名换为其他key去字典中取值 59 | */ 60 | + (void)setupReplacedKeyFromPropertyName121:(MJReplacedKeyFromPropertyName121)replacedKeyFromPropertyName121; 61 | 62 | #pragma mark - array model class配置 63 | /** 64 | * 数组中需要转换的模型类 65 | * 66 | * @param objectClassInArray 数组中需要转换的模型类 67 | */ 68 | + (void)setupObjectClassInArray:(MJObjectClassInArray)objectClassInArray; 69 | @end -------------------------------------------------------------------------------- /fmd数组/Lib/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 | #import "MJDictionaryCache.h" 17 | 18 | @implementation NSObject (Property) 19 | 20 | static const char MJReplacedKeyFromPropertyNameKey = '\0'; 21 | static const char MJReplacedKeyFromPropertyName121Key = '\0'; 22 | static const char MJNewValueFromOldValueKey = '\0'; 23 | static const char MJObjectClassInArrayKey = '\0'; 24 | 25 | static const char MJCachedPropertiesKey = '\0'; 26 | 27 | #pragma mark - --私有方法-- 28 | + (NSString *)propertyKey:(NSString *)propertyName 29 | { 30 | MJExtensionAssertParamNotNil2(propertyName, nil); 31 | 32 | __block NSString *key = nil; 33 | // 查看有没有需要替换的key 34 | if ([self respondsToSelector:@selector(replacedKeyFromPropertyName121:)]) { 35 | key = [self replacedKeyFromPropertyName121:propertyName]; 36 | } 37 | 38 | // 调用block 39 | if (!key) { 40 | [self enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { 41 | MJReplacedKeyFromPropertyName121 block = objc_getAssociatedObject(c, &MJReplacedKeyFromPropertyName121Key); 42 | if (block) { 43 | key = block(propertyName); 44 | } 45 | if (key) *stop = YES; 46 | }]; 47 | } 48 | 49 | // 查看有没有需要替换的key 50 | if (!key && [self respondsToSelector:@selector(replacedKeyFromPropertyName)]) { 51 | key = [self replacedKeyFromPropertyName][propertyName]; 52 | } 53 | 54 | if (!key) { 55 | [self enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { 56 | NSDictionary *dict = objc_getAssociatedObject(c, &MJReplacedKeyFromPropertyNameKey); 57 | if (dict) { 58 | key = dict[propertyName]; 59 | } 60 | if (key) *stop = YES; 61 | }]; 62 | } 63 | 64 | // 2.用属性名作为key 65 | if (!key) key = propertyName; 66 | 67 | return key; 68 | } 69 | 70 | + (Class)propertyObjectClassInArray:(NSString *)propertyName 71 | { 72 | __block id aClass = nil; 73 | if ([self respondsToSelector:@selector(objectClassInArray)]) { 74 | aClass = [self objectClassInArray][propertyName]; 75 | } 76 | 77 | if (!aClass) { 78 | [self enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { 79 | NSDictionary *dict = objc_getAssociatedObject(c, &MJObjectClassInArrayKey); 80 | if (dict) { 81 | aClass = dict[propertyName]; 82 | } 83 | if (aClass) *stop = YES; 84 | }]; 85 | } 86 | 87 | // 如果是NSString类型 88 | if ([aClass isKindOfClass:[NSString class]]) { 89 | aClass = NSClassFromString(aClass); 90 | } 91 | return aClass; 92 | } 93 | 94 | #pragma mark - --公共方法-- 95 | + (void)enumerateProperties:(MJPropertiesEnumeration)enumeration 96 | { 97 | // 获得成员变量 98 | NSArray *cachedProperties = [self properties]; 99 | 100 | // 遍历成员变量 101 | BOOL stop = NO; 102 | for (MJProperty *property in cachedProperties) { 103 | enumeration(property, &stop); 104 | if (stop) break; 105 | } 106 | } 107 | 108 | #pragma mark - 公共方法 109 | + (NSMutableArray *)properties 110 | { 111 | // 获得成员变量 112 | // 通过关联对象,以及提前定义好的MJCachedPropertiesKey来进行运行时,对所有属性的获取。 113 | 114 | //***objc_getAssociatedObject 方法用于判断当前是否已经获取过MJCachedPropertiesKey对应的关联对象 115 | // 1> 关联到的对象 116 | // 2> 关联的属性 key 117 | NSMutableArray *cachedProperties = [MJDictionaryCache objectForKey:NSStringFromClass(self) forDictId:&MJCachedPropertiesKey]; 118 | 119 | //*** 120 | if (cachedProperties == nil) { 121 | cachedProperties = [NSMutableArray array]; 122 | 123 | /** 遍历这个类的所有类()不包括NSObject这些基础类 */ 124 | [self enumerateClasses:^(__unsafe_unretained Class c, BOOL *stop) { 125 | // 1.获得所有的成员变量 126 | unsigned int outCount = 0; 127 | /** 128 | class_copyIvarList 成员变量,提示有很多第三方框架会使用 Ivar,能够获得更多的信息 129 | 但是:在 swift 中,由于语法结构的变化,使用 Ivar 非常不稳定,经常会崩溃! 130 | class_copyPropertyList 属性 131 | class_copyMethodList 方法 132 | class_copyProtocolList 协议 133 | */ 134 | objc_property_t *properties = class_copyPropertyList(c, &outCount); 135 | 136 | // 2.遍历每一个成员变量 137 | for (unsigned int i = 0; i 10 | 11 | @interface NSString (MJExtension) 12 | /** 13 | * 驼峰转下划线(loveYou -> love_you) 14 | */ 15 | - (NSString *)underlineFromCamel; 16 | /** 17 | * 下划线转驼峰(love_you -> loveYou) 18 | */ 19 | - (NSString *)camelFromUnderline; 20 | /** 21 | * 首字母变大写 22 | */ 23 | - (NSString *)firstCharUpper; 24 | /** 25 | * 首字母变小写 26 | */ 27 | - (NSString *)firstCharLower; 28 | 29 | - (BOOL)isPureInt; 30 | 31 | - (NSURL *)url; 32 | @end 33 | -------------------------------------------------------------------------------- /fmd数组/Lib/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 *)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 *)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 *)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)isPureInt 66 | { 67 | NSScanner *scan = [NSScanner scannerWithString:self]; 68 | int val; 69 | return [scan scanInt:&val] && [scan isAtEnd]; 70 | } 71 | 72 | - (NSURL *)url 73 | { 74 | return [NSURL URLWithString:(NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)self, (CFStringRef)@"!$&'()*+,-./:;=?@_~%#[]", NULL,kCFStringEncodingUTF8))]; 75 | } 76 | @end 77 | -------------------------------------------------------------------------------- /fmd数组/Lib/Store/MVVMStore.h: -------------------------------------------------------------------------------- 1 | // 2 | // MVVMStore.h 3 | // MVVMFramework 4 | // 5 | // Created by yuantao on 16/1/24. 6 | // Copyright © 2016年 momo. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "HMSingleton.h" 11 | 12 | @interface MVVMStoreItem : NSObject 13 | 14 | @property (copy, nonatomic) NSString *itemId; 15 | @property (strong, nonatomic) id itemObject; 16 | @property (strong, nonatomic) NSDate *createdTime; 17 | 18 | @end 19 | 20 | @interface MVVMStore : NSObject 21 | 22 | HMSingletonH(Store) 23 | 24 | /** 25 | * 根据dbName初始化数据库 26 | */ 27 | - (BOOL)db_initDBWithName:(NSString *)dbName; 28 | 29 | /** 30 | * 根据dbPath初始化数据库 31 | */ 32 | - (BOOL)db_initDBWithPath:(NSString *)dbPath; 33 | 34 | /** 35 | * 根据tableName创建数据表 36 | */ 37 | - (void)db_createTableWithName:(NSString *)tableName; 38 | 39 | /** 40 | * 初始化dbName并根据tableName创建表 41 | */ 42 | - (void)db_initWithDBName:(NSString *)dbName tableName:(NSString *)tableName; 43 | 44 | /** 45 | * 初始化dbPath并根据tableName创建表 46 | */ 47 | - (void)db_initWithDBPath:(NSString *)dbPath tableName:(NSString *)tableName; 48 | 49 | /** 50 | * 清空数据表 51 | */ 52 | - (void)db_clearTable:(NSString *)tableName; 53 | 54 | /** 55 | * 删除表 56 | */ 57 | - (BOOL)db_deleteTable:(NSString *)tableName; 58 | 59 | /** 60 | * 删除数据库 61 | */ 62 | - (void)db_deleteDatabseWithDBName:(NSString *)DBName; 63 | 64 | /** 65 | * 关闭数据库 66 | */ 67 | - (void)db_close; 68 | 69 | /** 70 | * 获得数据库存储路径 71 | */ 72 | - (NSString *)db_getDBPath; 73 | 74 | /** 75 | * tableName是否存在 76 | */ 77 | - (BOOL)db_isExistTableWithName:(NSString *)tableName; 78 | 79 | ///************************ Put&Get methods ***************************************** 80 | 81 | - (void)db_putObject:(id)object withId:(NSString *)objectId intoTable:(NSString *)tableName; 82 | 83 | - (id)db_getObjectById:(NSString *)objectId fromTable:(NSString *)tableName; 84 | 85 | - (MVVMStoreItem *)db_getStoreItemById:(NSString *)objectId fromTable:(NSString *)tableName; 86 | 87 | - (void)db_putString:(NSString *)string withId:(NSString *)stringId intoTable:(NSString *)tableName; 88 | 89 | - (NSString *)db_getStringById:(NSString *)stringId fromTable:(NSString *)tableName; 90 | 91 | - (void)db_putNumber:(NSNumber *)number withId:(NSString *)numberId intoTable:(NSString *)tableName; 92 | 93 | - (NSNumber *)db_getNumberById:(NSString *)numberId fromTable:(NSString *)tableName; 94 | 95 | - (NSArray *)db_getAllItemsFromTable:(NSString *)tableName; 96 | 97 | - (NSArray *)db_getItemsFromTable:(NSString *)tableName withRange:(NSRange)range; 98 | 99 | - (void)db_deleteObjectById:(NSString *)objectId fromTable:(NSString *)tableName; 100 | 101 | - (void)db_deleteObjectsByIdArray:(NSArray *)objectIdArray fromTable:(NSString *)tableName; 102 | 103 | - (void)db_deleteObjectsByIdPrefix:(NSString *)objectIdPrefix fromTable:(NSString *)tableName; 104 | 105 | 106 | @end 107 | -------------------------------------------------------------------------------- /fmd数组/Lib/Store/MVVMStore.m: -------------------------------------------------------------------------------- 1 | // 2 | // MVVMStore.m 3 | // MVVMFramework 4 | // 5 | // Created by yuantao on 16/1/24. 6 | // Copyright © 2016年 momo. All rights reserved. 7 | // 8 | 9 | #import "MVVMStore.h" 10 | #import "YTKKeyValueStore.h" 11 | 12 | @implementation MVVMStoreItem 13 | 14 | - (NSString *)description { 15 | return [NSString stringWithFormat:@"id=%@, value=%@, timeStamp=%@", _itemId, _itemObject, _createdTime]; 16 | } 17 | 18 | @end 19 | 20 | @interface MVVMStore () 21 | @property (nonatomic, strong) YTKKeyValueStore *ytk_store; 22 | @end 23 | 24 | @implementation MVVMStore 25 | 26 | HMSingletonM(Store) 27 | 28 | - (YTKKeyValueStore *)ytk_store { 29 | if (_ytk_store == nil) { 30 | _ytk_store = [[YTKKeyValueStore alloc]init]; 31 | } 32 | return _ytk_store; 33 | } 34 | 35 | - (BOOL)db_initDBWithName:(NSString *)dbName { 36 | return [self.ytk_store initDBWithName:dbName] == nil ? NO : YES; 37 | } 38 | 39 | - (BOOL)db_initDBWithPath:(NSString *)dbPath { 40 | return [self.ytk_store initWithDBWithPath:dbPath] == nil ? NO : YES; 41 | } 42 | 43 | - (void)db_createTableWithName:(NSString *)tableName { 44 | [self.ytk_store createTableWithName:tableName]; 45 | } 46 | 47 | - (void)db_initWithDBName:(NSString *)dbName tableName:(NSString *)tableName { 48 | if ([self db_initDBWithName:dbName]) { 49 | [self.ytk_store createTableWithName:tableName]; 50 | } 51 | } 52 | 53 | - (void)db_initWithDBPath:(NSString *)dbPath tableName:(NSString *)tableName { 54 | if ([self db_initDBWithPath:dbPath]) { 55 | [self.ytk_store createTableWithName:tableName]; 56 | } 57 | } 58 | 59 | - (void)db_clearTable:(NSString *)tableName { 60 | [self.ytk_store clearTable:tableName]; 61 | } 62 | 63 | - (BOOL)db_deleteTable:(NSString *)tableName { 64 | return [self.ytk_store deleteTable:tableName]; 65 | } 66 | 67 | - (void)db_deleteDatabseWithDBName:(NSString *)DBName { 68 | [self.ytk_store deleteDatabseWithDBName:DBName]; 69 | } 70 | 71 | - (void)db_close { 72 | [self.ytk_store close]; 73 | } 74 | 75 | - (NSString *)db_getDBPath { 76 | return [self.ytk_store getDBPath]; 77 | } 78 | 79 | - (BOOL)db_isExistTableWithName:(NSString *)tableName { 80 | return [self.ytk_store isExistTableWithName:tableName]; 81 | } 82 | ///************************ Put&Get methods ***************************************** 83 | 84 | - (void)db_putObject:(id)object withId:(NSString *)objectId intoTable:(NSString *)tableName { 85 | [self.ytk_store putObject:object withId:objectId intoTable:tableName]; 86 | } 87 | 88 | - (id)db_getObjectById:(NSString *)objectId fromTable:(NSString *)tableName { 89 | return [self.ytk_store getObjectById:objectId fromTable:tableName]; 90 | } 91 | 92 | - (MVVMStoreItem *)db_getStoreItemById:(NSString *)objectId fromTable:(NSString *)tableName { 93 | YTKKeyValueItem *item = [self.ytk_store getYTKKeyValueItemById:objectId fromTable:tableName]; 94 | 95 | MVVMStoreItem *storeItem = [[MVVMStoreItem alloc]init]; 96 | storeItem.itemId = item.itemId; 97 | storeItem.itemObject = item.itemObject; 98 | storeItem.createdTime = item.createdTime; 99 | 100 | return storeItem; 101 | } 102 | 103 | - (void)db_putString:(NSString *)string withId:(NSString *)stringId intoTable:(NSString *)tableName { 104 | [self.ytk_store putString:string withId:stringId intoTable:tableName]; 105 | } 106 | 107 | - (NSString *)db_getStringById:(NSString *)stringId fromTable:(NSString *)tableName { 108 | return [self.ytk_store getStringById:stringId fromTable:tableName]; 109 | } 110 | 111 | - (void)db_putNumber:(NSNumber *)number withId:(NSString *)numberId intoTable:(NSString *)tableName { 112 | [self.ytk_store putNumber:number withId:numberId intoTable:tableName]; 113 | } 114 | 115 | - (NSNumber *)db_getNumberById:(NSString *)numberId fromTable:(NSString *)tableName { 116 | return [self.ytk_store getNumberById:numberId fromTable:tableName]; 117 | } 118 | 119 | - (NSArray *)db_getAllItemsFromTable:(NSString *)tableName { 120 | return [self.ytk_store getAllItemsFromTable:tableName]; 121 | } 122 | 123 | - (void)db_deleteObjectById:(NSString *)objectId fromTable:(NSString *)tableName { 124 | [self.ytk_store deleteObjectById:objectId fromTable:tableName]; 125 | } 126 | 127 | - (void)db_deleteObjectsByIdArray:(NSArray *)objectIdArray fromTable:(NSString *)tableName { 128 | [self.ytk_store deleteObjectsByIdArray:objectIdArray fromTable:tableName]; 129 | } 130 | 131 | - (void)db_deleteObjectsByIdPrefix:(NSString *)objectIdPrefix fromTable:(NSString *)tableName { 132 | [self.ytk_store deleteObjectsByIdPrefix:objectIdPrefix fromTable:tableName]; 133 | } 134 | 135 | - (NSArray *)db_getItemsFromTable:(NSString *)tableName withRange:(NSRange)range { 136 | return [self.ytk_store getItemsFromTable:tableName withRange:range]; 137 | } 138 | 139 | @end 140 | -------------------------------------------------------------------------------- /fmd数组/Lib/Utils/Foundation+Log.m: -------------------------------------------------------------------------------- 1 | 2 | #import 3 | 4 | @implementation NSDictionary (Log) 5 | 6 | 7 | 8 | - (NSString *)descriptionWithLocale:(id)locale 9 | { 10 | NSMutableString *str = [NSMutableString string]; 11 | [str appendString:@"{\n"]; 12 | 13 | // 遍历字典的所有键值对 14 | [self enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { 15 | [str appendFormat:@"\t%@ = %@,\n", key, obj]; 16 | }]; 17 | [str appendString:@"}"]; 18 | 19 | // 查出最后一个,的范围 20 | NSRange range = [str rangeOfString:@"," options:NSBackwardsSearch]; 21 | if (range.length != 0) { 22 | // 删掉最后一个, 23 | [str deleteCharactersInRange:range]; 24 | } 25 | 26 | return str; 27 | } 28 | @end 29 | 30 | 31 | 32 | @implementation NSArray (Log) 33 | 34 | - (NSString *)descriptionWithLocale:(id)locale 35 | { 36 | NSMutableString *str = [NSMutableString string]; 37 | [str appendString:@"[\n"]; 38 | 39 | // 遍历数组的所有元素 40 | [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 41 | [str appendFormat:@"%@,\n", obj]; 42 | }]; 43 | [str appendString:@"]"]; 44 | 45 | // 查出最后一个,的范围 46 | NSRange range = [str rangeOfString:@"," options:NSBackwardsSearch]; 47 | if (range.length != 0) { 48 | // 删掉最后一个, 49 | [str deleteCharactersInRange:range]; 50 | } 51 | return str; 52 | } 53 | @end 54 | -------------------------------------------------------------------------------- /fmd数组/Lib/Utils/HMSingleton.h: -------------------------------------------------------------------------------- 1 | // .h文件 2 | #define HMSingletonH(name) + (instancetype)shared##name; 3 | 4 | // .m文件 5 | #if __has_feature(objc_arc) 6 | 7 | #define HMSingletonM(name) \ 8 | static id _instace; \ 9 | \ 10 | + (id)allocWithZone:(struct _NSZone *)zone \ 11 | { \ 12 | static dispatch_once_t onceToken; \ 13 | dispatch_once(&onceToken, ^{ \ 14 | _instace = [super allocWithZone:zone]; \ 15 | }); \ 16 | return _instace; \ 17 | } \ 18 | \ 19 | + (instancetype)shared##name \ 20 | { \ 21 | static dispatch_once_t onceToken; \ 22 | dispatch_once(&onceToken, ^{ \ 23 | _instace = [[self alloc] init]; \ 24 | }); \ 25 | return _instace; \ 26 | } \ 27 | \ 28 | - (id)copyWithZone:(NSZone *)zone \ 29 | { \ 30 | return _instace; \ 31 | } 32 | 33 | #else 34 | 35 | #define HMSingletonM(name) \ 36 | static id _instace; \ 37 | \ 38 | + (id)allocWithZone:(struct _NSZone *)zone \ 39 | { \ 40 | static dispatch_once_t onceToken; \ 41 | dispatch_once(&onceToken, ^{ \ 42 | _instace = [super allocWithZone:zone]; \ 43 | }); \ 44 | return _instace; \ 45 | } \ 46 | \ 47 | + (instancetype)shared##name \ 48 | { \ 49 | static dispatch_once_t onceToken; \ 50 | dispatch_once(&onceToken, ^{ \ 51 | _instace = [[self alloc] init]; \ 52 | }); \ 53 | return _instace; \ 54 | } \ 55 | \ 56 | - (id)copyWithZone:(NSZone *)zone \ 57 | { \ 58 | return _instace; \ 59 | } \ 60 | \ 61 | - (oneway void)release { } \ 62 | - (id)retain { return self; } \ 63 | - (NSUInteger)retainCount { return 1;} \ 64 | - (id)autorelease { return self;} 65 | 66 | #endif -------------------------------------------------------------------------------- /fmd数组/Lib/YTKKeyValueStore/YTKKeyValueStore.h: -------------------------------------------------------------------------------- 1 | // 2 | // YTKKeyValueStore.h 3 | // Ape 4 | // 5 | // Created by TangQiao on 12-11-6. 6 | // Copyright (c) 2012年 TangQiao. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YTKKeyValueItem : NSObject 12 | 13 | @property (strong, nonatomic) NSString *itemId; 14 | @property (strong, nonatomic) id itemObject; 15 | @property (strong, nonatomic) NSDate *createdTime; 16 | 17 | @end 18 | 19 | 20 | @interface YTKKeyValueStore : NSObject 21 | 22 | - (id)initDBWithName:(NSString *)dbName; 23 | 24 | - (id)initWithDBWithPath:(NSString *)dbPath; 25 | 26 | - (void)createTableWithName:(NSString *)tableName; 27 | 28 | - (BOOL)isTableExists:(NSString *)tableName; 29 | 30 | - (void)clearTable:(NSString *)tableName; 31 | 32 | - (void)close; 33 | 34 | ///************************ Put&Get methods ***************************************** 35 | 36 | - (void)putObject:(id)object withId:(NSString *)objectId intoTable:(NSString *)tableName; 37 | 38 | - (id)getObjectById:(NSString *)objectId fromTable:(NSString *)tableName; 39 | 40 | - (YTKKeyValueItem *)getYTKKeyValueItemById:(NSString *)objectId fromTable:(NSString *)tableName; 41 | 42 | - (void)putString:(NSString *)string withId:(NSString *)stringId intoTable:(NSString *)tableName; 43 | 44 | - (NSString *)getStringById:(NSString *)stringId fromTable:(NSString *)tableName; 45 | 46 | - (void)putNumber:(NSNumber *)number withId:(NSString *)numberId intoTable:(NSString *)tableName; 47 | 48 | - (NSNumber *)getNumberById:(NSString *)numberId fromTable:(NSString *)tableName; 49 | 50 | - (NSArray *)getAllItemsFromTable:(NSString *)tableName; 51 | 52 | - (void)deleteObjectById:(NSString *)objectId fromTable:(NSString *)tableName; 53 | 54 | - (void)deleteObjectsByIdArray:(NSArray *)objectIdArray fromTable:(NSString *)tableName; 55 | 56 | - (void)deleteObjectsByIdPrefix:(NSString *)objectIdPrefix fromTable:(NSString *)tableName; 57 | 58 | 59 | /** 60 | * ------------------------" 存入 " ----------------------- 61 | */ 62 | //ZM_Add 存入单个模型 63 | - (void)putModelObject:(id)object withId:(NSString *)objectId intoTable:(NSString *)tableName; 64 | 65 | //ZM_Add 存入模型数组:一个Model一个Id 66 | - (void)putModelObjectArray:(NSArray *)objectArray withIdArray:(NSArray *)objectIdArray intoTable:(NSString *)tableName; 67 | 68 | //ZM_Add 存入模型数组:只用一个Id 69 | - (void)putModelObjectArray:(NSArray *)objectArray intoTable:(NSString *)tableName; 70 | 71 | /** 72 | * ------------------------" 取出 "----------------------- 73 | */ 74 | //ZM_Add 根据一个Id取出一个模型对象 75 | - (id)getModelObjectById:(NSString *)objectId className:(Class)className fromTable:(NSString *)tableName; 76 | 77 | //ZM_Add 根据一个表名取出一个数组模型对象 78 | - (id)getModelArrayByclassName:(Class)className fromTable:(NSString *)tableName arrayCount:(int)arrayCount; 79 | 80 | //ZM_Add 81 | - (NSArray *)getItemsFromTable:(NSString *)tableName withRange:(NSRange)range; 82 | 83 | //ZM_Add 表是否存在 84 | - (BOOL)isExistTableWithName:(NSString *)tableName; 85 | 86 | 87 | /** 88 | * --------------------------" 删除 "---------------------- 89 | */ 90 | //ZM_Add 删除表 91 | - (BOOL)deleteTable:(NSString *)tableName; 92 | 93 | //ZM_Add 删除数据库 94 | - (void)deleteDatabseWithDBName:(NSString *)DBName; 95 | 96 | // 获取表库的路径 97 | - (NSString *)getDBPath; 98 | 99 | @end 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /fmd数组/Lib/YTKKeyValueStore/YTKKeyValueStore.m: -------------------------------------------------------------------------------- 1 | // 2 | // YTKKeyValueStore.m 3 | // Ape 4 | // 5 | // Created by TangQiao on 12-11-6. 6 | // Copyright (c) 2012年 TangQiao. All rights reserved. 7 | // 8 | 9 | #import "YTKKeyValueStore.h" 10 | #import "FMDatabase.h" 11 | #import "FMDatabaseAdditions.h" 12 | #import "FMDatabaseQueue.h" 13 | #import "MJExtension.h" 14 | #ifdef DEBUG 15 | #define debugLog(...) NSLog(__VA_ARGS__) 16 | #define debugMethod() NSLog(@"%s", __func__) 17 | #define debugError() NSLog(@"Error at %s Line:%d", __func__, __LINE__) 18 | #else 19 | #define debugLog(...) 20 | #define debugMethod() 21 | #define debugError() 22 | #endif 23 | 24 | #define PATH_OF_DOCUMENT [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] 25 | 26 | @implementation YTKKeyValueItem 27 | 28 | - (NSString *)description { 29 | return [NSString stringWithFormat:@"id=%@, value=%@, timeStamp=%@", _itemId, _itemObject, _createdTime]; 30 | } 31 | 32 | @end 33 | 34 | @interface YTKKeyValueStore() 35 | 36 | @property (strong, nonatomic) FMDatabaseQueue * dbQueue; 37 | @property (nonatomic, copy) NSString *dbPath; 38 | 39 | @end 40 | 41 | @implementation YTKKeyValueStore 42 | 43 | static NSString *const DEFAULT_DB_NAME = @"database.sqlite"; 44 | 45 | static NSString *const CREATE_TABLE_SQL = 46 | @"CREATE TABLE IF NOT EXISTS %@ ( \ 47 | id TEXT NOT NULL, \ 48 | json TEXT NOT NULL, \ 49 | createdTime TEXT NOT NULL, \ 50 | PRIMARY KEY(id)) \ 51 | "; 52 | 53 | static NSString *const UPDATE_ITEM_SQL = @"REPLACE INTO %@ (id, json, createdTime) values (?, ?, ?)"; 54 | 55 | static NSString *const QUERY_ITEM_SQL = @"SELECT json, createdTime from %@ where id = ? Limit 1"; 56 | 57 | static NSString *const SELECT_ALL_SQL = @"SELECT * from %@"; 58 | 59 | static NSString *const CLEAR_ALL_SQL = @"DELETE from %@"; 60 | 61 | static NSString *const DELETE_ITEM_SQL = @"DELETE from %@ where id = ?"; 62 | 63 | static NSString *const DELETE_ITEMS_SQL = @"DELETE from %@ where id in ( %@ )"; 64 | 65 | static NSString *const DELETE_ITEMS_WITH_PREFIX_SQL = @"DELETE from %@ where id like ? "; 66 | 67 | + (BOOL)checkTableName:(NSString *)tableName { 68 | if (tableName == nil || tableName.length == 0 || [tableName rangeOfString:@" "].location != NSNotFound) { 69 | debugLog(@"ERROR, table name: %@ format error.", tableName); 70 | return NO; 71 | } 72 | return YES; 73 | } 74 | 75 | - (id)init { 76 | return [self initDBWithName:DEFAULT_DB_NAME]; 77 | } 78 | 79 | - (id)initDBWithName:(NSString *)dbName { 80 | self = [super init]; 81 | if (self) { 82 | NSString * dbPath = [PATH_OF_DOCUMENT stringByAppendingPathComponent:dbName]; 83 | _dbPath = dbPath; 84 | debugLog(@"dbPath = %@", dbPath); 85 | if (_dbQueue) { 86 | [self close]; 87 | } 88 | _dbQueue = [FMDatabaseQueue databaseQueueWithPath:dbPath]; 89 | } 90 | return self; 91 | } 92 | 93 | - (id)initWithDBWithPath:(NSString *)dbPath { 94 | self = [super init]; 95 | if (self) { 96 | _dbPath = dbPath; 97 | debugLog(@"dbPath = %@", dbPath); 98 | if (_dbQueue) { 99 | [self close]; 100 | } 101 | _dbQueue = [FMDatabaseQueue databaseQueueWithPath:dbPath]; 102 | } 103 | return self; 104 | } 105 | 106 | - (void)createTableWithName:(NSString *)tableName { 107 | if ([YTKKeyValueStore checkTableName:tableName] == NO) { 108 | return; 109 | } 110 | NSString * sql = [NSString stringWithFormat:CREATE_TABLE_SQL, tableName]; 111 | __block BOOL result; 112 | [_dbQueue inDatabase:^(FMDatabase *db) { 113 | result = [db executeUpdate:sql]; 114 | }]; 115 | if (!result) { 116 | debugLog(@"ERROR, failed to create table: %@", tableName); 117 | } 118 | } 119 | 120 | - (BOOL)isTableExists:(NSString *)tableName{ 121 | if ([YTKKeyValueStore checkTableName:tableName] == NO) { 122 | return NO; 123 | } 124 | __block BOOL result; 125 | [_dbQueue inDatabase:^(FMDatabase *db) { 126 | result = [db tableExists:tableName]; 127 | }]; 128 | if (!result) { 129 | debugLog(@"ERROR, table: %@ not exists in current DB", tableName); 130 | } 131 | return result; 132 | } 133 | 134 | - (void)clearTable:(NSString *)tableName { 135 | if ([YTKKeyValueStore checkTableName:tableName] == NO) { 136 | return; 137 | } 138 | NSString * sql = [NSString stringWithFormat:CLEAR_ALL_SQL, tableName]; 139 | __block BOOL result; 140 | [_dbQueue inDatabase:^(FMDatabase *db) { 141 | result = [db executeUpdate:sql]; 142 | }]; 143 | if (!result) { 144 | debugLog(@"ERROR, failed to clear table: %@", tableName); 145 | } 146 | } 147 | 148 | - (void)putObject:(id)object withId:(NSString *)objectId intoTable:(NSString *)tableName { 149 | if ([YTKKeyValueStore checkTableName:tableName] == NO) { 150 | return; 151 | } 152 | NSError * error; 153 | NSData * data = [NSJSONSerialization dataWithJSONObject:object options:0 error:&error]; 154 | if (error) { 155 | debugLog(@"ERROR, faild to get json data"); 156 | return; 157 | } 158 | NSString * jsonString = [[NSString alloc] initWithData:data encoding:(NSUTF8StringEncoding)]; 159 | NSDate * createdTime = [NSDate date]; 160 | NSString * sql = [NSString stringWithFormat:UPDATE_ITEM_SQL, tableName]; 161 | __block BOOL result; 162 | [_dbQueue inDatabase:^(FMDatabase *db) { 163 | result = [db executeUpdate:sql, objectId, jsonString, createdTime]; 164 | }]; 165 | if (!result) { 166 | debugLog(@"ERROR, failed to insert/replace into table: %@", tableName); 167 | } 168 | } 169 | 170 | - (id)getObjectById:(NSString *)objectId fromTable:(NSString *)tableName { 171 | YTKKeyValueItem * item = [self getYTKKeyValueItemById:objectId fromTable:tableName]; 172 | if (item) { 173 | return item.itemObject; 174 | } else { 175 | return nil; 176 | } 177 | } 178 | 179 | - (YTKKeyValueItem *)getYTKKeyValueItemById:(NSString *)objectId fromTable:(NSString *)tableName { 180 | if ([YTKKeyValueStore checkTableName:tableName] == NO) { 181 | return nil; 182 | } 183 | NSString * sql = [NSString stringWithFormat:QUERY_ITEM_SQL, tableName]; 184 | __block NSString * json = nil; 185 | __block NSDate * createdTime = nil; 186 | [_dbQueue inDatabase:^(FMDatabase *db) { 187 | FMResultSet * rs = [db executeQuery:sql, objectId]; 188 | if ([rs next]) { 189 | json = [rs stringForColumn:@"json"]; 190 | createdTime = [rs dateForColumn:@"createdTime"]; 191 | } 192 | [rs close]; 193 | }]; 194 | if (json) { 195 | NSError * error; 196 | id result = [NSJSONSerialization JSONObjectWithData:[json dataUsingEncoding:NSUTF8StringEncoding] 197 | options:(NSJSONReadingAllowFragments) error:&error]; 198 | if (error) { 199 | debugLog(@"ERROR, faild to prase to json"); 200 | return nil; 201 | } 202 | YTKKeyValueItem * item = [[YTKKeyValueItem alloc] init]; 203 | item.itemId = objectId; 204 | item.itemObject = result; 205 | item.createdTime = createdTime; 206 | return item; 207 | } else { 208 | return nil; 209 | } 210 | } 211 | 212 | - (void)putString:(NSString *)string withId:(NSString *)stringId intoTable:(NSString *)tableName { 213 | if (string == nil) { 214 | debugLog(@"error, string is nil"); 215 | return; 216 | } 217 | [self putObject:@[string] withId:stringId intoTable:tableName]; 218 | } 219 | 220 | - (NSString *)getStringById:(NSString *)stringId fromTable:(NSString *)tableName { 221 | NSArray * array = [self getObjectById:stringId fromTable:tableName]; 222 | if (array && [array isKindOfClass:[NSArray class]]) { 223 | return array[0]; 224 | } 225 | return nil; 226 | } 227 | 228 | - (void)putNumber:(NSNumber *)number withId:(NSString *)numberId intoTable:(NSString *)tableName { 229 | if (number == nil) { 230 | debugLog(@"error, number is nil"); 231 | return; 232 | } 233 | [self putObject:@[number] withId:numberId intoTable:tableName]; 234 | } 235 | 236 | - (NSNumber *)getNumberById:(NSString *)numberId fromTable:(NSString *)tableName { 237 | NSArray * array = [self getObjectById:numberId fromTable:tableName]; 238 | if (array && [array isKindOfClass:[NSArray class]]) { 239 | return array[0]; 240 | } 241 | return nil; 242 | } 243 | 244 | - (NSArray *)getAllItemsFromTable:(NSString *)tableName { 245 | if ([YTKKeyValueStore checkTableName:tableName] == NO) { 246 | return nil; 247 | } 248 | NSString * sql = [NSString stringWithFormat:SELECT_ALL_SQL, tableName]; 249 | __block NSMutableArray * result = [NSMutableArray array]; 250 | [_dbQueue inDatabase:^(FMDatabase *db) { 251 | FMResultSet * rs = [db executeQuery:sql]; 252 | while ([rs next]) { 253 | YTKKeyValueItem * item = [[YTKKeyValueItem alloc] init]; 254 | item.itemId = [rs stringForColumn:@"id"]; 255 | item.itemObject = [rs stringForColumn:@"json"]; 256 | item.createdTime = [rs dateForColumn:@"createdTime"]; 257 | [result addObject:item]; 258 | } 259 | [rs close]; 260 | }]; 261 | // parse json string to object 262 | NSError * error; 263 | for (YTKKeyValueItem * item in result) { 264 | error = nil; 265 | id object = [NSJSONSerialization JSONObjectWithData:[item.itemObject dataUsingEncoding:NSUTF8StringEncoding] 266 | options:(NSJSONReadingAllowFragments) error:&error]; 267 | if (error) { 268 | debugLog(@"ERROR, faild to prase to json."); 269 | } else { 270 | item.itemObject = object; 271 | } 272 | } 273 | return result; 274 | } 275 | 276 | - (void)deleteObjectById:(NSString *)objectId fromTable:(NSString *)tableName { 277 | if ([YTKKeyValueStore checkTableName:tableName] == NO) { 278 | return; 279 | } 280 | NSString * sql = [NSString stringWithFormat:DELETE_ITEM_SQL, tableName]; 281 | __block BOOL result; 282 | [_dbQueue inDatabase:^(FMDatabase *db) { 283 | result = [db executeUpdate:sql, objectId]; 284 | }]; 285 | if (!result) { 286 | debugLog(@"ERROR, failed to delete item from table: %@", tableName); 287 | } 288 | } 289 | 290 | - (void)deleteObjectsByIdArray:(NSArray *)objectIdArray fromTable:(NSString *)tableName { 291 | if ([YTKKeyValueStore checkTableName:tableName] == NO) { 292 | return; 293 | } 294 | NSMutableString *stringBuilder = [NSMutableString string]; 295 | for (id objectId in objectIdArray) { 296 | NSString *item = [NSString stringWithFormat:@" '%@' ", objectId]; 297 | if (stringBuilder.length == 0) { 298 | [stringBuilder appendString:item]; 299 | } else { 300 | [stringBuilder appendString:@","]; 301 | [stringBuilder appendString:item]; 302 | } 303 | } 304 | NSString *sql = [NSString stringWithFormat:DELETE_ITEMS_SQL, tableName, stringBuilder]; 305 | __block BOOL result; 306 | [_dbQueue inDatabase:^(FMDatabase *db) { 307 | result = [db executeUpdate:sql]; 308 | }]; 309 | if (!result) { 310 | debugLog(@"ERROR, failed to delete items by ids from table: %@", tableName); 311 | } 312 | } 313 | 314 | - (void)deleteObjectsByIdPrefix:(NSString *)objectIdPrefix fromTable:(NSString *)tableName { 315 | if ([YTKKeyValueStore checkTableName:tableName] == NO) { 316 | return; 317 | } 318 | NSString *sql = [NSString stringWithFormat:DELETE_ITEMS_WITH_PREFIX_SQL, tableName]; 319 | NSString *prefixArgument = [NSString stringWithFormat:@"%@%%", objectIdPrefix]; 320 | __block BOOL result; 321 | [_dbQueue inDatabase:^(FMDatabase *db) { 322 | result = [db executeUpdate:sql, prefixArgument]; 323 | }]; 324 | if (!result) { 325 | debugLog(@"ERROR, failed to delete items by id prefix from table: %@", tableName); 326 | } 327 | } 328 | 329 | - (void)close { 330 | [_dbQueue close]; 331 | _dbQueue = nil; 332 | } 333 | 334 | #pragma mark//=======================" ZM_Add 添加更多可使用方法 "================================= 335 | 336 | //ZM_Add >> 存入单个模型 337 | - (void)putModelObject:(id)object withId:(NSString *)objectId intoTable:(NSString *)tableName 338 | { 339 | NSDictionary *dic = [object keyValues]; // 模型 -> 字典 340 | [self putObject:dic withId:objectId intoTable:tableName]; 341 | } 342 | //ZM_Add >> 存入数组:一个Model一个Id 343 | - (void)putModelObjectArray:(NSArray *)objectArray withIdArray:(NSArray *)objectIdArray intoTable:(NSString *)tableName 344 | { 345 | if ([objectArray count] == 0) { 346 | return; 347 | } 348 | if ([objectIdArray count] != [objectArray count]) { 349 | return; 350 | } 351 | for (int i = 0; i < [objectArray count]; i++) { 352 | [self putModelObject:objectArray[i] withId:objectIdArray[i] intoTable:tableName]; 353 | } 354 | } 355 | //ZM_Add >> 存入数组:只用一个Id 356 | - (void)putModelObjectArray:(NSArray *)objectArray intoTable:(NSString *)tableName 357 | { 358 | if ([objectArray count] == 0) { 359 | return; 360 | } 361 | for (int i = 0; i < [objectArray count]; i++) { 362 | NSString* Id = [NSString stringWithFormat:@"%d",i]; 363 | [self putModelObject:objectArray[i] withId:Id intoTable:tableName]; 364 | } 365 | } 366 | 367 | 368 | //ZM_Add << 根据一个Id取出一个模型对象 369 | - (id)getModelObjectById:(NSString *)objectId className:(Class)className fromTable:(NSString *)tableName 370 | { 371 | NSDictionary *dic = [self getObjectById:objectId fromTable:tableName]; 372 | return [className objectWithKeyValues:dic]; 373 | 374 | } 375 | //ZM_Add << 根据一个表名取出一个数组模型对象 376 | - (id)getModelArrayByclassName:(Class)className fromTable:(NSString *)tableName arrayCount:(int)arrayCount 377 | { 378 | NSMutableArray* objectArray = [[NSMutableArray alloc] init]; 379 | 380 | for (int i = 0; i < arrayCount; i++) { 381 | NSString* objectId = [NSString stringWithFormat:@"%d",i]; 382 | NSDictionary *dic = [self getObjectById:objectId fromTable:tableName]; 383 | 384 | id Model = [className objectWithKeyValues:dic]; 385 | [objectArray addObject:Model]; 386 | } 387 | return objectArray; 388 | 389 | } 390 | //ZM_Add 391 | - (NSArray *)getItemsFromTable:(NSString *)tableName withRange:(NSRange)range { 392 | 393 | if ([YTKKeyValueStore checkTableName:tableName] == NO) { 394 | return nil; 395 | } 396 | 397 | NSString * sql = [NSString stringWithFormat:@"SELECT * FROM %@ LIMIT %lu, %lu",tableName, range.location, range.length]; 398 | __block NSMutableArray * result = [NSMutableArray array]; 399 | [_dbQueue inDatabase:^(FMDatabase *db) { 400 | FMResultSet * rs = [db executeQuery:sql]; 401 | while ([rs next]) { 402 | YTKKeyValueItem * item = [[YTKKeyValueItem alloc] init]; 403 | item.itemId = [rs stringForColumn:@"id"]; 404 | item.itemObject = [rs stringForColumn:@"json"]; 405 | item.createdTime = [rs dateForColumn:@"createdTime"]; 406 | [result addObject:item]; 407 | } 408 | [rs close]; 409 | }]; 410 | // parse json string to object 411 | NSError * error; 412 | for (YTKKeyValueItem * item in result) { 413 | error = nil; 414 | id object = [NSJSONSerialization JSONObjectWithData:[item.itemObject dataUsingEncoding:NSUTF8StringEncoding] 415 | options:(NSJSONReadingAllowFragments) error:&error]; 416 | if (error) { 417 | debugLog(@"ERROR, faild to prase to json."); 418 | } else { 419 | item.itemObject = object; 420 | } 421 | } 422 | return result; 423 | } 424 | //ZM_Add 表是否存在 425 | - (BOOL)isExistTableWithName:(NSString *)tableName 426 | { 427 | __block BOOL result; 428 | [_dbQueue inDatabase:^(FMDatabase *db) { 429 | FMResultSet * rs = [db executeQuery:@"select count(*) as 'count' from sqlite_master where type ='table' and name = ?", tableName]; 430 | while ([rs next]) 431 | { 432 | NSInteger count = [rs intForColumn:@"count"]; 433 | if (0 == count){ 434 | result = NO; 435 | }else{ 436 | result = YES; 437 | } 438 | } 439 | }]; 440 | return result; 441 | } 442 | //ZM_Add 删除表 443 | - (BOOL)deleteTable:(NSString *)tableName 444 | { 445 | __block BOOL result; 446 | [_dbQueue inDatabase:^(FMDatabase *db) { 447 | NSString *sqlstr = [NSString stringWithFormat:@"DROP TABLE %@", tableName]; 448 | if (![db executeUpdate:sqlstr]) 449 | { 450 | debugLog(@"Delete table error!"); 451 | result = NO; 452 | } 453 | result = YES; 454 | }]; 455 | return result; 456 | } 457 | //ZM_Add 删除数据库 458 | - (void)deleteDatabseWithDBName:(NSString *)DBName 459 | { 460 | __block BOOL success; 461 | __block NSError *error; 462 | 463 | NSFileManager *fileManager = [NSFileManager defaultManager]; 464 | 465 | // delete the old db. 466 | if ([fileManager fileExistsAtPath:DBName]) 467 | { 468 | [_dbQueue inDatabase:^(FMDatabase *db) { 469 | [db close]; 470 | success = [fileManager removeItemAtPath:DBName error:&error]; 471 | if (!success) { 472 | NSAssert1(0, @"Failed to delete old database file with message '%@'.", [error localizedDescription]); 473 | } 474 | }]; 475 | } 476 | } 477 | 478 | // 获取表库的路径 479 | - (NSString *)getDBPath { 480 | return _dbPath; 481 | } 482 | 483 | @end 484 | -------------------------------------------------------------------------------- /fmd数组/Lib/fmdb/FMDB.h: -------------------------------------------------------------------------------- 1 | #import "FMDatabase.h" 2 | #import "FMResultSet.h" 3 | #import "FMDatabaseAdditions.h" 4 | #import "FMDatabaseQueue.h" 5 | #import "FMDatabasePool.h" 6 | -------------------------------------------------------------------------------- /fmd数组/Lib/fmdb/FMDatabaseAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // FMDatabaseAdditions.h 3 | // fmdb 4 | // 5 | // Created by August Mueller on 10/30/05. 6 | // Copyright 2005 Flying Meat Inc.. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "FMDatabase.h" 11 | 12 | 13 | /** Category of additions for `` class. 14 | 15 | ### See also 16 | 17 | - `` 18 | */ 19 | 20 | @interface FMDatabase (FMDatabaseAdditions) 21 | 22 | ///---------------------------------------- 23 | /// @name Return results of SQL to variable 24 | ///---------------------------------------- 25 | 26 | /** Return `int` value for query 27 | 28 | @param query The SQL query to be performed. 29 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. 30 | 31 | @return `int` value. 32 | */ 33 | 34 | - (int)intForQuery:(NSString*)query, ...; 35 | 36 | /** Return `long` value for query 37 | 38 | @param query The SQL query to be performed. 39 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. 40 | 41 | @return `long` value. 42 | */ 43 | 44 | - (long)longForQuery:(NSString*)query, ...; 45 | 46 | /** Return `BOOL` value for query 47 | 48 | @param query The SQL query to be performed. 49 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. 50 | 51 | @return `BOOL` value. 52 | */ 53 | 54 | - (BOOL)boolForQuery:(NSString*)query, ...; 55 | 56 | /** Return `double` value for query 57 | 58 | @param query The SQL query to be performed. 59 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. 60 | 61 | @return `double` value. 62 | */ 63 | 64 | - (double)doubleForQuery:(NSString*)query, ...; 65 | 66 | /** Return `NSString` value for query 67 | 68 | @param query The SQL query to be performed. 69 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. 70 | 71 | @return `NSString` value. 72 | */ 73 | 74 | - (NSString*)stringForQuery:(NSString*)query, ...; 75 | 76 | /** Return `NSData` value for query 77 | 78 | @param query The SQL query to be performed. 79 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. 80 | 81 | @return `NSData` value. 82 | */ 83 | 84 | - (NSData*)dataForQuery:(NSString*)query, ...; 85 | 86 | /** Return `NSDate` value for query 87 | 88 | @param query The SQL query to be performed. 89 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. 90 | 91 | @return `NSDate` value. 92 | */ 93 | 94 | - (NSDate*)dateForQuery:(NSString*)query, ...; 95 | 96 | 97 | // Notice that there's no dataNoCopyForQuery:. 98 | // That would be a bad idea, because we close out the result set, and then what 99 | // happens to the data that we just didn't copy? Who knows, not I. 100 | 101 | 102 | ///-------------------------------- 103 | /// @name Schema related operations 104 | ///-------------------------------- 105 | 106 | /** Does table exist in database? 107 | 108 | @param tableName The name of the table being looked for. 109 | 110 | @return `YES` if table found; `NO` if not found. 111 | */ 112 | 113 | - (BOOL)tableExists:(NSString*)tableName; 114 | 115 | /** The schema of the database. 116 | 117 | This will be the schema for the entire database. For each entity, each row of the result set will include the following fields: 118 | 119 | - `type` - The type of entity (e.g. table, index, view, or trigger) 120 | - `name` - The name of the object 121 | - `tbl_name` - The name of the table to which the object references 122 | - `rootpage` - The page number of the root b-tree page for tables and indices 123 | - `sql` - The SQL that created the entity 124 | 125 | @return `FMResultSet` of schema; `nil` on error. 126 | 127 | @see [SQLite File Format](http://www.sqlite.org/fileformat.html) 128 | */ 129 | 130 | - (FMResultSet*)getSchema; 131 | 132 | /** The schema of the database. 133 | 134 | This will be the schema for a particular table as report by SQLite `PRAGMA`, for example: 135 | 136 | PRAGMA table_info('employees') 137 | 138 | This will report: 139 | 140 | - `cid` - The column ID number 141 | - `name` - The name of the column 142 | - `type` - The data type specified for the column 143 | - `notnull` - whether the field is defined as NOT NULL (i.e. values required) 144 | - `dflt_value` - The default value for the column 145 | - `pk` - Whether the field is part of the primary key of the table 146 | 147 | @param tableName The name of the table for whom the schema will be returned. 148 | 149 | @return `FMResultSet` of schema; `nil` on error. 150 | 151 | @see [table_info](http://www.sqlite.org/pragma.html#pragma_table_info) 152 | */ 153 | 154 | - (FMResultSet*)getTableSchema:(NSString*)tableName; 155 | 156 | /** Test to see if particular column exists for particular table in database 157 | 158 | @param columnName The name of the column. 159 | 160 | @param tableName The name of the table. 161 | 162 | @return `YES` if column exists in table in question; `NO` otherwise. 163 | */ 164 | 165 | - (BOOL)columnExists:(NSString*)columnName inTableWithName:(NSString*)tableName; 166 | 167 | /** Test to see if particular column exists for particular table in database 168 | 169 | @param columnName The name of the column. 170 | 171 | @param tableName The name of the table. 172 | 173 | @return `YES` if column exists in table in question; `NO` otherwise. 174 | 175 | @see columnExists:inTableWithName: 176 | 177 | @warning Deprecated - use `` instead. 178 | */ 179 | 180 | - (BOOL)columnExists:(NSString*)tableName columnName:(NSString*)columnName __attribute__ ((deprecated)); 181 | 182 | 183 | /** Validate SQL statement 184 | 185 | This validates SQL statement by performing `sqlite3_prepare_v2`, but not returning the results, but instead immediately calling `sqlite3_finalize`. 186 | 187 | @param sql The SQL statement being validated. 188 | 189 | @param error This is a pointer to a `NSError` object that will receive the autoreleased `NSError` object if there was any error. If this is `nil`, no `NSError` result will be returned. 190 | 191 | @return `YES` if validation succeeded without incident; `NO` otherwise. 192 | 193 | */ 194 | 195 | - (BOOL)validateSQL:(NSString*)sql error:(NSError**)error; 196 | 197 | 198 | #if SQLITE_VERSION_NUMBER >= 3007017 199 | 200 | ///----------------------------------- 201 | /// @name Application identifier tasks 202 | ///----------------------------------- 203 | 204 | /** Retrieve application ID 205 | 206 | @return The `uint32_t` numeric value of the application ID. 207 | 208 | @see setApplicationID: 209 | */ 210 | 211 | - (uint32_t)applicationID; 212 | 213 | /** Set the application ID 214 | 215 | @param appID The `uint32_t` numeric value of the application ID. 216 | 217 | @see applicationID 218 | */ 219 | 220 | - (void)setApplicationID:(uint32_t)appID; 221 | 222 | #if TARGET_OS_MAC && !TARGET_OS_IPHONE 223 | /** Retrieve application ID string 224 | 225 | @return The `NSString` value of the application ID. 226 | 227 | @see setApplicationIDString: 228 | */ 229 | 230 | 231 | - (NSString*)applicationIDString; 232 | 233 | /** Set the application ID string 234 | 235 | @param string The `NSString` value of the application ID. 236 | 237 | @see applicationIDString 238 | */ 239 | 240 | - (void)setApplicationIDString:(NSString*)string; 241 | #endif 242 | 243 | #endif 244 | 245 | ///----------------------------------- 246 | /// @name user version identifier tasks 247 | ///----------------------------------- 248 | 249 | /** Retrieve user version 250 | 251 | @return The `uint32_t` numeric value of the user version. 252 | 253 | @see setUserVersion: 254 | */ 255 | 256 | - (uint32_t)userVersion; 257 | 258 | /** Set the user-version 259 | 260 | @param version The `uint32_t` numeric value of the user version. 261 | 262 | @see userVersion 263 | */ 264 | 265 | - (void)setUserVersion:(uint32_t)version; 266 | 267 | @end 268 | -------------------------------------------------------------------------------- /fmd数组/Lib/fmdb/FMDatabaseAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // FMDatabaseAdditions.m 3 | // fmdb 4 | // 5 | // Created by August Mueller on 10/30/05. 6 | // Copyright 2005 Flying Meat Inc.. All rights reserved. 7 | // 8 | 9 | #import "FMDatabase.h" 10 | #import "FMDatabaseAdditions.h" 11 | #import "TargetConditionals.h" 12 | 13 | @interface FMDatabase (PrivateStuff) 14 | - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args; 15 | @end 16 | 17 | @implementation FMDatabase (FMDatabaseAdditions) 18 | 19 | #define RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(type, sel) \ 20 | va_list args; \ 21 | va_start(args, query); \ 22 | FMResultSet *resultSet = [self executeQuery:query withArgumentsInArray:0x00 orDictionary:0x00 orVAList:args]; \ 23 | va_end(args); \ 24 | if (![resultSet next]) { return (type)0; } \ 25 | type ret = [resultSet sel:0]; \ 26 | [resultSet close]; \ 27 | [resultSet setParentDB:nil]; \ 28 | return ret; 29 | 30 | 31 | - (NSString*)stringForQuery:(NSString*)query, ... { 32 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSString *, stringForColumnIndex); 33 | } 34 | 35 | - (int)intForQuery:(NSString*)query, ... { 36 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(int, intForColumnIndex); 37 | } 38 | 39 | - (long)longForQuery:(NSString*)query, ... { 40 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(long, longForColumnIndex); 41 | } 42 | 43 | - (BOOL)boolForQuery:(NSString*)query, ... { 44 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(BOOL, boolForColumnIndex); 45 | } 46 | 47 | - (double)doubleForQuery:(NSString*)query, ... { 48 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(double, doubleForColumnIndex); 49 | } 50 | 51 | - (NSData*)dataForQuery:(NSString*)query, ... { 52 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSData *, dataForColumnIndex); 53 | } 54 | 55 | - (NSDate*)dateForQuery:(NSString*)query, ... { 56 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSDate *, dateForColumnIndex); 57 | } 58 | 59 | 60 | - (BOOL)tableExists:(NSString*)tableName { 61 | 62 | tableName = [tableName lowercaseString]; 63 | 64 | FMResultSet *rs = [self executeQuery:@"select [sql] from sqlite_master where [type] = 'table' and lower(name) = ?", tableName]; 65 | 66 | //if at least one next exists, table exists 67 | BOOL returnBool = [rs next]; 68 | 69 | //close and free object 70 | [rs close]; 71 | 72 | return returnBool; 73 | } 74 | 75 | /* 76 | get table with list of tables: result colums: type[STRING], name[STRING],tbl_name[STRING],rootpage[INTEGER],sql[STRING] 77 | check if table exist in database (patch from OZLB) 78 | */ 79 | - (FMResultSet*)getSchema { 80 | 81 | //result colums: type[STRING], name[STRING],tbl_name[STRING],rootpage[INTEGER],sql[STRING] 82 | FMResultSet *rs = [self executeQuery:@"SELECT type, name, tbl_name, rootpage, sql FROM (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type != 'meta' AND name NOT LIKE 'sqlite_%' ORDER BY tbl_name, type DESC, name"]; 83 | 84 | return rs; 85 | } 86 | 87 | /* 88 | get table schema: result colums: cid[INTEGER], name,type [STRING], notnull[INTEGER], dflt_value[],pk[INTEGER] 89 | */ 90 | - (FMResultSet*)getTableSchema:(NSString*)tableName { 91 | 92 | //result colums: cid[INTEGER], name,type [STRING], notnull[INTEGER], dflt_value[],pk[INTEGER] 93 | FMResultSet *rs = [self executeQuery:[NSString stringWithFormat: @"pragma table_info('%@')", tableName]]; 94 | 95 | return rs; 96 | } 97 | 98 | - (BOOL)columnExists:(NSString*)columnName inTableWithName:(NSString*)tableName { 99 | 100 | BOOL returnBool = NO; 101 | 102 | tableName = [tableName lowercaseString]; 103 | columnName = [columnName lowercaseString]; 104 | 105 | FMResultSet *rs = [self getTableSchema:tableName]; 106 | 107 | //check if column is present in table schema 108 | while ([rs next]) { 109 | if ([[[rs stringForColumn:@"name"] lowercaseString] isEqualToString:columnName]) { 110 | returnBool = YES; 111 | break; 112 | } 113 | } 114 | 115 | //If this is not done FMDatabase instance stays out of pool 116 | [rs close]; 117 | 118 | return returnBool; 119 | } 120 | 121 | 122 | #if SQLITE_VERSION_NUMBER >= 3007017 123 | 124 | - (uint32_t)applicationID { 125 | 126 | uint32_t r = 0; 127 | 128 | FMResultSet *rs = [self executeQuery:@"pragma application_id"]; 129 | 130 | if ([rs next]) { 131 | r = (uint32_t)[rs longLongIntForColumnIndex:0]; 132 | } 133 | 134 | [rs close]; 135 | 136 | return r; 137 | } 138 | 139 | - (void)setApplicationID:(uint32_t)appID { 140 | NSString *query = [NSString stringWithFormat:@"pragma application_id=%d", appID]; 141 | FMResultSet *rs = [self executeQuery:query]; 142 | [rs next]; 143 | [rs close]; 144 | } 145 | 146 | 147 | #if TARGET_OS_MAC && !TARGET_OS_IPHONE 148 | - (NSString*)applicationIDString { 149 | NSString *s = NSFileTypeForHFSTypeCode([self applicationID]); 150 | 151 | assert([s length] == 6); 152 | 153 | s = [s substringWithRange:NSMakeRange(1, 4)]; 154 | 155 | 156 | return s; 157 | 158 | } 159 | 160 | - (void)setApplicationIDString:(NSString*)s { 161 | 162 | if ([s length] != 4) { 163 | NSLog(@"setApplicationIDString: string passed is not exactly 4 chars long. (was %ld)", [s length]); 164 | } 165 | 166 | [self setApplicationID:NSHFSTypeCodeFromFileType([NSString stringWithFormat:@"'%@'", s])]; 167 | } 168 | 169 | 170 | #endif 171 | 172 | #endif 173 | 174 | - (uint32_t)userVersion { 175 | uint32_t r = 0; 176 | 177 | FMResultSet *rs = [self executeQuery:@"pragma user_version"]; 178 | 179 | if ([rs next]) { 180 | r = (uint32_t)[rs longLongIntForColumnIndex:0]; 181 | } 182 | 183 | [rs close]; 184 | return r; 185 | } 186 | 187 | - (void)setUserVersion:(uint32_t)version { 188 | NSString *query = [NSString stringWithFormat:@"pragma user_version = %d", version]; 189 | FMResultSet *rs = [self executeQuery:query]; 190 | [rs next]; 191 | [rs close]; 192 | } 193 | 194 | #pragma clang diagnostic push 195 | #pragma clang diagnostic ignored "-Wdeprecated-implementations" 196 | 197 | - (BOOL)columnExists:(NSString*)tableName columnName:(NSString*)columnName __attribute__ ((deprecated)) { 198 | return [self columnExists:columnName inTableWithName:tableName]; 199 | } 200 | 201 | #pragma clang diagnostic pop 202 | 203 | 204 | - (BOOL)validateSQL:(NSString*)sql error:(NSError**)error { 205 | sqlite3_stmt *pStmt = NULL; 206 | BOOL validationSucceeded = YES; 207 | 208 | int rc = sqlite3_prepare_v2(_db, [sql UTF8String], -1, &pStmt, 0); 209 | if (rc != SQLITE_OK) { 210 | validationSucceeded = NO; 211 | if (error) { 212 | *error = [NSError errorWithDomain:NSCocoaErrorDomain 213 | code:[self lastErrorCode] 214 | userInfo:[NSDictionary dictionaryWithObject:[self lastErrorMessage] 215 | forKey:NSLocalizedDescriptionKey]]; 216 | } 217 | } 218 | 219 | sqlite3_finalize(pStmt); 220 | 221 | return validationSucceeded; 222 | } 223 | 224 | @end 225 | -------------------------------------------------------------------------------- /fmd数组/Lib/fmdb/FMDatabasePool.h: -------------------------------------------------------------------------------- 1 | // 2 | // FMDatabasePool.h 3 | // fmdb 4 | // 5 | // Created by August Mueller on 6/22/11. 6 | // Copyright 2011 Flying Meat Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "sqlite3.h" 11 | 12 | @class FMDatabase; 13 | 14 | /** Pool of `` objects. 15 | 16 | ### See also 17 | 18 | - `` 19 | - `` 20 | 21 | @warning Before using `FMDatabasePool`, please consider using `` instead. 22 | 23 | If you really really really know what you're doing and `FMDatabasePool` is what 24 | you really really need (ie, you're using a read only database), OK you can use 25 | it. But just be careful not to deadlock! 26 | 27 | For an example on deadlocking, search for: 28 | `ONLY_USE_THE_POOL_IF_YOU_ARE_DOING_READS_OTHERWISE_YOULL_DEADLOCK_USE_FMDATABASEQUEUE_INSTEAD` 29 | in the main.m file. 30 | */ 31 | 32 | @interface FMDatabasePool : NSObject { 33 | NSString *_path; 34 | 35 | dispatch_queue_t _lockQueue; 36 | 37 | NSMutableArray *_databaseInPool; 38 | NSMutableArray *_databaseOutPool; 39 | 40 | __unsafe_unretained id _delegate; 41 | 42 | NSUInteger _maximumNumberOfDatabasesToCreate; 43 | int _openFlags; 44 | } 45 | 46 | /** Database path */ 47 | 48 | @property (atomic, retain) NSString *path; 49 | 50 | /** Delegate object */ 51 | 52 | @property (atomic, assign) id delegate; 53 | 54 | /** Maximum number of databases to create */ 55 | 56 | @property (atomic, assign) NSUInteger maximumNumberOfDatabasesToCreate; 57 | 58 | /** Open flags */ 59 | 60 | @property (atomic, readonly) int openFlags; 61 | 62 | 63 | ///--------------------- 64 | /// @name Initialization 65 | ///--------------------- 66 | 67 | /** Create pool using path. 68 | 69 | @param aPath The file path of the database. 70 | 71 | @return The `FMDatabasePool` object. `nil` on error. 72 | */ 73 | 74 | + (instancetype)databasePoolWithPath:(NSString*)aPath; 75 | 76 | /** Create pool using path and specified flags 77 | 78 | @param aPath The file path of the database. 79 | @param openFlags Flags passed to the openWithFlags method of the database 80 | 81 | @return The `FMDatabasePool` object. `nil` on error. 82 | */ 83 | 84 | + (instancetype)databasePoolWithPath:(NSString*)aPath flags:(int)openFlags; 85 | 86 | /** Create pool using path. 87 | 88 | @param aPath The file path of the database. 89 | 90 | @return The `FMDatabasePool` object. `nil` on error. 91 | */ 92 | 93 | - (instancetype)initWithPath:(NSString*)aPath; 94 | 95 | /** Create pool using path and specified flags. 96 | 97 | @param aPath The file path of the database. 98 | @param openFlags Flags passed to the openWithFlags method of the database 99 | 100 | @return The `FMDatabasePool` object. `nil` on error. 101 | */ 102 | 103 | - (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags; 104 | 105 | ///------------------------------------------------ 106 | /// @name Keeping track of checked in/out databases 107 | ///------------------------------------------------ 108 | 109 | /** Number of checked-in databases in pool 110 | 111 | @returns Number of databases 112 | */ 113 | 114 | - (NSUInteger)countOfCheckedInDatabases; 115 | 116 | /** Number of checked-out databases in pool 117 | 118 | @returns Number of databases 119 | */ 120 | 121 | - (NSUInteger)countOfCheckedOutDatabases; 122 | 123 | /** Total number of databases in pool 124 | 125 | @returns Number of databases 126 | */ 127 | 128 | - (NSUInteger)countOfOpenDatabases; 129 | 130 | /** Release all databases in pool */ 131 | 132 | - (void)releaseAllDatabases; 133 | 134 | ///------------------------------------------ 135 | /// @name Perform database operations in pool 136 | ///------------------------------------------ 137 | 138 | /** Synchronously perform database operations in pool. 139 | 140 | @param block The code to be run on the `FMDatabasePool` pool. 141 | */ 142 | 143 | - (void)inDatabase:(void (^)(FMDatabase *db))block; 144 | 145 | /** Synchronously perform database operations in pool using transaction. 146 | 147 | @param block The code to be run on the `FMDatabasePool` pool. 148 | */ 149 | 150 | - (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block; 151 | 152 | /** Synchronously perform database operations in pool using deferred transaction. 153 | 154 | @param block The code to be run on the `FMDatabasePool` pool. 155 | */ 156 | 157 | - (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block; 158 | 159 | #if SQLITE_VERSION_NUMBER >= 3007000 160 | 161 | /** Synchronously perform database operations in pool using save point. 162 | 163 | @param block The code to be run on the `FMDatabasePool` pool. 164 | 165 | @return `NSError` object if error; `nil` if successful. 166 | 167 | @warning You can not nest these, since calling it will pull another database out of the pool and you'll get a deadlock. If you need to nest, use `<[FMDatabase startSavePointWithName:error:]>` instead. 168 | */ 169 | 170 | - (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block; 171 | #endif 172 | 173 | @end 174 | 175 | 176 | /** FMDatabasePool delegate category 177 | 178 | This is a category that defines the protocol for the FMDatabasePool delegate 179 | */ 180 | 181 | @interface NSObject (FMDatabasePoolDelegate) 182 | 183 | /** Asks the delegate whether database should be added to the pool. 184 | 185 | @param pool The `FMDatabasePool` object. 186 | @param database The `FMDatabase` object. 187 | 188 | @return `YES` if it should add database to pool; `NO` if not. 189 | 190 | */ 191 | 192 | - (BOOL)databasePool:(FMDatabasePool*)pool shouldAddDatabaseToPool:(FMDatabase*)database; 193 | 194 | /** Tells the delegate that database was added to the pool. 195 | 196 | @param pool The `FMDatabasePool` object. 197 | @param database The `FMDatabase` object. 198 | 199 | */ 200 | 201 | - (void)databasePool:(FMDatabasePool*)pool didAddDatabase:(FMDatabase*)database; 202 | 203 | @end 204 | 205 | -------------------------------------------------------------------------------- /fmd数组/Lib/fmdb/FMDatabasePool.m: -------------------------------------------------------------------------------- 1 | // 2 | // FMDatabasePool.m 3 | // fmdb 4 | // 5 | // Created by August Mueller on 6/22/11. 6 | // Copyright 2011 Flying Meat Inc. All rights reserved. 7 | // 8 | 9 | #import "FMDatabasePool.h" 10 | #import "FMDatabase.h" 11 | 12 | @interface FMDatabasePool() 13 | 14 | - (void)pushDatabaseBackInPool:(FMDatabase*)db; 15 | - (FMDatabase*)db; 16 | 17 | @end 18 | 19 | 20 | @implementation FMDatabasePool 21 | @synthesize path=_path; 22 | @synthesize delegate=_delegate; 23 | @synthesize maximumNumberOfDatabasesToCreate=_maximumNumberOfDatabasesToCreate; 24 | @synthesize openFlags=_openFlags; 25 | 26 | 27 | + (instancetype)databasePoolWithPath:(NSString*)aPath { 28 | return FMDBReturnAutoreleased([[self alloc] initWithPath:aPath]); 29 | } 30 | 31 | + (instancetype)databasePoolWithPath:(NSString*)aPath flags:(int)openFlags { 32 | return FMDBReturnAutoreleased([[self alloc] initWithPath:aPath flags:openFlags]); 33 | } 34 | 35 | - (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags { 36 | 37 | self = [super init]; 38 | 39 | if (self != nil) { 40 | _path = [aPath copy]; 41 | _lockQueue = dispatch_queue_create([[NSString stringWithFormat:@"fmdb.%@", self] UTF8String], NULL); 42 | _databaseInPool = FMDBReturnRetained([NSMutableArray array]); 43 | _databaseOutPool = FMDBReturnRetained([NSMutableArray array]); 44 | _openFlags = openFlags; 45 | } 46 | 47 | return self; 48 | } 49 | 50 | - (instancetype)initWithPath:(NSString*)aPath 51 | { 52 | // default flags for sqlite3_open 53 | return [self initWithPath:aPath flags:SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE]; 54 | } 55 | 56 | - (instancetype)init { 57 | return [self initWithPath:nil]; 58 | } 59 | 60 | 61 | - (void)dealloc { 62 | 63 | _delegate = 0x00; 64 | FMDBRelease(_path); 65 | FMDBRelease(_databaseInPool); 66 | FMDBRelease(_databaseOutPool); 67 | 68 | if (_lockQueue) { 69 | FMDBDispatchQueueRelease(_lockQueue); 70 | _lockQueue = 0x00; 71 | } 72 | #if ! __has_feature(objc_arc) 73 | [super dealloc]; 74 | #endif 75 | } 76 | 77 | 78 | - (void)executeLocked:(void (^)(void))aBlock { 79 | dispatch_sync(_lockQueue, aBlock); 80 | } 81 | 82 | - (void)pushDatabaseBackInPool:(FMDatabase*)db { 83 | 84 | if (!db) { // db can be null if we set an upper bound on the # of databases to create. 85 | return; 86 | } 87 | 88 | [self executeLocked:^() { 89 | 90 | if ([self->_databaseInPool containsObject:db]) { 91 | [[NSException exceptionWithName:@"Database already in pool" reason:@"The FMDatabase being put back into the pool is already present in the pool" userInfo:nil] raise]; 92 | } 93 | 94 | [self->_databaseInPool addObject:db]; 95 | [self->_databaseOutPool removeObject:db]; 96 | 97 | }]; 98 | } 99 | 100 | - (FMDatabase*)db { 101 | 102 | __block FMDatabase *db; 103 | 104 | 105 | [self executeLocked:^() { 106 | db = [self->_databaseInPool lastObject]; 107 | 108 | BOOL shouldNotifyDelegate = NO; 109 | 110 | if (db) { 111 | [self->_databaseOutPool addObject:db]; 112 | [self->_databaseInPool removeLastObject]; 113 | } 114 | else { 115 | 116 | if (self->_maximumNumberOfDatabasesToCreate) { 117 | NSUInteger currentCount = [self->_databaseOutPool count] + [self->_databaseInPool count]; 118 | 119 | if (currentCount >= self->_maximumNumberOfDatabasesToCreate) { 120 | NSLog(@"Maximum number of databases (%ld) has already been reached!", (long)currentCount); 121 | return; 122 | } 123 | } 124 | 125 | db = [FMDatabase databaseWithPath:self->_path]; 126 | shouldNotifyDelegate = YES; 127 | } 128 | 129 | //This ensures that the db is opened before returning 130 | #if SQLITE_VERSION_NUMBER >= 3005000 131 | BOOL success = [db openWithFlags:self->_openFlags]; 132 | #else 133 | BOOL success = [db open]; 134 | #endif 135 | if (success) { 136 | if ([self->_delegate respondsToSelector:@selector(databasePool:shouldAddDatabaseToPool:)] && ![self->_delegate databasePool:self shouldAddDatabaseToPool:db]) { 137 | [db close]; 138 | db = 0x00; 139 | } 140 | else { 141 | //It should not get added in the pool twice if lastObject was found 142 | if (![self->_databaseOutPool containsObject:db]) { 143 | [self->_databaseOutPool addObject:db]; 144 | 145 | if (shouldNotifyDelegate && [self->_delegate respondsToSelector:@selector(databasePool:didAddDatabase:)]) { 146 | [self->_delegate databasePool:self didAddDatabase:db]; 147 | } 148 | } 149 | } 150 | } 151 | else { 152 | NSLog(@"Could not open up the database at path %@", self->_path); 153 | db = 0x00; 154 | } 155 | }]; 156 | 157 | return db; 158 | } 159 | 160 | - (NSUInteger)countOfCheckedInDatabases { 161 | 162 | __block NSUInteger count; 163 | 164 | [self executeLocked:^() { 165 | count = [self->_databaseInPool count]; 166 | }]; 167 | 168 | return count; 169 | } 170 | 171 | - (NSUInteger)countOfCheckedOutDatabases { 172 | 173 | __block NSUInteger count; 174 | 175 | [self executeLocked:^() { 176 | count = [self->_databaseOutPool count]; 177 | }]; 178 | 179 | return count; 180 | } 181 | 182 | - (NSUInteger)countOfOpenDatabases { 183 | __block NSUInteger count; 184 | 185 | [self executeLocked:^() { 186 | count = [self->_databaseOutPool count] + [self->_databaseInPool count]; 187 | }]; 188 | 189 | return count; 190 | } 191 | 192 | - (void)releaseAllDatabases { 193 | [self executeLocked:^() { 194 | [self->_databaseOutPool removeAllObjects]; 195 | [self->_databaseInPool removeAllObjects]; 196 | }]; 197 | } 198 | 199 | - (void)inDatabase:(void (^)(FMDatabase *db))block { 200 | 201 | FMDatabase *db = [self db]; 202 | 203 | block(db); 204 | 205 | [self pushDatabaseBackInPool:db]; 206 | } 207 | 208 | - (void)beginTransaction:(BOOL)useDeferred withBlock:(void (^)(FMDatabase *db, BOOL *rollback))block { 209 | 210 | BOOL shouldRollback = NO; 211 | 212 | FMDatabase *db = [self db]; 213 | 214 | if (useDeferred) { 215 | [db beginDeferredTransaction]; 216 | } 217 | else { 218 | [db beginTransaction]; 219 | } 220 | 221 | 222 | block(db, &shouldRollback); 223 | 224 | if (shouldRollback) { 225 | [db rollback]; 226 | } 227 | else { 228 | [db commit]; 229 | } 230 | 231 | [self pushDatabaseBackInPool:db]; 232 | } 233 | 234 | - (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block { 235 | [self beginTransaction:YES withBlock:block]; 236 | } 237 | 238 | - (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block { 239 | [self beginTransaction:NO withBlock:block]; 240 | } 241 | #if SQLITE_VERSION_NUMBER >= 3007000 242 | - (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block { 243 | 244 | static unsigned long savePointIdx = 0; 245 | 246 | NSString *name = [NSString stringWithFormat:@"savePoint%ld", savePointIdx++]; 247 | 248 | BOOL shouldRollback = NO; 249 | 250 | FMDatabase *db = [self db]; 251 | 252 | NSError *err = 0x00; 253 | 254 | if (![db startSavePointWithName:name error:&err]) { 255 | [self pushDatabaseBackInPool:db]; 256 | return err; 257 | } 258 | 259 | block(db, &shouldRollback); 260 | 261 | if (shouldRollback) { 262 | // We need to rollback and release this savepoint to remove it 263 | [db rollbackToSavePointWithName:name error:&err]; 264 | } 265 | [db releaseSavePointWithName:name error:&err]; 266 | 267 | [self pushDatabaseBackInPool:db]; 268 | 269 | return err; 270 | } 271 | #endif 272 | 273 | @end 274 | -------------------------------------------------------------------------------- /fmd数组/Lib/fmdb/FMDatabaseQueue.h: -------------------------------------------------------------------------------- 1 | // 2 | // FMDatabaseQueue.h 3 | // fmdb 4 | // 5 | // Created by August Mueller on 6/22/11. 6 | // Copyright 2011 Flying Meat Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "sqlite3.h" 11 | 12 | @class FMDatabase; 13 | 14 | /** To perform queries and updates on multiple threads, you'll want to use `FMDatabaseQueue`. 15 | 16 | Using a single instance of `` from multiple threads at once is a bad idea. It has always been OK to make a `` object *per thread*. Just don't share a single instance across threads, and definitely not across multiple threads at the same time. 17 | 18 | Instead, use `FMDatabaseQueue`. Here's how to use it: 19 | 20 | First, make your queue. 21 | 22 | FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath]; 23 | 24 | Then use it like so: 25 | 26 | [queue inDatabase:^(FMDatabase *db) { 27 | [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]]; 28 | [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]]; 29 | [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]]; 30 | 31 | FMResultSet *rs = [db executeQuery:@"select * from foo"]; 32 | while ([rs next]) { 33 | //… 34 | } 35 | }]; 36 | 37 | An easy way to wrap things up in a transaction can be done like this: 38 | 39 | [queue inTransaction:^(FMDatabase *db, BOOL *rollback) { 40 | [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]]; 41 | [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]]; 42 | [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]]; 43 | 44 | if (whoopsSomethingWrongHappened) { 45 | *rollback = YES; 46 | return; 47 | } 48 | // etc… 49 | [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:4]]; 50 | }]; 51 | 52 | `FMDatabaseQueue` will run the blocks on a serialized queue (hence the name of the class). So if you call `FMDatabaseQueue`'s methods from multiple threads at the same time, they will be executed in the order they are received. This way queries and updates won't step on each other's toes, and every one is happy. 53 | 54 | ### See also 55 | 56 | - `` 57 | 58 | @warning Do not instantiate a single `` object and use it across multiple threads. Use `FMDatabaseQueue` instead. 59 | 60 | @warning The calls to `FMDatabaseQueue`'s methods are blocking. So even though you are passing along blocks, they will **not** be run on another thread. 61 | 62 | */ 63 | 64 | @interface FMDatabaseQueue : NSObject { 65 | NSString *_path; 66 | dispatch_queue_t _queue; 67 | FMDatabase *_db; 68 | int _openFlags; 69 | } 70 | 71 | /** Path of database */ 72 | 73 | @property (atomic, retain) NSString *path; 74 | 75 | /** Open flags */ 76 | 77 | @property (atomic, readonly) int openFlags; 78 | 79 | ///---------------------------------------------------- 80 | /// @name Initialization, opening, and closing of queue 81 | ///---------------------------------------------------- 82 | 83 | /** Create queue using path. 84 | 85 | @param aPath The file path of the database. 86 | 87 | @return The `FMDatabaseQueue` object. `nil` on error. 88 | */ 89 | 90 | + (instancetype)databaseQueueWithPath:(NSString*)aPath; 91 | 92 | /** Create queue using path and specified flags. 93 | 94 | @param aPath The file path of the database. 95 | @param openFlags Flags passed to the openWithFlags method of the database 96 | 97 | @return The `FMDatabaseQueue` object. `nil` on error. 98 | */ 99 | + (instancetype)databaseQueueWithPath:(NSString*)aPath flags:(int)openFlags; 100 | 101 | /** Create queue using path. 102 | 103 | @param aPath The file path of the database. 104 | 105 | @return The `FMDatabaseQueue` object. `nil` on error. 106 | */ 107 | 108 | - (instancetype)initWithPath:(NSString*)aPath; 109 | 110 | /** Create queue using path and specified flags. 111 | 112 | @param aPath The file path of the database. 113 | @param openFlags Flags passed to the openWithFlags method of the database 114 | 115 | @return The `FMDatabaseQueue` object. `nil` on error. 116 | */ 117 | 118 | - (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags; 119 | 120 | /** Returns the Class of 'FMDatabase' subclass, that will be used to instantiate database object. 121 | 122 | Subclasses can override this method to return specified Class of 'FMDatabase' subclass. 123 | 124 | @return The Class of 'FMDatabase' subclass, that will be used to instantiate database object. 125 | */ 126 | 127 | + (Class)databaseClass; 128 | 129 | /** Close database used by queue. */ 130 | 131 | - (void)close; 132 | 133 | ///----------------------------------------------- 134 | /// @name Dispatching database operations to queue 135 | ///----------------------------------------------- 136 | 137 | /** Synchronously perform database operations on queue. 138 | 139 | @param block The code to be run on the queue of `FMDatabaseQueue` 140 | */ 141 | 142 | - (void)inDatabase:(void (^)(FMDatabase *db))block; 143 | 144 | /** Synchronously perform database operations on queue, using transactions. 145 | 146 | @param block The code to be run on the queue of `FMDatabaseQueue` 147 | */ 148 | 149 | - (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block; 150 | 151 | /** Synchronously perform database operations on queue, using deferred transactions. 152 | 153 | @param block The code to be run on the queue of `FMDatabaseQueue` 154 | */ 155 | 156 | - (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block; 157 | 158 | ///----------------------------------------------- 159 | /// @name Dispatching database operations to queue 160 | ///----------------------------------------------- 161 | 162 | /** Synchronously perform database operations using save point. 163 | 164 | @param block The code to be run on the queue of `FMDatabaseQueue` 165 | */ 166 | 167 | #if SQLITE_VERSION_NUMBER >= 3007000 168 | // NOTE: you can not nest these, since calling it will pull another database out of the pool and you'll get a deadlock. 169 | // If you need to nest, use FMDatabase's startSavePointWithName:error: instead. 170 | - (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block; 171 | #endif 172 | 173 | @end 174 | 175 | -------------------------------------------------------------------------------- /fmd数组/Lib/fmdb/FMDatabaseQueue.m: -------------------------------------------------------------------------------- 1 | // 2 | // FMDatabaseQueue.m 3 | // fmdb 4 | // 5 | // Created by August Mueller on 6/22/11. 6 | // Copyright 2011 Flying Meat Inc. All rights reserved. 7 | // 8 | 9 | #import "FMDatabaseQueue.h" 10 | #import "FMDatabase.h" 11 | 12 | /* 13 | 14 | Note: we call [self retain]; before using dispatch_sync, just incase 15 | FMDatabaseQueue is released on another thread and we're in the middle of doing 16 | something in dispatch_sync 17 | 18 | */ 19 | 20 | /* 21 | * A key used to associate the FMDatabaseQueue object with the dispatch_queue_t it uses. 22 | * This in turn is used for deadlock detection by seeing if inDatabase: is called on 23 | * the queue's dispatch queue, which should not happen and causes a deadlock. 24 | */ 25 | static const void * const kDispatchQueueSpecificKey = &kDispatchQueueSpecificKey; 26 | 27 | @implementation FMDatabaseQueue 28 | 29 | @synthesize path = _path; 30 | @synthesize openFlags = _openFlags; 31 | 32 | + (instancetype)databaseQueueWithPath:(NSString*)aPath { 33 | 34 | FMDatabaseQueue *q = [[self alloc] initWithPath:aPath]; 35 | 36 | FMDBAutorelease(q); 37 | 38 | return q; 39 | } 40 | 41 | + (instancetype)databaseQueueWithPath:(NSString*)aPath flags:(int)openFlags { 42 | 43 | FMDatabaseQueue *q = [[self alloc] initWithPath:aPath flags:openFlags]; 44 | 45 | FMDBAutorelease(q); 46 | 47 | return q; 48 | } 49 | 50 | + (Class)databaseClass { 51 | return [FMDatabase class]; 52 | } 53 | 54 | - (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags { 55 | 56 | self = [super init]; 57 | 58 | if (self != nil) { 59 | 60 | _db = [[[self class] databaseClass] databaseWithPath:aPath]; 61 | FMDBRetain(_db); 62 | 63 | #if SQLITE_VERSION_NUMBER >= 3005000 64 | BOOL success = [_db openWithFlags:openFlags]; 65 | #else 66 | BOOL success = [_db open]; 67 | #endif 68 | if (!success) { 69 | NSLog(@"Could not create database queue for path %@", aPath); 70 | FMDBRelease(self); 71 | return 0x00; 72 | } 73 | 74 | _path = FMDBReturnRetained(aPath); 75 | 76 | _queue = dispatch_queue_create([[NSString stringWithFormat:@"fmdb.%@", self] UTF8String], NULL); 77 | dispatch_queue_set_specific(_queue, kDispatchQueueSpecificKey, (__bridge void *)self, NULL); 78 | _openFlags = openFlags; 79 | } 80 | 81 | return self; 82 | } 83 | 84 | - (instancetype)initWithPath:(NSString*)aPath { 85 | 86 | // default flags for sqlite3_open 87 | return [self initWithPath:aPath flags:SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE]; 88 | } 89 | 90 | - (instancetype)init { 91 | return [self initWithPath:nil]; 92 | } 93 | 94 | 95 | - (void)dealloc { 96 | 97 | FMDBRelease(_db); 98 | FMDBRelease(_path); 99 | 100 | if (_queue) { 101 | FMDBDispatchQueueRelease(_queue); 102 | _queue = 0x00; 103 | } 104 | #if ! __has_feature(objc_arc) 105 | [super dealloc]; 106 | #endif 107 | } 108 | 109 | - (void)close { 110 | FMDBRetain(self); 111 | dispatch_sync(_queue, ^() { 112 | [self->_db close]; 113 | FMDBRelease(_db); 114 | self->_db = 0x00; 115 | }); 116 | FMDBRelease(self); 117 | } 118 | 119 | - (FMDatabase*)database { 120 | if (!_db) { 121 | _db = FMDBReturnRetained([FMDatabase databaseWithPath:_path]); 122 | 123 | #if SQLITE_VERSION_NUMBER >= 3005000 124 | BOOL success = [_db openWithFlags:_openFlags]; 125 | #else 126 | BOOL success = [db open]; 127 | #endif 128 | if (!success) { 129 | NSLog(@"FMDatabaseQueue could not reopen database for path %@", _path); 130 | FMDBRelease(_db); 131 | _db = 0x00; 132 | return 0x00; 133 | } 134 | } 135 | 136 | return _db; 137 | } 138 | 139 | - (void)inDatabase:(void (^)(FMDatabase *db))block { 140 | /* Get the currently executing queue (which should probably be nil, but in theory could be another DB queue 141 | * and then check it against self to make sure we're not about to deadlock. */ 142 | FMDatabaseQueue *currentSyncQueue = (__bridge id)dispatch_get_specific(kDispatchQueueSpecificKey); 143 | assert(currentSyncQueue != self && "inDatabase: was called reentrantly on the same queue, which would lead to a deadlock"); 144 | 145 | FMDBRetain(self); 146 | 147 | dispatch_sync(_queue, ^() { 148 | 149 | FMDatabase *db = [self database]; 150 | block(db); 151 | 152 | if ([db hasOpenResultSets]) { 153 | NSLog(@"Warning: there is at least one open result set around after performing [FMDatabaseQueue inDatabase:]"); 154 | 155 | #ifdef DEBUG 156 | NSSet *openSetCopy = FMDBReturnAutoreleased([[db valueForKey:@"_openResultSets"] copy]); 157 | for (NSValue *rsInWrappedInATastyValueMeal in openSetCopy) { 158 | FMResultSet *rs = (FMResultSet *)[rsInWrappedInATastyValueMeal pointerValue]; 159 | NSLog(@"query: '%@'", [rs query]); 160 | } 161 | #endif 162 | } 163 | }); 164 | 165 | FMDBRelease(self); 166 | } 167 | 168 | 169 | - (void)beginTransaction:(BOOL)useDeferred withBlock:(void (^)(FMDatabase *db, BOOL *rollback))block { 170 | FMDBRetain(self); 171 | dispatch_sync(_queue, ^() { 172 | 173 | BOOL shouldRollback = NO; 174 | 175 | if (useDeferred) { 176 | [[self database] beginDeferredTransaction]; 177 | } 178 | else { 179 | [[self database] beginTransaction]; 180 | } 181 | 182 | block([self database], &shouldRollback); 183 | 184 | if (shouldRollback) { 185 | [[self database] rollback]; 186 | } 187 | else { 188 | [[self database] commit]; 189 | } 190 | }); 191 | 192 | FMDBRelease(self); 193 | } 194 | 195 | - (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block { 196 | [self beginTransaction:YES withBlock:block]; 197 | } 198 | 199 | - (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block { 200 | [self beginTransaction:NO withBlock:block]; 201 | } 202 | 203 | #if SQLITE_VERSION_NUMBER >= 3007000 204 | - (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block { 205 | 206 | static unsigned long savePointIdx = 0; 207 | __block NSError *err = 0x00; 208 | FMDBRetain(self); 209 | dispatch_sync(_queue, ^() { 210 | 211 | NSString *name = [NSString stringWithFormat:@"savePoint%ld", savePointIdx++]; 212 | 213 | BOOL shouldRollback = NO; 214 | 215 | if ([[self database] startSavePointWithName:name error:&err]) { 216 | 217 | block([self database], &shouldRollback); 218 | 219 | if (shouldRollback) { 220 | // We need to rollback and release this savepoint to remove it 221 | [[self database] rollbackToSavePointWithName:name error:&err]; 222 | } 223 | [[self database] releaseSavePointWithName:name error:&err]; 224 | 225 | } 226 | }); 227 | FMDBRelease(self); 228 | return err; 229 | } 230 | #endif 231 | 232 | @end 233 | -------------------------------------------------------------------------------- /fmd数组/Lib/fmdb/FMResultSet.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "sqlite3.h" 3 | 4 | #ifndef __has_feature // Optional. 5 | #define __has_feature(x) 0 // Compatibility with non-clang compilers. 6 | #endif 7 | 8 | #ifndef NS_RETURNS_NOT_RETAINED 9 | #if __has_feature(attribute_ns_returns_not_retained) 10 | #define NS_RETURNS_NOT_RETAINED __attribute__((ns_returns_not_retained)) 11 | #else 12 | #define NS_RETURNS_NOT_RETAINED 13 | #endif 14 | #endif 15 | 16 | @class FMDatabase; 17 | @class FMStatement; 18 | 19 | /** Represents the results of executing a query on an ``. 20 | 21 | ### See also 22 | 23 | - `` 24 | */ 25 | 26 | @interface FMResultSet : NSObject { 27 | FMDatabase *_parentDB; 28 | FMStatement *_statement; 29 | 30 | NSString *_query; 31 | NSMutableDictionary *_columnNameToIndexMap; 32 | } 33 | 34 | ///----------------- 35 | /// @name Properties 36 | ///----------------- 37 | 38 | /** Executed query */ 39 | 40 | @property (atomic, retain) NSString *query; 41 | 42 | /** `NSMutableDictionary` mapping column names to numeric index */ 43 | 44 | @property (readonly) NSMutableDictionary *columnNameToIndexMap; 45 | 46 | /** `FMStatement` used by result set. */ 47 | 48 | @property (atomic, retain) FMStatement *statement; 49 | 50 | ///------------------------------------ 51 | /// @name Creating and closing database 52 | ///------------------------------------ 53 | 54 | /** Create result set from `` 55 | 56 | @param statement A `` to be performed 57 | 58 | @param aDB A `` to be used 59 | 60 | @return A `FMResultSet` on success; `nil` on failure 61 | */ 62 | 63 | + (instancetype)resultSetWithStatement:(FMStatement *)statement usingParentDatabase:(FMDatabase*)aDB; 64 | 65 | /** Close result set */ 66 | 67 | - (void)close; 68 | 69 | - (void)setParentDB:(FMDatabase *)newDb; 70 | 71 | ///--------------------------------------- 72 | /// @name Iterating through the result set 73 | ///--------------------------------------- 74 | 75 | /** Retrieve next row for result set. 76 | 77 | You must always invoke `next` before attempting to access the values returned in a query, even if you're only expecting one. 78 | 79 | @return `YES` if row successfully retrieved; `NO` if end of result set reached 80 | 81 | @see hasAnotherRow 82 | */ 83 | 84 | - (BOOL)next; 85 | 86 | /** Did the last call to `` succeed in retrieving another row? 87 | 88 | @return `YES` if the last call to `` succeeded in retrieving another record; `NO` if not. 89 | 90 | @see next 91 | 92 | @warning The `hasAnotherRow` method must follow a call to ``. If the previous database interaction was something other than a call to `next`, then this method may return `NO`, whether there is another row of data or not. 93 | */ 94 | 95 | - (BOOL)hasAnotherRow; 96 | 97 | ///--------------------------------------------- 98 | /// @name Retrieving information from result set 99 | ///--------------------------------------------- 100 | 101 | /** How many columns in result set 102 | 103 | @return Integer value of the number of columns. 104 | */ 105 | 106 | - (int)columnCount; 107 | 108 | /** Column index for column name 109 | 110 | @param columnName `NSString` value of the name of the column. 111 | 112 | @return Zero-based index for column. 113 | */ 114 | 115 | - (int)columnIndexForName:(NSString*)columnName; 116 | 117 | /** Column name for column index 118 | 119 | @param columnIdx Zero-based index for column. 120 | 121 | @return columnName `NSString` value of the name of the column. 122 | */ 123 | 124 | - (NSString*)columnNameForIndex:(int)columnIdx; 125 | 126 | /** Result set integer value for column. 127 | 128 | @param columnName `NSString` value of the name of the column. 129 | 130 | @return `int` value of the result set's column. 131 | */ 132 | 133 | - (int)intForColumn:(NSString*)columnName; 134 | 135 | /** Result set integer value for column. 136 | 137 | @param columnIdx Zero-based index for column. 138 | 139 | @return `int` value of the result set's column. 140 | */ 141 | 142 | - (int)intForColumnIndex:(int)columnIdx; 143 | 144 | /** Result set `long` value for column. 145 | 146 | @param columnName `NSString` value of the name of the column. 147 | 148 | @return `long` value of the result set's column. 149 | */ 150 | 151 | - (long)longForColumn:(NSString*)columnName; 152 | 153 | /** Result set long value for column. 154 | 155 | @param columnIdx Zero-based index for column. 156 | 157 | @return `long` value of the result set's column. 158 | */ 159 | 160 | - (long)longForColumnIndex:(int)columnIdx; 161 | 162 | /** Result set `long long int` value for column. 163 | 164 | @param columnName `NSString` value of the name of the column. 165 | 166 | @return `long long int` value of the result set's column. 167 | */ 168 | 169 | - (long long int)longLongIntForColumn:(NSString*)columnName; 170 | 171 | /** Result set `long long int` value for column. 172 | 173 | @param columnIdx Zero-based index for column. 174 | 175 | @return `long long int` value of the result set's column. 176 | */ 177 | 178 | - (long long int)longLongIntForColumnIndex:(int)columnIdx; 179 | 180 | /** Result set `unsigned long long int` value for column. 181 | 182 | @param columnName `NSString` value of the name of the column. 183 | 184 | @return `unsigned long long int` value of the result set's column. 185 | */ 186 | 187 | - (unsigned long long int)unsignedLongLongIntForColumn:(NSString*)columnName; 188 | 189 | /** Result set `unsigned long long int` value for column. 190 | 191 | @param columnIdx Zero-based index for column. 192 | 193 | @return `unsigned long long int` value of the result set's column. 194 | */ 195 | 196 | - (unsigned long long int)unsignedLongLongIntForColumnIndex:(int)columnIdx; 197 | 198 | /** Result set `BOOL` value for column. 199 | 200 | @param columnName `NSString` value of the name of the column. 201 | 202 | @return `BOOL` value of the result set's column. 203 | */ 204 | 205 | - (BOOL)boolForColumn:(NSString*)columnName; 206 | 207 | /** Result set `BOOL` value for column. 208 | 209 | @param columnIdx Zero-based index for column. 210 | 211 | @return `BOOL` value of the result set's column. 212 | */ 213 | 214 | - (BOOL)boolForColumnIndex:(int)columnIdx; 215 | 216 | /** Result set `double` value for column. 217 | 218 | @param columnName `NSString` value of the name of the column. 219 | 220 | @return `double` value of the result set's column. 221 | 222 | */ 223 | 224 | - (double)doubleForColumn:(NSString*)columnName; 225 | 226 | /** Result set `double` value for column. 227 | 228 | @param columnIdx Zero-based index for column. 229 | 230 | @return `double` value of the result set's column. 231 | 232 | */ 233 | 234 | - (double)doubleForColumnIndex:(int)columnIdx; 235 | 236 | /** Result set `NSString` value for column. 237 | 238 | @param columnName `NSString` value of the name of the column. 239 | 240 | @return `NSString` value of the result set's column. 241 | 242 | */ 243 | 244 | - (NSString*)stringForColumn:(NSString*)columnName; 245 | 246 | /** Result set `NSString` value for column. 247 | 248 | @param columnIdx Zero-based index for column. 249 | 250 | @return `NSString` value of the result set's column. 251 | */ 252 | 253 | - (NSString*)stringForColumnIndex:(int)columnIdx; 254 | 255 | /** Result set `NSDate` value for column. 256 | 257 | @param columnName `NSString` value of the name of the column. 258 | 259 | @return `NSDate` value of the result set's column. 260 | */ 261 | 262 | - (NSDate*)dateForColumn:(NSString*)columnName; 263 | 264 | /** Result set `NSDate` value for column. 265 | 266 | @param columnIdx Zero-based index for column. 267 | 268 | @return `NSDate` value of the result set's column. 269 | 270 | */ 271 | 272 | - (NSDate*)dateForColumnIndex:(int)columnIdx; 273 | 274 | /** Result set `NSData` value for column. 275 | 276 | This is useful when storing binary data in table (such as image or the like). 277 | 278 | @param columnName `NSString` value of the name of the column. 279 | 280 | @return `NSData` value of the result set's column. 281 | 282 | */ 283 | 284 | - (NSData*)dataForColumn:(NSString*)columnName; 285 | 286 | /** Result set `NSData` value for column. 287 | 288 | @param columnIdx Zero-based index for column. 289 | 290 | @return `NSData` value of the result set's column. 291 | */ 292 | 293 | - (NSData*)dataForColumnIndex:(int)columnIdx; 294 | 295 | /** Result set `(const unsigned char *)` value for column. 296 | 297 | @param columnName `NSString` value of the name of the column. 298 | 299 | @return `(const unsigned char *)` value of the result set's column. 300 | */ 301 | 302 | - (const unsigned char *)UTF8StringForColumnName:(NSString*)columnName; 303 | 304 | /** Result set `(const unsigned char *)` value for column. 305 | 306 | @param columnIdx Zero-based index for column. 307 | 308 | @return `(const unsigned char *)` value of the result set's column. 309 | */ 310 | 311 | - (const unsigned char *)UTF8StringForColumnIndex:(int)columnIdx; 312 | 313 | /** Result set object for column. 314 | 315 | @param columnName `NSString` value of the name of the column. 316 | 317 | @return Either `NSNumber`, `NSString`, `NSData`, or `NSNull`. If the column was `NULL`, this returns `[NSNull null]` object. 318 | 319 | @see objectForKeyedSubscript: 320 | */ 321 | 322 | - (id)objectForColumnName:(NSString*)columnName; 323 | 324 | /** Result set object for column. 325 | 326 | @param columnIdx Zero-based index for column. 327 | 328 | @return Either `NSNumber`, `NSString`, `NSData`, or `NSNull`. If the column was `NULL`, this returns `[NSNull null]` object. 329 | 330 | @see objectAtIndexedSubscript: 331 | */ 332 | 333 | - (id)objectForColumnIndex:(int)columnIdx; 334 | 335 | /** Result set object for column. 336 | 337 | This method allows the use of the "boxed" syntax supported in Modern Objective-C. For example, by defining this method, the following syntax is now supported: 338 | 339 | id result = rs[@"employee_name"]; 340 | 341 | This simplified syntax is equivalent to calling: 342 | 343 | id result = [rs objectForKeyedSubscript:@"employee_name"]; 344 | 345 | which is, it turns out, equivalent to calling: 346 | 347 | id result = [rs objectForColumnName:@"employee_name"]; 348 | 349 | @param columnName `NSString` value of the name of the column. 350 | 351 | @return Either `NSNumber`, `NSString`, `NSData`, or `NSNull`. If the column was `NULL`, this returns `[NSNull null]` object. 352 | */ 353 | 354 | - (id)objectForKeyedSubscript:(NSString *)columnName; 355 | 356 | /** Result set object for column. 357 | 358 | This method allows the use of the "boxed" syntax supported in Modern Objective-C. For example, by defining this method, the following syntax is now supported: 359 | 360 | id result = rs[0]; 361 | 362 | This simplified syntax is equivalent to calling: 363 | 364 | id result = [rs objectForKeyedSubscript:0]; 365 | 366 | which is, it turns out, equivalent to calling: 367 | 368 | id result = [rs objectForColumnName:0]; 369 | 370 | @param columnIdx Zero-based index for column. 371 | 372 | @return Either `NSNumber`, `NSString`, `NSData`, or `NSNull`. If the column was `NULL`, this returns `[NSNull null]` object. 373 | */ 374 | 375 | - (id)objectAtIndexedSubscript:(int)columnIdx; 376 | 377 | /** Result set `NSData` value for column. 378 | 379 | @param columnName `NSString` value of the name of the column. 380 | 381 | @return `NSData` value of the result set's column. 382 | 383 | @warning If you are going to use this data after you iterate over the next row, or after you close the 384 | result set, make sure to make a copy of the data first (or just use ``/``) 385 | If you don't, you're going to be in a world of hurt when you try and use the data. 386 | 387 | */ 388 | 389 | - (NSData*)dataNoCopyForColumn:(NSString*)columnName NS_RETURNS_NOT_RETAINED; 390 | 391 | /** Result set `NSData` value for column. 392 | 393 | @param columnIdx Zero-based index for column. 394 | 395 | @return `NSData` value of the result set's column. 396 | 397 | @warning If you are going to use this data after you iterate over the next row, or after you close the 398 | result set, make sure to make a copy of the data first (or just use ``/``) 399 | If you don't, you're going to be in a world of hurt when you try and use the data. 400 | 401 | */ 402 | 403 | - (NSData*)dataNoCopyForColumnIndex:(int)columnIdx NS_RETURNS_NOT_RETAINED; 404 | 405 | /** Is the column `NULL`? 406 | 407 | @param columnIdx Zero-based index for column. 408 | 409 | @return `YES` if column is `NULL`; `NO` if not `NULL`. 410 | */ 411 | 412 | - (BOOL)columnIndexIsNull:(int)columnIdx; 413 | 414 | /** Is the column `NULL`? 415 | 416 | @param columnName `NSString` value of the name of the column. 417 | 418 | @return `YES` if column is `NULL`; `NO` if not `NULL`. 419 | */ 420 | 421 | - (BOOL)columnIsNull:(NSString*)columnName; 422 | 423 | 424 | /** Returns a dictionary of the row results mapped to case sensitive keys of the column names. 425 | 426 | @returns `NSDictionary` of the row results. 427 | 428 | @warning The keys to the dictionary are case sensitive of the column names. 429 | */ 430 | 431 | - (NSDictionary*)resultDictionary; 432 | 433 | /** Returns a dictionary of the row results 434 | 435 | @see resultDictionary 436 | 437 | @warning **Deprecated**: Please use `` instead. Also, beware that `` is case sensitive! 438 | */ 439 | 440 | - (NSDictionary*)resultDict __attribute__ ((deprecated)); 441 | 442 | ///----------------------------- 443 | /// @name Key value coding magic 444 | ///----------------------------- 445 | 446 | /** Performs `setValue` to yield support for key value observing. 447 | 448 | @param object The object for which the values will be set. This is the key-value-coding compliant object that you might, for example, observe. 449 | 450 | */ 451 | 452 | - (void)kvcMagic:(id)object; 453 | 454 | 455 | @end 456 | 457 | -------------------------------------------------------------------------------- /fmd数组/Lib/fmdb/FMResultSet.m: -------------------------------------------------------------------------------- 1 | #import "FMResultSet.h" 2 | #import "FMDatabase.h" 3 | #import "unistd.h" 4 | 5 | @interface FMDatabase () 6 | - (void)resultSetDidClose:(FMResultSet *)resultSet; 7 | @end 8 | 9 | 10 | @implementation FMResultSet 11 | @synthesize query=_query; 12 | @synthesize statement=_statement; 13 | 14 | + (instancetype)resultSetWithStatement:(FMStatement *)statement usingParentDatabase:(FMDatabase*)aDB { 15 | 16 | FMResultSet *rs = [[FMResultSet alloc] init]; 17 | 18 | [rs setStatement:statement]; 19 | [rs setParentDB:aDB]; 20 | 21 | NSParameterAssert(![statement inUse]); 22 | [statement setInUse:YES]; // weak reference 23 | 24 | return FMDBReturnAutoreleased(rs); 25 | } 26 | 27 | - (void)finalize { 28 | [self close]; 29 | [super finalize]; 30 | } 31 | 32 | - (void)dealloc { 33 | [self close]; 34 | 35 | FMDBRelease(_query); 36 | _query = nil; 37 | 38 | FMDBRelease(_columnNameToIndexMap); 39 | _columnNameToIndexMap = nil; 40 | 41 | #if ! __has_feature(objc_arc) 42 | [super dealloc]; 43 | #endif 44 | } 45 | 46 | - (void)close { 47 | [_statement reset]; 48 | FMDBRelease(_statement); 49 | _statement = nil; 50 | 51 | // we don't need this anymore... (i think) 52 | //[_parentDB setInUse:NO]; 53 | [_parentDB resultSetDidClose:self]; 54 | _parentDB = nil; 55 | } 56 | 57 | - (int)columnCount { 58 | return sqlite3_column_count([_statement statement]); 59 | } 60 | 61 | - (NSMutableDictionary *)columnNameToIndexMap { 62 | if (!_columnNameToIndexMap) { 63 | int columnCount = sqlite3_column_count([_statement statement]); 64 | _columnNameToIndexMap = [[NSMutableDictionary alloc] initWithCapacity:(NSUInteger)columnCount]; 65 | int columnIdx = 0; 66 | for (columnIdx = 0; columnIdx < columnCount; columnIdx++) { 67 | [_columnNameToIndexMap setObject:[NSNumber numberWithInt:columnIdx] 68 | forKey:[[NSString stringWithUTF8String:sqlite3_column_name([_statement statement], columnIdx)] lowercaseString]]; 69 | } 70 | } 71 | return _columnNameToIndexMap; 72 | } 73 | 74 | - (void)kvcMagic:(id)object { 75 | 76 | int columnCount = sqlite3_column_count([_statement statement]); 77 | 78 | int columnIdx = 0; 79 | for (columnIdx = 0; columnIdx < columnCount; columnIdx++) { 80 | 81 | const char *c = (const char *)sqlite3_column_text([_statement statement], columnIdx); 82 | 83 | // check for a null row 84 | if (c) { 85 | NSString *s = [NSString stringWithUTF8String:c]; 86 | 87 | [object setValue:s forKey:[NSString stringWithUTF8String:sqlite3_column_name([_statement statement], columnIdx)]]; 88 | } 89 | } 90 | } 91 | 92 | #pragma clang diagnostic push 93 | #pragma clang diagnostic ignored "-Wdeprecated-implementations" 94 | 95 | - (NSDictionary*)resultDict { 96 | 97 | NSUInteger num_cols = (NSUInteger)sqlite3_data_count([_statement statement]); 98 | 99 | if (num_cols > 0) { 100 | NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:num_cols]; 101 | 102 | NSEnumerator *columnNames = [[self columnNameToIndexMap] keyEnumerator]; 103 | NSString *columnName = nil; 104 | while ((columnName = [columnNames nextObject])) { 105 | id objectValue = [self objectForColumnName:columnName]; 106 | [dict setObject:objectValue forKey:columnName]; 107 | } 108 | 109 | return FMDBReturnAutoreleased([dict copy]); 110 | } 111 | else { 112 | NSLog(@"Warning: There seem to be no columns in this set."); 113 | } 114 | 115 | return nil; 116 | } 117 | 118 | #pragma clang diagnostic pop 119 | 120 | - (NSDictionary*)resultDictionary { 121 | 122 | NSUInteger num_cols = (NSUInteger)sqlite3_data_count([_statement statement]); 123 | 124 | if (num_cols > 0) { 125 | NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:num_cols]; 126 | 127 | int columnCount = sqlite3_column_count([_statement statement]); 128 | 129 | int columnIdx = 0; 130 | for (columnIdx = 0; columnIdx < columnCount; columnIdx++) { 131 | 132 | NSString *columnName = [NSString stringWithUTF8String:sqlite3_column_name([_statement statement], columnIdx)]; 133 | id objectValue = [self objectForColumnIndex:columnIdx]; 134 | [dict setObject:objectValue forKey:columnName]; 135 | } 136 | 137 | return dict; 138 | } 139 | else { 140 | NSLog(@"Warning: There seem to be no columns in this set."); 141 | } 142 | 143 | return nil; 144 | } 145 | 146 | 147 | 148 | 149 | - (BOOL)next { 150 | 151 | int rc = sqlite3_step([_statement statement]); 152 | 153 | if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) { 154 | NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [_parentDB databasePath]); 155 | NSLog(@"Database busy"); 156 | } 157 | else if (SQLITE_DONE == rc || SQLITE_ROW == rc) { 158 | // all is well, let's return. 159 | } 160 | else if (SQLITE_ERROR == rc) { 161 | NSLog(@"Error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle])); 162 | } 163 | else if (SQLITE_MISUSE == rc) { 164 | // uh oh. 165 | NSLog(@"Error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle])); 166 | } 167 | else { 168 | // wtf? 169 | NSLog(@"Unknown error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle])); 170 | } 171 | 172 | 173 | if (rc != SQLITE_ROW) { 174 | [self close]; 175 | } 176 | 177 | return (rc == SQLITE_ROW); 178 | } 179 | 180 | - (BOOL)hasAnotherRow { 181 | return sqlite3_errcode([_parentDB sqliteHandle]) == SQLITE_ROW; 182 | } 183 | 184 | - (int)columnIndexForName:(NSString*)columnName { 185 | columnName = [columnName lowercaseString]; 186 | 187 | NSNumber *n = [[self columnNameToIndexMap] objectForKey:columnName]; 188 | 189 | if (n) { 190 | return [n intValue]; 191 | } 192 | 193 | NSLog(@"Warning: I could not find the column named '%@'.", columnName); 194 | 195 | return -1; 196 | } 197 | 198 | 199 | 200 | - (int)intForColumn:(NSString*)columnName { 201 | return [self intForColumnIndex:[self columnIndexForName:columnName]]; 202 | } 203 | 204 | - (int)intForColumnIndex:(int)columnIdx { 205 | return sqlite3_column_int([_statement statement], columnIdx); 206 | } 207 | 208 | - (long)longForColumn:(NSString*)columnName { 209 | return [self longForColumnIndex:[self columnIndexForName:columnName]]; 210 | } 211 | 212 | - (long)longForColumnIndex:(int)columnIdx { 213 | return (long)sqlite3_column_int64([_statement statement], columnIdx); 214 | } 215 | 216 | - (long long int)longLongIntForColumn:(NSString*)columnName { 217 | return [self longLongIntForColumnIndex:[self columnIndexForName:columnName]]; 218 | } 219 | 220 | - (long long int)longLongIntForColumnIndex:(int)columnIdx { 221 | return sqlite3_column_int64([_statement statement], columnIdx); 222 | } 223 | 224 | - (unsigned long long int)unsignedLongLongIntForColumn:(NSString*)columnName { 225 | return [self unsignedLongLongIntForColumnIndex:[self columnIndexForName:columnName]]; 226 | } 227 | 228 | - (unsigned long long int)unsignedLongLongIntForColumnIndex:(int)columnIdx { 229 | return (unsigned long long int)[self longLongIntForColumnIndex:columnIdx]; 230 | } 231 | 232 | - (BOOL)boolForColumn:(NSString*)columnName { 233 | return [self boolForColumnIndex:[self columnIndexForName:columnName]]; 234 | } 235 | 236 | - (BOOL)boolForColumnIndex:(int)columnIdx { 237 | return ([self intForColumnIndex:columnIdx] != 0); 238 | } 239 | 240 | - (double)doubleForColumn:(NSString*)columnName { 241 | return [self doubleForColumnIndex:[self columnIndexForName:columnName]]; 242 | } 243 | 244 | - (double)doubleForColumnIndex:(int)columnIdx { 245 | return sqlite3_column_double([_statement statement], columnIdx); 246 | } 247 | 248 | - (NSString*)stringForColumnIndex:(int)columnIdx { 249 | 250 | if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) { 251 | return nil; 252 | } 253 | 254 | const char *c = (const char *)sqlite3_column_text([_statement statement], columnIdx); 255 | 256 | if (!c) { 257 | // null row. 258 | return nil; 259 | } 260 | 261 | return [NSString stringWithUTF8String:c]; 262 | } 263 | 264 | - (NSString*)stringForColumn:(NSString*)columnName { 265 | return [self stringForColumnIndex:[self columnIndexForName:columnName]]; 266 | } 267 | 268 | - (NSDate*)dateForColumn:(NSString*)columnName { 269 | return [self dateForColumnIndex:[self columnIndexForName:columnName]]; 270 | } 271 | 272 | - (NSDate*)dateForColumnIndex:(int)columnIdx { 273 | 274 | if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) { 275 | return nil; 276 | } 277 | 278 | return [_parentDB hasDateFormatter] ? [_parentDB dateFromString:[self stringForColumnIndex:columnIdx]] : [NSDate dateWithTimeIntervalSince1970:[self doubleForColumnIndex:columnIdx]]; 279 | } 280 | 281 | 282 | - (NSData*)dataForColumn:(NSString*)columnName { 283 | return [self dataForColumnIndex:[self columnIndexForName:columnName]]; 284 | } 285 | 286 | - (NSData*)dataForColumnIndex:(int)columnIdx { 287 | 288 | if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) { 289 | return nil; 290 | } 291 | 292 | int dataSize = sqlite3_column_bytes([_statement statement], columnIdx); 293 | const char *dataBuffer = sqlite3_column_blob([_statement statement], columnIdx); 294 | 295 | if (dataBuffer == NULL) { 296 | return nil; 297 | } 298 | 299 | return [NSData dataWithBytes:(const void *)dataBuffer length:(NSUInteger)dataSize]; 300 | } 301 | 302 | 303 | - (NSData*)dataNoCopyForColumn:(NSString*)columnName { 304 | return [self dataNoCopyForColumnIndex:[self columnIndexForName:columnName]]; 305 | } 306 | 307 | - (NSData*)dataNoCopyForColumnIndex:(int)columnIdx { 308 | 309 | if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) { 310 | return nil; 311 | } 312 | 313 | int dataSize = sqlite3_column_bytes([_statement statement], columnIdx); 314 | 315 | NSData *data = [NSData dataWithBytesNoCopy:(void *)sqlite3_column_blob([_statement statement], columnIdx) length:(NSUInteger)dataSize freeWhenDone:NO]; 316 | 317 | return data; 318 | } 319 | 320 | 321 | - (BOOL)columnIndexIsNull:(int)columnIdx { 322 | return sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL; 323 | } 324 | 325 | - (BOOL)columnIsNull:(NSString*)columnName { 326 | return [self columnIndexIsNull:[self columnIndexForName:columnName]]; 327 | } 328 | 329 | - (const unsigned char *)UTF8StringForColumnIndex:(int)columnIdx { 330 | 331 | if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0)) { 332 | return nil; 333 | } 334 | 335 | return sqlite3_column_text([_statement statement], columnIdx); 336 | } 337 | 338 | - (const unsigned char *)UTF8StringForColumnName:(NSString*)columnName { 339 | return [self UTF8StringForColumnIndex:[self columnIndexForName:columnName]]; 340 | } 341 | 342 | - (id)objectForColumnIndex:(int)columnIdx { 343 | int columnType = sqlite3_column_type([_statement statement], columnIdx); 344 | 345 | id returnValue = nil; 346 | 347 | if (columnType == SQLITE_INTEGER) { 348 | returnValue = [NSNumber numberWithLongLong:[self longLongIntForColumnIndex:columnIdx]]; 349 | } 350 | else if (columnType == SQLITE_FLOAT) { 351 | returnValue = [NSNumber numberWithDouble:[self doubleForColumnIndex:columnIdx]]; 352 | } 353 | else if (columnType == SQLITE_BLOB) { 354 | returnValue = [self dataForColumnIndex:columnIdx]; 355 | } 356 | else { 357 | //default to a string for everything else 358 | returnValue = [self stringForColumnIndex:columnIdx]; 359 | } 360 | 361 | if (returnValue == nil) { 362 | returnValue = [NSNull null]; 363 | } 364 | 365 | return returnValue; 366 | } 367 | 368 | - (id)objectForColumnName:(NSString*)columnName { 369 | return [self objectForColumnIndex:[self columnIndexForName:columnName]]; 370 | } 371 | 372 | // returns autoreleased NSString containing the name of the column in the result set 373 | - (NSString*)columnNameForIndex:(int)columnIdx { 374 | return [NSString stringWithUTF8String: sqlite3_column_name([_statement statement], columnIdx)]; 375 | } 376 | 377 | - (void)setParentDB:(FMDatabase *)newDb { 378 | _parentDB = newDb; 379 | } 380 | 381 | - (id)objectAtIndexedSubscript:(int)columnIdx { 382 | return [self objectForColumnIndex:columnIdx]; 383 | } 384 | 385 | - (id)objectForKeyedSubscript:(NSString *)columnName { 386 | return [self objectForColumnName:columnName]; 387 | } 388 | 389 | 390 | @end 391 | -------------------------------------------------------------------------------- /fmd数组/People.h: -------------------------------------------------------------------------------- 1 | // 2 | // People.h 3 | // fmd数组 4 | // 5 | // Created by jglz on 16/4/29. 6 | // Copyright © 2016年 yxb. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface People : NSObject 12 | @property (nonatomic,copy) NSString * name; 13 | @property (nonatomic,copy) NSString * age; 14 | @property (nonatomic,strong) NSMutableArray * arr; 15 | @end 16 | -------------------------------------------------------------------------------- /fmd数组/People.m: -------------------------------------------------------------------------------- 1 | // 2 | // People.m 3 | // fmd数组 4 | // 5 | // Created by jglz on 16/4/29. 6 | // Copyright © 2016年 yxb. All rights reserved. 7 | // 8 | 9 | #import "People.h" 10 | 11 | @implementation People 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /fmd数组/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // fmd数组 4 | // 5 | // Created by jglz on 16/4/29. 6 | // Copyright © 2016年 yxb. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /fmd数组/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // fmd数组 4 | // 5 | // Created by jglz on 16/4/29. 6 | // Copyright © 2016年 yxb. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "People.h" 11 | #import "YTKKeyValueStore.h" 12 | @interface ViewController () 13 | { 14 | NSMutableArray *dataArr ; 15 | 16 | } 17 | @end 18 | 19 | @implementation ViewController 20 | 21 | - (void)viewDidLoad { 22 | [super viewDidLoad]; 23 | dataArr = [NSMutableArray arrayWithCapacity:0]; 24 | for (int i = 0; i< 10; i++) { 25 | People * p = [[People alloc]init]; 26 | p.name = [NSString stringWithFormat:@"gxh%d",i]; 27 | p.age = [NSString stringWithFormat:@"age%d",i]; 28 | 29 | p.arr = [NSMutableArray arrayWithCapacity:0]; 30 | for (int j = 0 ; j<10; j++) { //测试模型里面套数组,数组里面装的是模型 31 | People * p1 = [[People alloc]init]; 32 | p1.name = [NSString stringWithFormat:@"yxb%d",j]; 33 | p1.age = [NSString stringWithFormat:@"yxb%d",j]; 34 | [p.arr addObject:p1]; 35 | } 36 | 37 | [dataArr addObject:p]; 38 | } 39 | 40 | 41 | 42 | // 创建数据库 开始存 43 | YTKKeyValueStore *store = [[YTKKeyValueStore alloc] initDBWithName:@"test.db"]; 44 | // 创建表格 45 | NSString *tableName = @"user_table"; 46 | [store createTableWithName:tableName]; 47 | 48 | [store putModelObjectArray:dataArr intoTable:tableName]; 49 | 50 | 51 | //取数据 52 | NSMutableArray *arr = [store getModelArrayByclassName:[People class]fromTable:tableName arrayCount:10]; 53 | 54 | NSLog(@"%@",arr); 55 | 56 | [store clearTable:tableName]; 57 | // Do any additional setup after loading the view, typically from a nib. 58 | } 59 | 60 | - (void)didReceiveMemoryWarning { 61 | [super didReceiveMemoryWarning]; 62 | // Dispose of any resources that can be recreated. 63 | } 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /fmd数组/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // fmd数组 4 | // 5 | // Created by jglz on 16/4/29. 6 | // Copyright © 2016年 yxb. 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 | -------------------------------------------------------------------------------- /fmd数组Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 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 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /fmd数组Tests/fmd__Tests.m: -------------------------------------------------------------------------------- 1 | // 2 | // fmd__Tests.m 3 | // fmd数组Tests 4 | // 5 | // Created by jglz on 16/4/29. 6 | // Copyright © 2016年 yxb. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface fmd__Tests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation fmd__Tests 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 | -------------------------------------------------------------------------------- /fmd数组UITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 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 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /fmd数组UITests/fmd__UITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // fmd__UITests.m 3 | // fmd数组UITests 4 | // 5 | // Created by jglz on 16/4/29. 6 | // Copyright © 2016年 yxb. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface fmd__UITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation fmd__UITests 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 | --------------------------------------------------------------------------------