├── 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 |
--------------------------------------------------------------------------------