├── .gitignore ├── LICENSE ├── Podfile ├── Podfile.lock ├── Pods ├── FMDB │ ├── LICENSE.txt │ ├── README.markdown │ └── src │ │ └── fmdb │ │ ├── FMDB.h │ │ ├── FMDatabase.h │ │ ├── FMDatabase.m │ │ ├── FMDatabaseAdditions.h │ │ ├── FMDatabaseAdditions.m │ │ ├── FMDatabasePool.h │ │ ├── FMDatabasePool.m │ │ ├── FMDatabaseQueue.h │ │ ├── FMDatabaseQueue.m │ │ ├── FMResultSet.h │ │ └── FMResultSet.m ├── Manifest.lock ├── Pods.xcodeproj │ └── project.pbxproj └── Target Support Files │ ├── FMDB │ ├── FMDB-dummy.m │ ├── FMDB-prefix.pch │ ├── FMDB-umbrella.h │ ├── FMDB.modulemap │ ├── FMDB.xcconfig │ └── Info.plist │ └── Pods-YIIFMDB │ ├── Info.plist │ ├── Pods-YIIFMDB-acknowledgements.markdown │ ├── Pods-YIIFMDB-acknowledgements.plist │ ├── Pods-YIIFMDB-dummy.m │ ├── Pods-YIIFMDB-frameworks.sh │ ├── Pods-YIIFMDB-resources.sh │ ├── Pods-YIIFMDB-umbrella.h │ ├── Pods-YIIFMDB.debug.xcconfig │ ├── Pods-YIIFMDB.modulemap │ └── Pods-YIIFMDB.release.xcconfig ├── README.md ├── YIIFMDB.podspec ├── YIIFMDB.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── YIIFMDB.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── YIIFMDB ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── LCVideoModel.h ├── LCVideoModel.m ├── ViewController.h ├── ViewController.m ├── YIIFMDB │ ├── YIIFMDB.h │ ├── YIIFMDB.m │ ├── YIIParameters.h │ └── YIIParameters.m └── main.m └── YIIFMDBUITests ├── Info.plist └── YIIFMDBUITests.m /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | # OS X 6 | .DS_Store 7 | 8 | ## Build generated 9 | build/ 10 | DerivedData/ 11 | 12 | ## Various settings 13 | *.pbxuser 14 | !default.pbxuser 15 | *.mode1v3 16 | !default.mode1v3 17 | *.mode2v3 18 | !default.mode2v3 19 | *.perspectivev3 20 | !default.perspectivev3 21 | xcuserdata/ 22 | 23 | ## Other 24 | *.moved-aside 25 | *.xcuserstate 26 | *.xcscmblueprint 27 | 28 | ## Obj-C/Swift specific 29 | *.hmap 30 | *.ipa 31 | *.dSYM.zip 32 | *.dSYM 33 | 34 | ## Playgrounds 35 | timeline.xctimeline 36 | playground.xcworkspace 37 | 38 | # Swift Package Manager 39 | # 40 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 41 | # Packages/ 42 | .build/ 43 | 44 | # CocoaPods 45 | # 46 | # We recommend against adding the Pods directory to your .gitignore. However 47 | # you should judge for yourself, the pros and cons are mentioned at: 48 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 49 | # 50 | Pods/ 51 | 52 | # Carthage 53 | # 54 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 55 | # Carthage/Checkouts 56 | 57 | Carthage/Build 58 | 59 | # fastlane 60 | # 61 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 62 | # screenshots whenever they are needed. 63 | # For more information about the recommended setup visit: 64 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 65 | 66 | fastlane/report.xml 67 | fastlane/Preview.html 68 | fastlane/screenshots 69 | fastlane/test_output 70 | 71 | # Code Injection 72 | # 73 | # After new code Injection tools there's a generated folder /iOSInjectionProject 74 | # https://github.com/johnno1962/injectionforxcode 75 | 76 | iOSInjectionProject/ 77 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 刘冲 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '8.0' 2 | 3 | source 'https://github.com/CocoaPods/Specs.git' 4 | use_frameworks! 5 | inhibit_all_warnings! 6 | 7 | target 'YIIFMDB' do 8 | 9 | pod 'FMDB' 10 | 11 | end 12 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - FMDB (2.7.5): 3 | - FMDB/standard (= 2.7.5) 4 | - FMDB/standard (2.7.5) 5 | 6 | DEPENDENCIES: 7 | - FMDB 8 | 9 | SPEC REPOS: 10 | https://github.com/cocoapods/specs.git: 11 | - FMDB 12 | 13 | SPEC CHECKSUMS: 14 | FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a 15 | 16 | PODFILE CHECKSUM: f13ff6581f135bc6c1597623f3e34035a55cb088 17 | 18 | COCOAPODS: 1.5.3 19 | -------------------------------------------------------------------------------- /Pods/FMDB/LICENSE.txt: -------------------------------------------------------------------------------- 1 | If you are using FMDB in your project, I'd love to hear about it. Let Gus know 2 | by sending an email to gus@flyingmeat.com. 3 | 4 | And if you happen to come across either Gus Mueller or Rob Ryan in a bar, you 5 | might consider purchasing a drink of their choosing if FMDB has been useful to 6 | you. 7 | 8 | Finally, and shortly, this is the MIT License. 9 | 10 | Copyright (c) 2008-2014 Flying Meat Inc. 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining a copy 13 | of this software and associated documentation files (the "Software"), to deal 14 | in the Software without restriction, including without limitation the rights 15 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | copies of the Software, and to permit persons to whom the Software is 17 | furnished to do so, subject to the following conditions: 18 | 19 | The above copyright notice and this permission notice shall be included in 20 | all copies or substantial portions of the Software. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 28 | THE SOFTWARE. -------------------------------------------------------------------------------- /Pods/FMDB/src/fmdb/FMDB.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | FOUNDATION_EXPORT double FMDBVersionNumber; 4 | FOUNDATION_EXPORT const unsigned char FMDBVersionString[]; 5 | 6 | #import "FMDatabase.h" 7 | #import "FMResultSet.h" 8 | #import "FMDatabaseAdditions.h" 9 | #import "FMDatabaseQueue.h" 10 | #import "FMDatabasePool.h" 11 | -------------------------------------------------------------------------------- /Pods/FMDB/src/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 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | /** Category of additions for `` class. 15 | 16 | ### See also 17 | 18 | - `` 19 | */ 20 | 21 | @interface FMDatabase (FMDatabaseAdditions) 22 | 23 | ///---------------------------------------- 24 | /// @name Return results of SQL to variable 25 | ///---------------------------------------- 26 | 27 | /** Return `int` value for query 28 | 29 | @param query The SQL query to be performed. 30 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. 31 | 32 | @return `int` value. 33 | 34 | @note This is not available from Swift. 35 | */ 36 | 37 | - (int)intForQuery:(NSString*)query, ...; 38 | 39 | /** Return `long` value for query 40 | 41 | @param query The SQL query to be performed. 42 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. 43 | 44 | @return `long` value. 45 | 46 | @note This is not available from Swift. 47 | */ 48 | 49 | - (long)longForQuery:(NSString*)query, ...; 50 | 51 | /** Return `BOOL` value for query 52 | 53 | @param query The SQL query to be performed. 54 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. 55 | 56 | @return `BOOL` value. 57 | 58 | @note This is not available from Swift. 59 | */ 60 | 61 | - (BOOL)boolForQuery:(NSString*)query, ...; 62 | 63 | /** Return `double` value for query 64 | 65 | @param query The SQL query to be performed. 66 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. 67 | 68 | @return `double` value. 69 | 70 | @note This is not available from Swift. 71 | */ 72 | 73 | - (double)doubleForQuery:(NSString*)query, ...; 74 | 75 | /** Return `NSString` value for query 76 | 77 | @param query The SQL query to be performed. 78 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. 79 | 80 | @return `NSString` value. 81 | 82 | @note This is not available from Swift. 83 | */ 84 | 85 | - (NSString * _Nullable)stringForQuery:(NSString*)query, ...; 86 | 87 | /** Return `NSData` value for query 88 | 89 | @param query The SQL query to be performed. 90 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. 91 | 92 | @return `NSData` value. 93 | 94 | @note This is not available from Swift. 95 | */ 96 | 97 | - (NSData * _Nullable)dataForQuery:(NSString*)query, ...; 98 | 99 | /** Return `NSDate` value for query 100 | 101 | @param query The SQL query to be performed. 102 | @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query. 103 | 104 | @return `NSDate` value. 105 | 106 | @note This is not available from Swift. 107 | */ 108 | 109 | - (NSDate * _Nullable)dateForQuery:(NSString*)query, ...; 110 | 111 | 112 | // Notice that there's no dataNoCopyForQuery:. 113 | // That would be a bad idea, because we close out the result set, and then what 114 | // happens to the data that we just didn't copy? Who knows, not I. 115 | 116 | 117 | ///-------------------------------- 118 | /// @name Schema related operations 119 | ///-------------------------------- 120 | 121 | /** Does table exist in database? 122 | 123 | @param tableName The name of the table being looked for. 124 | 125 | @return `YES` if table found; `NO` if not found. 126 | */ 127 | 128 | - (BOOL)tableExists:(NSString*)tableName; 129 | 130 | /** The schema of the database. 131 | 132 | This will be the schema for the entire database. For each entity, each row of the result set will include the following fields: 133 | 134 | - `type` - The type of entity (e.g. table, index, view, or trigger) 135 | - `name` - The name of the object 136 | - `tbl_name` - The name of the table to which the object references 137 | - `rootpage` - The page number of the root b-tree page for tables and indices 138 | - `sql` - The SQL that created the entity 139 | 140 | @return `FMResultSet` of schema; `nil` on error. 141 | 142 | @see [SQLite File Format](http://www.sqlite.org/fileformat.html) 143 | */ 144 | 145 | - (FMResultSet * _Nullable)getSchema; 146 | 147 | /** The schema of the database. 148 | 149 | This will be the schema for a particular table as report by SQLite `PRAGMA`, for example: 150 | 151 | PRAGMA table_info('employees') 152 | 153 | This will report: 154 | 155 | - `cid` - The column ID number 156 | - `name` - The name of the column 157 | - `type` - The data type specified for the column 158 | - `notnull` - whether the field is defined as NOT NULL (i.e. values required) 159 | - `dflt_value` - The default value for the column 160 | - `pk` - Whether the field is part of the primary key of the table 161 | 162 | @param tableName The name of the table for whom the schema will be returned. 163 | 164 | @return `FMResultSet` of schema; `nil` on error. 165 | 166 | @see [table_info](http://www.sqlite.org/pragma.html#pragma_table_info) 167 | */ 168 | 169 | - (FMResultSet * _Nullable)getTableSchema:(NSString*)tableName; 170 | 171 | /** Test to see if particular column exists for particular table in database 172 | 173 | @param columnName The name of the column. 174 | 175 | @param tableName The name of the table. 176 | 177 | @return `YES` if column exists in table in question; `NO` otherwise. 178 | */ 179 | 180 | - (BOOL)columnExists:(NSString*)columnName inTableWithName:(NSString*)tableName; 181 | 182 | /** Test to see if particular column exists for particular table in database 183 | 184 | @param columnName The name of the column. 185 | 186 | @param tableName The name of the table. 187 | 188 | @return `YES` if column exists in table in question; `NO` otherwise. 189 | 190 | @see columnExists:inTableWithName: 191 | 192 | @warning Deprecated - use `` instead. 193 | */ 194 | 195 | - (BOOL)columnExists:(NSString*)tableName columnName:(NSString*)columnName __deprecated_msg("Use columnExists:inTableWithName: instead"); 196 | 197 | 198 | /** Validate SQL statement 199 | 200 | This validates SQL statement by performing `sqlite3_prepare_v2`, but not returning the results, but instead immediately calling `sqlite3_finalize`. 201 | 202 | @param sql The SQL statement being validated. 203 | 204 | @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. 205 | 206 | @return `YES` if validation succeeded without incident; `NO` otherwise. 207 | 208 | */ 209 | 210 | - (BOOL)validateSQL:(NSString*)sql error:(NSError * _Nullable *)error; 211 | 212 | 213 | ///----------------------------------- 214 | /// @name Application identifier tasks 215 | ///----------------------------------- 216 | 217 | /** Retrieve application ID 218 | 219 | @return The `uint32_t` numeric value of the application ID. 220 | 221 | @see setApplicationID: 222 | */ 223 | 224 | @property (nonatomic) uint32_t applicationID; 225 | 226 | #if TARGET_OS_MAC && !TARGET_OS_IPHONE 227 | 228 | /** Retrieve application ID string 229 | 230 | @see setApplicationIDString: 231 | */ 232 | 233 | @property (nonatomic, retain) NSString *applicationIDString; 234 | 235 | #endif 236 | 237 | ///----------------------------------- 238 | /// @name user version identifier tasks 239 | ///----------------------------------- 240 | 241 | /** Retrieve user version 242 | 243 | @see setUserVersion: 244 | */ 245 | 246 | @property (nonatomic) uint32_t userVersion; 247 | 248 | @end 249 | 250 | NS_ASSUME_NONNULL_END 251 | -------------------------------------------------------------------------------- /Pods/FMDB/src/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 | #if FMDB_SQLITE_STANDALONE 14 | #import 15 | #else 16 | #import 17 | #endif 18 | 19 | @interface FMDatabase (PrivateStuff) 20 | - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray * _Nullable)arrayArgs orDictionary:(NSDictionary * _Nullable)dictionaryArgs orVAList:(va_list)args; 21 | @end 22 | 23 | @implementation FMDatabase (FMDatabaseAdditions) 24 | 25 | #define RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(type, sel) \ 26 | va_list args; \ 27 | va_start(args, query); \ 28 | FMResultSet *resultSet = [self executeQuery:query withArgumentsInArray:0x00 orDictionary:0x00 orVAList:args]; \ 29 | va_end(args); \ 30 | if (![resultSet next]) { return (type)0; } \ 31 | type ret = [resultSet sel:0]; \ 32 | [resultSet close]; \ 33 | [resultSet setParentDB:nil]; \ 34 | return ret; 35 | 36 | 37 | - (NSString *)stringForQuery:(NSString*)query, ... { 38 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSString *, stringForColumnIndex); 39 | } 40 | 41 | - (int)intForQuery:(NSString*)query, ... { 42 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(int, intForColumnIndex); 43 | } 44 | 45 | - (long)longForQuery:(NSString*)query, ... { 46 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(long, longForColumnIndex); 47 | } 48 | 49 | - (BOOL)boolForQuery:(NSString*)query, ... { 50 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(BOOL, boolForColumnIndex); 51 | } 52 | 53 | - (double)doubleForQuery:(NSString*)query, ... { 54 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(double, doubleForColumnIndex); 55 | } 56 | 57 | - (NSData*)dataForQuery:(NSString*)query, ... { 58 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSData *, dataForColumnIndex); 59 | } 60 | 61 | - (NSDate*)dateForQuery:(NSString*)query, ... { 62 | RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSDate *, dateForColumnIndex); 63 | } 64 | 65 | 66 | - (BOOL)tableExists:(NSString*)tableName { 67 | 68 | tableName = [tableName lowercaseString]; 69 | 70 | FMResultSet *rs = [self executeQuery:@"select [sql] from sqlite_master where [type] = 'table' and lower(name) = ?", tableName]; 71 | 72 | //if at least one next exists, table exists 73 | BOOL returnBool = [rs next]; 74 | 75 | //close and free object 76 | [rs close]; 77 | 78 | return returnBool; 79 | } 80 | 81 | /* 82 | get table with list of tables: result colums: type[STRING], name[STRING],tbl_name[STRING],rootpage[INTEGER],sql[STRING] 83 | check if table exist in database (patch from OZLB) 84 | */ 85 | - (FMResultSet * _Nullable)getSchema { 86 | 87 | //result colums: type[STRING], name[STRING],tbl_name[STRING],rootpage[INTEGER],sql[STRING] 88 | 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"]; 89 | 90 | return rs; 91 | } 92 | 93 | /* 94 | get table schema: result colums: cid[INTEGER], name,type [STRING], notnull[INTEGER], dflt_value[],pk[INTEGER] 95 | */ 96 | - (FMResultSet * _Nullable)getTableSchema:(NSString*)tableName { 97 | 98 | //result colums: cid[INTEGER], name,type [STRING], notnull[INTEGER], dflt_value[],pk[INTEGER] 99 | FMResultSet *rs = [self executeQuery:[NSString stringWithFormat: @"pragma table_info('%@')", tableName]]; 100 | 101 | return rs; 102 | } 103 | 104 | - (BOOL)columnExists:(NSString*)columnName inTableWithName:(NSString*)tableName { 105 | 106 | BOOL returnBool = NO; 107 | 108 | tableName = [tableName lowercaseString]; 109 | columnName = [columnName lowercaseString]; 110 | 111 | FMResultSet *rs = [self getTableSchema:tableName]; 112 | 113 | //check if column is present in table schema 114 | while ([rs next]) { 115 | if ([[[rs stringForColumn:@"name"] lowercaseString] isEqualToString:columnName]) { 116 | returnBool = YES; 117 | break; 118 | } 119 | } 120 | 121 | //If this is not done FMDatabase instance stays out of pool 122 | [rs close]; 123 | 124 | return returnBool; 125 | } 126 | 127 | 128 | 129 | - (uint32_t)applicationID { 130 | #if SQLITE_VERSION_NUMBER >= 3007017 131 | uint32_t r = 0; 132 | 133 | FMResultSet *rs = [self executeQuery:@"pragma application_id"]; 134 | 135 | if ([rs next]) { 136 | r = (uint32_t)[rs longLongIntForColumnIndex:0]; 137 | } 138 | 139 | [rs close]; 140 | 141 | return r; 142 | #else 143 | NSString *errorMessage = NSLocalizedStringFromTable(@"Application ID functions require SQLite 3.7.17", @"FMDB", nil); 144 | if (self.logsErrors) NSLog(@"%@", errorMessage); 145 | return 0; 146 | #endif 147 | } 148 | 149 | - (void)setApplicationID:(uint32_t)appID { 150 | #if SQLITE_VERSION_NUMBER >= 3007017 151 | NSString *query = [NSString stringWithFormat:@"pragma application_id=%d", appID]; 152 | FMResultSet *rs = [self executeQuery:query]; 153 | [rs next]; 154 | [rs close]; 155 | #else 156 | NSString *errorMessage = NSLocalizedStringFromTable(@"Application ID functions require SQLite 3.7.17", @"FMDB", nil); 157 | if (self.logsErrors) NSLog(@"%@", errorMessage); 158 | #endif 159 | } 160 | 161 | 162 | #if TARGET_OS_MAC && !TARGET_OS_IPHONE 163 | 164 | - (NSString*)applicationIDString { 165 | #if SQLITE_VERSION_NUMBER >= 3007017 166 | NSString *s = NSFileTypeForHFSTypeCode([self applicationID]); 167 | 168 | assert([s length] == 6); 169 | 170 | s = [s substringWithRange:NSMakeRange(1, 4)]; 171 | 172 | 173 | return s; 174 | #else 175 | NSString *errorMessage = NSLocalizedStringFromTable(@"Application ID functions require SQLite 3.7.17", @"FMDB", nil); 176 | if (self.logsErrors) NSLog(@"%@", errorMessage); 177 | return nil; 178 | #endif 179 | } 180 | 181 | - (void)setApplicationIDString:(NSString*)s { 182 | #if SQLITE_VERSION_NUMBER >= 3007017 183 | if ([s length] != 4) { 184 | NSLog(@"setApplicationIDString: string passed is not exactly 4 chars long. (was %ld)", [s length]); 185 | } 186 | 187 | [self setApplicationID:NSHFSTypeCodeFromFileType([NSString stringWithFormat:@"'%@'", s])]; 188 | #else 189 | NSString *errorMessage = NSLocalizedStringFromTable(@"Application ID functions require SQLite 3.7.17", @"FMDB", nil); 190 | if (self.logsErrors) NSLog(@"%@", errorMessage); 191 | #endif 192 | } 193 | 194 | #endif 195 | 196 | - (uint32_t)userVersion { 197 | uint32_t r = 0; 198 | 199 | FMResultSet *rs = [self executeQuery:@"pragma user_version"]; 200 | 201 | if ([rs next]) { 202 | r = (uint32_t)[rs longLongIntForColumnIndex:0]; 203 | } 204 | 205 | [rs close]; 206 | return r; 207 | } 208 | 209 | - (void)setUserVersion:(uint32_t)version { 210 | NSString *query = [NSString stringWithFormat:@"pragma user_version = %d", version]; 211 | FMResultSet *rs = [self executeQuery:query]; 212 | [rs next]; 213 | [rs close]; 214 | } 215 | 216 | #pragma clang diagnostic push 217 | #pragma clang diagnostic ignored "-Wdeprecated-implementations" 218 | 219 | - (BOOL)columnExists:(NSString*)tableName columnName:(NSString*)columnName __attribute__ ((deprecated)) { 220 | return [self columnExists:columnName inTableWithName:tableName]; 221 | } 222 | 223 | #pragma clang diagnostic pop 224 | 225 | - (BOOL)validateSQL:(NSString*)sql error:(NSError**)error { 226 | sqlite3_stmt *pStmt = NULL; 227 | BOOL validationSucceeded = YES; 228 | 229 | int rc = sqlite3_prepare_v2([self sqliteHandle], [sql UTF8String], -1, &pStmt, 0); 230 | if (rc != SQLITE_OK) { 231 | validationSucceeded = NO; 232 | if (error) { 233 | *error = [NSError errorWithDomain:NSCocoaErrorDomain 234 | code:[self lastErrorCode] 235 | userInfo:[NSDictionary dictionaryWithObject:[self lastErrorMessage] 236 | forKey:NSLocalizedDescriptionKey]]; 237 | } 238 | } 239 | 240 | sqlite3_finalize(pStmt); 241 | 242 | return validationSucceeded; 243 | } 244 | 245 | @end 246 | -------------------------------------------------------------------------------- /Pods/FMDB/src/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 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @class FMDatabase; 14 | 15 | /** Pool of `` objects. 16 | 17 | ### See also 18 | 19 | - `` 20 | - `` 21 | 22 | @warning Before using `FMDatabasePool`, please consider using `` instead. 23 | 24 | If you really really really know what you're doing and `FMDatabasePool` is what 25 | you really really need (ie, you're using a read only database), OK you can use 26 | it. But just be careful not to deadlock! 27 | 28 | For an example on deadlocking, search for: 29 | `ONLY_USE_THE_POOL_IF_YOU_ARE_DOING_READS_OTHERWISE_YOULL_DEADLOCK_USE_FMDATABASEQUEUE_INSTEAD` 30 | in the main.m file. 31 | */ 32 | 33 | @interface FMDatabasePool : NSObject 34 | 35 | /** Database path */ 36 | 37 | @property (atomic, copy, nullable) NSString *path; 38 | 39 | /** Delegate object */ 40 | 41 | @property (atomic, assign, nullable) id delegate; 42 | 43 | /** Maximum number of databases to create */ 44 | 45 | @property (atomic, assign) NSUInteger maximumNumberOfDatabasesToCreate; 46 | 47 | /** Open flags */ 48 | 49 | @property (atomic, readonly) int openFlags; 50 | 51 | /** Custom virtual file system name */ 52 | 53 | @property (atomic, copy, nullable) NSString *vfsName; 54 | 55 | 56 | ///--------------------- 57 | /// @name Initialization 58 | ///--------------------- 59 | 60 | /** Create pool using path. 61 | 62 | @param aPath The file path of the database. 63 | 64 | @return The `FMDatabasePool` object. `nil` on error. 65 | */ 66 | 67 | + (instancetype)databasePoolWithPath:(NSString * _Nullable)aPath; 68 | 69 | /** Create pool using file URL. 70 | 71 | @param url The file `NSURL` of the database. 72 | 73 | @return The `FMDatabasePool` object. `nil` on error. 74 | */ 75 | 76 | + (instancetype)databasePoolWithURL:(NSURL * _Nullable)url; 77 | 78 | /** Create pool using path and specified flags 79 | 80 | @param aPath The file path of the database. 81 | @param openFlags Flags passed to the openWithFlags method of the database. 82 | 83 | @return The `FMDatabasePool` object. `nil` on error. 84 | */ 85 | 86 | + (instancetype)databasePoolWithPath:(NSString * _Nullable)aPath flags:(int)openFlags; 87 | 88 | /** Create pool using file URL and specified flags 89 | 90 | @param url The file `NSURL` of the database. 91 | @param openFlags Flags passed to the openWithFlags method of the database. 92 | 93 | @return The `FMDatabasePool` object. `nil` on error. 94 | */ 95 | 96 | + (instancetype)databasePoolWithURL:(NSURL * _Nullable)url flags:(int)openFlags; 97 | 98 | /** Create pool using path. 99 | 100 | @param aPath The file path of the database. 101 | 102 | @return The `FMDatabasePool` object. `nil` on error. 103 | */ 104 | 105 | - (instancetype)initWithPath:(NSString * _Nullable)aPath; 106 | 107 | /** Create pool using file URL. 108 | 109 | @param url The file `NSURL of the database. 110 | 111 | @return The `FMDatabasePool` object. `nil` on error. 112 | */ 113 | 114 | - (instancetype)initWithURL:(NSURL * _Nullable)url; 115 | 116 | /** Create pool using path and specified flags. 117 | 118 | @param aPath The file path of the database. 119 | @param openFlags Flags passed to the openWithFlags method of the database 120 | 121 | @return The `FMDatabasePool` object. `nil` on error. 122 | */ 123 | 124 | - (instancetype)initWithPath:(NSString * _Nullable)aPath flags:(int)openFlags; 125 | 126 | /** Create pool using file URL and specified flags. 127 | 128 | @param url The file `NSURL` of the database. 129 | @param openFlags Flags passed to the openWithFlags method of the database 130 | 131 | @return The `FMDatabasePool` object. `nil` on error. 132 | */ 133 | 134 | - (instancetype)initWithURL:(NSURL * _Nullable)url flags:(int)openFlags; 135 | 136 | /** Create pool using path and specified flags. 137 | 138 | @param aPath The file path of the database. 139 | @param openFlags Flags passed to the openWithFlags method of the database 140 | @param vfsName The name of a custom virtual file system 141 | 142 | @return The `FMDatabasePool` object. `nil` on error. 143 | */ 144 | 145 | - (instancetype)initWithPath:(NSString * _Nullable)aPath flags:(int)openFlags vfs:(NSString * _Nullable)vfsName; 146 | 147 | /** Create pool using file URL and specified flags. 148 | 149 | @param url The file `NSURL` of the database. 150 | @param openFlags Flags passed to the openWithFlags method of the database 151 | @param vfsName The name of a custom virtual file system 152 | 153 | @return The `FMDatabasePool` object. `nil` on error. 154 | */ 155 | 156 | - (instancetype)initWithURL:(NSURL * _Nullable)url flags:(int)openFlags vfs:(NSString * _Nullable)vfsName; 157 | 158 | /** Returns the Class of 'FMDatabase' subclass, that will be used to instantiate database object. 159 | 160 | Subclasses can override this method to return specified Class of 'FMDatabase' subclass. 161 | 162 | @return The Class of 'FMDatabase' subclass, that will be used to instantiate database object. 163 | */ 164 | 165 | + (Class)databaseClass; 166 | 167 | ///------------------------------------------------ 168 | /// @name Keeping track of checked in/out databases 169 | ///------------------------------------------------ 170 | 171 | /** Number of checked-in databases in pool 172 | */ 173 | 174 | @property (nonatomic, readonly) NSUInteger countOfCheckedInDatabases; 175 | 176 | /** Number of checked-out databases in pool 177 | */ 178 | 179 | @property (nonatomic, readonly) NSUInteger countOfCheckedOutDatabases; 180 | 181 | /** Total number of databases in pool 182 | */ 183 | 184 | @property (nonatomic, readonly) NSUInteger countOfOpenDatabases; 185 | 186 | /** Release all databases in pool */ 187 | 188 | - (void)releaseAllDatabases; 189 | 190 | ///------------------------------------------ 191 | /// @name Perform database operations in pool 192 | ///------------------------------------------ 193 | 194 | /** Synchronously perform database operations in pool. 195 | 196 | @param block The code to be run on the `FMDatabasePool` pool. 197 | */ 198 | 199 | - (void)inDatabase:(__attribute__((noescape)) void (^)(FMDatabase *db))block; 200 | 201 | /** Synchronously perform database operations in pool using transaction. 202 | 203 | @param block The code to be run on the `FMDatabasePool` pool. 204 | 205 | @warning Unlike SQLite's `BEGIN TRANSACTION`, this method currently performs 206 | an exclusive transaction, not a deferred transaction. This behavior 207 | is likely to change in future versions of FMDB, whereby this method 208 | will likely eventually adopt standard SQLite behavior and perform 209 | deferred transactions. If you really need exclusive tranaction, it is 210 | recommended that you use `inExclusiveTransaction`, instead, not only 211 | to make your intent explicit, but also to future-proof your code. 212 | */ 213 | 214 | - (void)inTransaction:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block; 215 | 216 | /** Synchronously perform database operations in pool using exclusive transaction. 217 | 218 | @param block The code to be run on the `FMDatabasePool` pool. 219 | */ 220 | 221 | - (void)inExclusiveTransaction:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block; 222 | 223 | /** Synchronously perform database operations in pool using deferred transaction. 224 | 225 | @param block The code to be run on the `FMDatabasePool` pool. 226 | */ 227 | 228 | - (void)inDeferredTransaction:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block; 229 | 230 | /** Synchronously perform database operations on queue, using immediate transactions. 231 | 232 | @param block The code to be run on the queue of `FMDatabaseQueue` 233 | */ 234 | 235 | - (void)inImmediateTransaction:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block; 236 | 237 | /** Synchronously perform database operations in pool using save point. 238 | 239 | @param block The code to be run on the `FMDatabasePool` pool. 240 | 241 | @return `NSError` object if error; `nil` if successful. 242 | 243 | @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. 244 | */ 245 | 246 | - (NSError * _Nullable)inSavePoint:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block; 247 | 248 | @end 249 | 250 | 251 | /** FMDatabasePool delegate category 252 | 253 | This is a category that defines the protocol for the FMDatabasePool delegate 254 | */ 255 | 256 | @interface NSObject (FMDatabasePoolDelegate) 257 | 258 | /** Asks the delegate whether database should be added to the pool. 259 | 260 | @param pool The `FMDatabasePool` object. 261 | @param database The `FMDatabase` object. 262 | 263 | @return `YES` if it should add database to pool; `NO` if not. 264 | 265 | */ 266 | 267 | - (BOOL)databasePool:(FMDatabasePool*)pool shouldAddDatabaseToPool:(FMDatabase*)database; 268 | 269 | /** Tells the delegate that database was added to the pool. 270 | 271 | @param pool The `FMDatabasePool` object. 272 | @param database The `FMDatabase` object. 273 | 274 | */ 275 | 276 | - (void)databasePool:(FMDatabasePool*)pool didAddDatabase:(FMDatabase*)database; 277 | 278 | @end 279 | 280 | NS_ASSUME_NONNULL_END 281 | -------------------------------------------------------------------------------- /Pods/FMDB/src/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 | #if FMDB_SQLITE_STANDALONE 10 | #import 11 | #else 12 | #import 13 | #endif 14 | 15 | #import "FMDatabasePool.h" 16 | #import "FMDatabase.h" 17 | 18 | typedef NS_ENUM(NSInteger, FMDBTransaction) { 19 | FMDBTransactionExclusive, 20 | FMDBTransactionDeferred, 21 | FMDBTransactionImmediate, 22 | }; 23 | 24 | @interface FMDatabasePool () { 25 | dispatch_queue_t _lockQueue; 26 | 27 | NSMutableArray *_databaseInPool; 28 | NSMutableArray *_databaseOutPool; 29 | } 30 | 31 | - (void)pushDatabaseBackInPool:(FMDatabase*)db; 32 | - (FMDatabase*)db; 33 | 34 | @end 35 | 36 | 37 | @implementation FMDatabasePool 38 | @synthesize path=_path; 39 | @synthesize delegate=_delegate; 40 | @synthesize maximumNumberOfDatabasesToCreate=_maximumNumberOfDatabasesToCreate; 41 | @synthesize openFlags=_openFlags; 42 | 43 | 44 | + (instancetype)databasePoolWithPath:(NSString *)aPath { 45 | return FMDBReturnAutoreleased([[self alloc] initWithPath:aPath]); 46 | } 47 | 48 | + (instancetype)databasePoolWithURL:(NSURL *)url { 49 | return FMDBReturnAutoreleased([[self alloc] initWithPath:url.path]); 50 | } 51 | 52 | + (instancetype)databasePoolWithPath:(NSString *)aPath flags:(int)openFlags { 53 | return FMDBReturnAutoreleased([[self alloc] initWithPath:aPath flags:openFlags]); 54 | } 55 | 56 | + (instancetype)databasePoolWithURL:(NSURL *)url flags:(int)openFlags { 57 | return FMDBReturnAutoreleased([[self alloc] initWithPath:url.path flags:openFlags]); 58 | } 59 | 60 | - (instancetype)initWithURL:(NSURL *)url flags:(int)openFlags vfs:(NSString *)vfsName { 61 | return [self initWithPath:url.path flags:openFlags vfs:vfsName]; 62 | } 63 | 64 | - (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags vfs:(NSString *)vfsName { 65 | 66 | self = [super init]; 67 | 68 | if (self != nil) { 69 | _path = [aPath copy]; 70 | _lockQueue = dispatch_queue_create([[NSString stringWithFormat:@"fmdb.%@", self] UTF8String], NULL); 71 | _databaseInPool = FMDBReturnRetained([NSMutableArray array]); 72 | _databaseOutPool = FMDBReturnRetained([NSMutableArray array]); 73 | _openFlags = openFlags; 74 | _vfsName = [vfsName copy]; 75 | } 76 | 77 | return self; 78 | } 79 | 80 | - (instancetype)initWithPath:(NSString *)aPath flags:(int)openFlags { 81 | return [self initWithPath:aPath flags:openFlags vfs:nil]; 82 | } 83 | 84 | - (instancetype)initWithURL:(NSURL *)url flags:(int)openFlags { 85 | return [self initWithPath:url.path flags:openFlags vfs:nil]; 86 | } 87 | 88 | - (instancetype)initWithPath:(NSString*)aPath { 89 | // default flags for sqlite3_open 90 | return [self initWithPath:aPath flags:SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE]; 91 | } 92 | 93 | - (instancetype)initWithURL:(NSURL *)url { 94 | return [self initWithPath:url.path]; 95 | } 96 | 97 | - (instancetype)init { 98 | return [self initWithPath:nil]; 99 | } 100 | 101 | + (Class)databaseClass { 102 | return [FMDatabase class]; 103 | } 104 | 105 | - (void)dealloc { 106 | 107 | _delegate = 0x00; 108 | FMDBRelease(_path); 109 | FMDBRelease(_databaseInPool); 110 | FMDBRelease(_databaseOutPool); 111 | FMDBRelease(_vfsName); 112 | 113 | if (_lockQueue) { 114 | FMDBDispatchQueueRelease(_lockQueue); 115 | _lockQueue = 0x00; 116 | } 117 | #if ! __has_feature(objc_arc) 118 | [super dealloc]; 119 | #endif 120 | } 121 | 122 | 123 | - (void)executeLocked:(void (^)(void))aBlock { 124 | dispatch_sync(_lockQueue, aBlock); 125 | } 126 | 127 | - (void)pushDatabaseBackInPool:(FMDatabase*)db { 128 | 129 | if (!db) { // db can be null if we set an upper bound on the # of databases to create. 130 | return; 131 | } 132 | 133 | [self executeLocked:^() { 134 | 135 | if ([self->_databaseInPool containsObject:db]) { 136 | [[NSException exceptionWithName:@"Database already in pool" reason:@"The FMDatabase being put back into the pool is already present in the pool" userInfo:nil] raise]; 137 | } 138 | 139 | [self->_databaseInPool addObject:db]; 140 | [self->_databaseOutPool removeObject:db]; 141 | 142 | }]; 143 | } 144 | 145 | - (FMDatabase*)db { 146 | 147 | __block FMDatabase *db; 148 | 149 | 150 | [self executeLocked:^() { 151 | db = [self->_databaseInPool lastObject]; 152 | 153 | BOOL shouldNotifyDelegate = NO; 154 | 155 | if (db) { 156 | [self->_databaseOutPool addObject:db]; 157 | [self->_databaseInPool removeLastObject]; 158 | } 159 | else { 160 | 161 | if (self->_maximumNumberOfDatabasesToCreate) { 162 | NSUInteger currentCount = [self->_databaseOutPool count] + [self->_databaseInPool count]; 163 | 164 | if (currentCount >= self->_maximumNumberOfDatabasesToCreate) { 165 | NSLog(@"Maximum number of databases (%ld) has already been reached!", (long)currentCount); 166 | return; 167 | } 168 | } 169 | 170 | db = [[[self class] databaseClass] databaseWithPath:self->_path]; 171 | shouldNotifyDelegate = YES; 172 | } 173 | 174 | //This ensures that the db is opened before returning 175 | #if SQLITE_VERSION_NUMBER >= 3005000 176 | BOOL success = [db openWithFlags:self->_openFlags vfs:self->_vfsName]; 177 | #else 178 | BOOL success = [db open]; 179 | #endif 180 | if (success) { 181 | if ([self->_delegate respondsToSelector:@selector(databasePool:shouldAddDatabaseToPool:)] && ![self->_delegate databasePool:self shouldAddDatabaseToPool:db]) { 182 | [db close]; 183 | db = 0x00; 184 | } 185 | else { 186 | //It should not get added in the pool twice if lastObject was found 187 | if (![self->_databaseOutPool containsObject:db]) { 188 | [self->_databaseOutPool addObject:db]; 189 | 190 | if (shouldNotifyDelegate && [self->_delegate respondsToSelector:@selector(databasePool:didAddDatabase:)]) { 191 | [self->_delegate databasePool:self didAddDatabase:db]; 192 | } 193 | } 194 | } 195 | } 196 | else { 197 | NSLog(@"Could not open up the database at path %@", self->_path); 198 | db = 0x00; 199 | } 200 | }]; 201 | 202 | return db; 203 | } 204 | 205 | - (NSUInteger)countOfCheckedInDatabases { 206 | 207 | __block NSUInteger count; 208 | 209 | [self executeLocked:^() { 210 | count = [self->_databaseInPool count]; 211 | }]; 212 | 213 | return count; 214 | } 215 | 216 | - (NSUInteger)countOfCheckedOutDatabases { 217 | 218 | __block NSUInteger count; 219 | 220 | [self executeLocked:^() { 221 | count = [self->_databaseOutPool count]; 222 | }]; 223 | 224 | return count; 225 | } 226 | 227 | - (NSUInteger)countOfOpenDatabases { 228 | __block NSUInteger count; 229 | 230 | [self executeLocked:^() { 231 | count = [self->_databaseOutPool count] + [self->_databaseInPool count]; 232 | }]; 233 | 234 | return count; 235 | } 236 | 237 | - (void)releaseAllDatabases { 238 | [self executeLocked:^() { 239 | [self->_databaseOutPool removeAllObjects]; 240 | [self->_databaseInPool removeAllObjects]; 241 | }]; 242 | } 243 | 244 | - (void)inDatabase:(__attribute__((noescape)) void (^)(FMDatabase *db))block { 245 | 246 | FMDatabase *db = [self db]; 247 | 248 | block(db); 249 | 250 | [self pushDatabaseBackInPool:db]; 251 | } 252 | 253 | - (void)beginTransaction:(FMDBTransaction)transaction withBlock:(void (^)(FMDatabase *db, BOOL *rollback))block { 254 | 255 | BOOL shouldRollback = NO; 256 | 257 | FMDatabase *db = [self db]; 258 | 259 | switch (transaction) { 260 | case FMDBTransactionExclusive: 261 | [db beginTransaction]; 262 | break; 263 | case FMDBTransactionDeferred: 264 | [db beginDeferredTransaction]; 265 | break; 266 | case FMDBTransactionImmediate: 267 | [db beginImmediateTransaction]; 268 | break; 269 | } 270 | 271 | 272 | block(db, &shouldRollback); 273 | 274 | if (shouldRollback) { 275 | [db rollback]; 276 | } 277 | else { 278 | [db commit]; 279 | } 280 | 281 | [self pushDatabaseBackInPool:db]; 282 | } 283 | 284 | - (void)inTransaction:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block { 285 | [self beginTransaction:FMDBTransactionExclusive withBlock:block]; 286 | } 287 | 288 | - (void)inDeferredTransaction:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block { 289 | [self beginTransaction:FMDBTransactionDeferred withBlock:block]; 290 | } 291 | 292 | - (void)inExclusiveTransaction:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block { 293 | [self beginTransaction:FMDBTransactionExclusive withBlock:block]; 294 | } 295 | 296 | - (void)inImmediateTransaction:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block { 297 | [self beginTransaction:FMDBTransactionImmediate withBlock:block]; 298 | } 299 | 300 | - (NSError*)inSavePoint:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block { 301 | #if SQLITE_VERSION_NUMBER >= 3007000 302 | static unsigned long savePointIdx = 0; 303 | 304 | NSString *name = [NSString stringWithFormat:@"savePoint%ld", savePointIdx++]; 305 | 306 | BOOL shouldRollback = NO; 307 | 308 | FMDatabase *db = [self db]; 309 | 310 | NSError *err = 0x00; 311 | 312 | if (![db startSavePointWithName:name error:&err]) { 313 | [self pushDatabaseBackInPool:db]; 314 | return err; 315 | } 316 | 317 | block(db, &shouldRollback); 318 | 319 | if (shouldRollback) { 320 | // We need to rollback and release this savepoint to remove it 321 | [db rollbackToSavePointWithName:name error:&err]; 322 | } 323 | [db releaseSavePointWithName:name error:&err]; 324 | 325 | [self pushDatabaseBackInPool:db]; 326 | 327 | return err; 328 | #else 329 | NSString *errorMessage = NSLocalizedStringFromTable(@"Save point functions require SQLite 3.7", @"FMDB", nil); 330 | if (self.logsErrors) NSLog(@"%@", errorMessage); 331 | return [NSError errorWithDomain:@"FMDatabase" code:0 userInfo:@{NSLocalizedDescriptionKey : errorMessage}]; 332 | #endif 333 | } 334 | 335 | @end 336 | -------------------------------------------------------------------------------- /Pods/FMDB/src/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 "FMDatabase.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 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 | /** Path of database */ 66 | 67 | @property (atomic, retain, nullable) NSString *path; 68 | 69 | /** Open flags */ 70 | 71 | @property (atomic, readonly) int openFlags; 72 | 73 | /** Custom virtual file system name */ 74 | 75 | @property (atomic, copy, nullable) NSString *vfsName; 76 | 77 | ///---------------------------------------------------- 78 | /// @name Initialization, opening, and closing of queue 79 | ///---------------------------------------------------- 80 | 81 | /** Create queue using path. 82 | 83 | @param aPath The file path of the database. 84 | 85 | @return The `FMDatabaseQueue` object. `nil` on error. 86 | */ 87 | 88 | + (nullable instancetype)databaseQueueWithPath:(NSString * _Nullable)aPath; 89 | 90 | /** Create queue using file URL. 91 | 92 | @param url The file `NSURL` of the database. 93 | 94 | @return The `FMDatabaseQueue` object. `nil` on error. 95 | */ 96 | 97 | + (nullable instancetype)databaseQueueWithURL:(NSURL * _Nullable)url; 98 | 99 | /** Create queue using path and specified flags. 100 | 101 | @param aPath The file path of the database. 102 | @param openFlags Flags passed to the openWithFlags method of the database. 103 | 104 | @return The `FMDatabaseQueue` object. `nil` on error. 105 | */ 106 | + (nullable instancetype)databaseQueueWithPath:(NSString * _Nullable)aPath flags:(int)openFlags; 107 | 108 | /** Create queue using file URL and specified flags. 109 | 110 | @param url The file `NSURL` of the database. 111 | @param openFlags Flags passed to the openWithFlags method of the database. 112 | 113 | @return The `FMDatabaseQueue` object. `nil` on error. 114 | */ 115 | + (nullable instancetype)databaseQueueWithURL:(NSURL * _Nullable)url flags:(int)openFlags; 116 | 117 | /** Create queue using path. 118 | 119 | @param aPath The file path of the database. 120 | 121 | @return The `FMDatabaseQueue` object. `nil` on error. 122 | */ 123 | 124 | - (nullable instancetype)initWithPath:(NSString * _Nullable)aPath; 125 | 126 | /** Create queue using file URL. 127 | 128 | @param url The file `NSURL of the database. 129 | 130 | @return The `FMDatabaseQueue` object. `nil` on error. 131 | */ 132 | 133 | - (nullable instancetype)initWithURL:(NSURL * _Nullable)url; 134 | 135 | /** Create queue using path and specified flags. 136 | 137 | @param aPath The file path of the database. 138 | @param openFlags Flags passed to the openWithFlags method of the database. 139 | 140 | @return The `FMDatabaseQueue` object. `nil` on error. 141 | */ 142 | 143 | - (nullable instancetype)initWithPath:(NSString * _Nullable)aPath flags:(int)openFlags; 144 | 145 | /** Create queue using file URL and specified flags. 146 | 147 | @param url The file path of the database. 148 | @param openFlags Flags passed to the openWithFlags method of the database. 149 | 150 | @return The `FMDatabaseQueue` object. `nil` on error. 151 | */ 152 | 153 | - (nullable instancetype)initWithURL:(NSURL * _Nullable)url flags:(int)openFlags; 154 | 155 | /** Create queue using path and specified flags. 156 | 157 | @param aPath The file path of the database. 158 | @param openFlags Flags passed to the openWithFlags method of the database 159 | @param vfsName The name of a custom virtual file system 160 | 161 | @return The `FMDatabaseQueue` object. `nil` on error. 162 | */ 163 | 164 | - (nullable instancetype)initWithPath:(NSString * _Nullable)aPath flags:(int)openFlags vfs:(NSString * _Nullable)vfsName; 165 | 166 | /** Create queue using file URL and specified flags. 167 | 168 | @param url The file `NSURL of the database. 169 | @param openFlags Flags passed to the openWithFlags method of the database 170 | @param vfsName The name of a custom virtual file system 171 | 172 | @return The `FMDatabaseQueue` object. `nil` on error. 173 | */ 174 | 175 | - (nullable instancetype)initWithURL:(NSURL * _Nullable)url flags:(int)openFlags vfs:(NSString * _Nullable)vfsName; 176 | 177 | /** Returns the Class of 'FMDatabase' subclass, that will be used to instantiate database object. 178 | 179 | Subclasses can override this method to return specified Class of 'FMDatabase' subclass. 180 | 181 | @return The Class of 'FMDatabase' subclass, that will be used to instantiate database object. 182 | */ 183 | 184 | + (Class)databaseClass; 185 | 186 | /** Close database used by queue. */ 187 | 188 | - (void)close; 189 | 190 | /** Interupt pending database operation. */ 191 | 192 | - (void)interrupt; 193 | 194 | ///----------------------------------------------- 195 | /// @name Dispatching database operations to queue 196 | ///----------------------------------------------- 197 | 198 | /** Synchronously perform database operations on queue. 199 | 200 | @param block The code to be run on the queue of `FMDatabaseQueue` 201 | */ 202 | 203 | - (void)inDatabase:(__attribute__((noescape)) void (^)(FMDatabase *db))block; 204 | 205 | /** Synchronously perform database operations on queue, using transactions. 206 | 207 | @param block The code to be run on the queue of `FMDatabaseQueue` 208 | 209 | @warning Unlike SQLite's `BEGIN TRANSACTION`, this method currently performs 210 | an exclusive transaction, not a deferred transaction. This behavior 211 | is likely to change in future versions of FMDB, whereby this method 212 | will likely eventually adopt standard SQLite behavior and perform 213 | deferred transactions. If you really need exclusive tranaction, it is 214 | recommended that you use `inExclusiveTransaction`, instead, not only 215 | to make your intent explicit, but also to future-proof your code. 216 | 217 | */ 218 | 219 | - (void)inTransaction:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block; 220 | 221 | /** Synchronously perform database operations on queue, using deferred transactions. 222 | 223 | @param block The code to be run on the queue of `FMDatabaseQueue` 224 | */ 225 | 226 | - (void)inDeferredTransaction:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block; 227 | 228 | /** Synchronously perform database operations on queue, using exclusive transactions. 229 | 230 | @param block The code to be run on the queue of `FMDatabaseQueue` 231 | */ 232 | 233 | - (void)inExclusiveTransaction:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block; 234 | 235 | /** Synchronously perform database operations on queue, using immediate transactions. 236 | 237 | @param block The code to be run on the queue of `FMDatabaseQueue` 238 | */ 239 | 240 | - (void)inImmediateTransaction:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block; 241 | 242 | ///----------------------------------------------- 243 | /// @name Dispatching database operations to queue 244 | ///----------------------------------------------- 245 | 246 | /** Synchronously perform database operations using save point. 247 | 248 | @param block The code to be run on the queue of `FMDatabaseQueue` 249 | */ 250 | 251 | // NOTE: you can not nest these, since calling it will pull another database out of the pool and you'll get a deadlock. 252 | // If you need to nest, use FMDatabase's startSavePointWithName:error: instead. 253 | - (NSError * _Nullable)inSavePoint:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block; 254 | 255 | ///----------------- 256 | /// @name Checkpoint 257 | ///----------------- 258 | 259 | /** Performs a WAL checkpoint 260 | 261 | @param checkpointMode The checkpoint mode for sqlite3_wal_checkpoint_v2 262 | @param error The NSError corresponding to the error, if any. 263 | @return YES on success, otherwise NO. 264 | */ 265 | - (BOOL)checkpoint:(FMDBCheckpointMode)checkpointMode error:(NSError * _Nullable *)error; 266 | 267 | /** Performs a WAL checkpoint 268 | 269 | @param checkpointMode The checkpoint mode for sqlite3_wal_checkpoint_v2 270 | @param name The db name for sqlite3_wal_checkpoint_v2 271 | @param error The NSError corresponding to the error, if any. 272 | @return YES on success, otherwise NO. 273 | */ 274 | - (BOOL)checkpoint:(FMDBCheckpointMode)checkpointMode name:(NSString * _Nullable)name error:(NSError * _Nullable *)error; 275 | 276 | /** Performs a WAL checkpoint 277 | 278 | @param checkpointMode The checkpoint mode for sqlite3_wal_checkpoint_v2 279 | @param name The db name for sqlite3_wal_checkpoint_v2 280 | @param error The NSError corresponding to the error, if any. 281 | @param logFrameCount If not NULL, then this is set to the total number of frames in the log file or to -1 if the checkpoint could not run because of an error or because the database is not in WAL mode. 282 | @param checkpointCount If not NULL, then this is set to the total number of checkpointed frames in the log file (including any that were already checkpointed before the function was called) or to -1 if the checkpoint could not run due to an error or because the database is not in WAL mode. 283 | @return YES on success, otherwise NO. 284 | */ 285 | - (BOOL)checkpoint:(FMDBCheckpointMode)checkpointMode name:(NSString * _Nullable)name logFrameCount:(int * _Nullable)logFrameCount checkpointCount:(int * _Nullable)checkpointCount error:(NSError * _Nullable *)error; 286 | 287 | @end 288 | 289 | NS_ASSUME_NONNULL_END 290 | -------------------------------------------------------------------------------- /Pods/FMDB/src/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 | #if FMDB_SQLITE_STANDALONE 13 | #import 14 | #else 15 | #import 16 | #endif 17 | 18 | typedef NS_ENUM(NSInteger, FMDBTransaction) { 19 | FMDBTransactionExclusive, 20 | FMDBTransactionDeferred, 21 | FMDBTransactionImmediate, 22 | }; 23 | 24 | /* 25 | 26 | Note: we call [self retain]; before using dispatch_sync, just incase 27 | FMDatabaseQueue is released on another thread and we're in the middle of doing 28 | something in dispatch_sync 29 | 30 | */ 31 | 32 | /* 33 | * A key used to associate the FMDatabaseQueue object with the dispatch_queue_t it uses. 34 | * This in turn is used for deadlock detection by seeing if inDatabase: is called on 35 | * the queue's dispatch queue, which should not happen and causes a deadlock. 36 | */ 37 | static const void * const kDispatchQueueSpecificKey = &kDispatchQueueSpecificKey; 38 | 39 | @interface FMDatabaseQueue () { 40 | dispatch_queue_t _queue; 41 | FMDatabase *_db; 42 | } 43 | @end 44 | 45 | @implementation FMDatabaseQueue 46 | 47 | + (instancetype)databaseQueueWithPath:(NSString *)aPath { 48 | FMDatabaseQueue *q = [[self alloc] initWithPath:aPath]; 49 | 50 | FMDBAutorelease(q); 51 | 52 | return q; 53 | } 54 | 55 | + (instancetype)databaseQueueWithURL:(NSURL *)url { 56 | return [self databaseQueueWithPath:url.path]; 57 | } 58 | 59 | + (instancetype)databaseQueueWithPath:(NSString *)aPath flags:(int)openFlags { 60 | FMDatabaseQueue *q = [[self alloc] initWithPath:aPath flags:openFlags]; 61 | 62 | FMDBAutorelease(q); 63 | 64 | return q; 65 | } 66 | 67 | + (instancetype)databaseQueueWithURL:(NSURL *)url flags:(int)openFlags { 68 | return [self databaseQueueWithPath:url.path flags:openFlags]; 69 | } 70 | 71 | + (Class)databaseClass { 72 | return [FMDatabase class]; 73 | } 74 | 75 | - (instancetype)initWithURL:(NSURL *)url flags:(int)openFlags vfs:(NSString *)vfsName { 76 | return [self initWithPath:url.path flags:openFlags vfs:vfsName]; 77 | } 78 | 79 | - (instancetype)initWithPath:(NSString*)aPath flags:(int)openFlags vfs:(NSString *)vfsName { 80 | self = [super init]; 81 | 82 | if (self != nil) { 83 | 84 | _db = [[[self class] databaseClass] databaseWithPath:aPath]; 85 | FMDBRetain(_db); 86 | 87 | #if SQLITE_VERSION_NUMBER >= 3005000 88 | BOOL success = [_db openWithFlags:openFlags vfs:vfsName]; 89 | #else 90 | BOOL success = [_db open]; 91 | #endif 92 | if (!success) { 93 | NSLog(@"Could not create database queue for path %@", aPath); 94 | FMDBRelease(self); 95 | return 0x00; 96 | } 97 | 98 | _path = FMDBReturnRetained(aPath); 99 | 100 | _queue = dispatch_queue_create([[NSString stringWithFormat:@"fmdb.%@", self] UTF8String], NULL); 101 | dispatch_queue_set_specific(_queue, kDispatchQueueSpecificKey, (__bridge void *)self, NULL); 102 | _openFlags = openFlags; 103 | _vfsName = [vfsName copy]; 104 | } 105 | 106 | return self; 107 | } 108 | 109 | - (instancetype)initWithPath:(NSString *)aPath flags:(int)openFlags { 110 | return [self initWithPath:aPath flags:openFlags vfs:nil]; 111 | } 112 | 113 | - (instancetype)initWithURL:(NSURL *)url flags:(int)openFlags { 114 | return [self initWithPath:url.path flags:openFlags vfs:nil]; 115 | } 116 | 117 | - (instancetype)initWithURL:(NSURL *)url { 118 | return [self initWithPath:url.path]; 119 | } 120 | 121 | - (instancetype)initWithPath:(NSString *)aPath { 122 | // default flags for sqlite3_open 123 | return [self initWithPath:aPath flags:SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE vfs:nil]; 124 | } 125 | 126 | - (instancetype)init { 127 | return [self initWithPath:nil]; 128 | } 129 | 130 | - (void)dealloc { 131 | FMDBRelease(_db); 132 | FMDBRelease(_path); 133 | FMDBRelease(_vfsName); 134 | 135 | if (_queue) { 136 | FMDBDispatchQueueRelease(_queue); 137 | _queue = 0x00; 138 | } 139 | #if ! __has_feature(objc_arc) 140 | [super dealloc]; 141 | #endif 142 | } 143 | 144 | - (void)close { 145 | FMDBRetain(self); 146 | dispatch_sync(_queue, ^() { 147 | [self->_db close]; 148 | FMDBRelease(_db); 149 | self->_db = 0x00; 150 | }); 151 | FMDBRelease(self); 152 | } 153 | 154 | - (void)interrupt { 155 | [[self database] interrupt]; 156 | } 157 | 158 | - (FMDatabase*)database { 159 | if (![_db isOpen]) { 160 | if (!_db) { 161 | _db = FMDBReturnRetained([[[self class] databaseClass] databaseWithPath:_path]); 162 | } 163 | 164 | #if SQLITE_VERSION_NUMBER >= 3005000 165 | BOOL success = [_db openWithFlags:_openFlags vfs:_vfsName]; 166 | #else 167 | BOOL success = [_db open]; 168 | #endif 169 | if (!success) { 170 | NSLog(@"FMDatabaseQueue could not reopen database for path %@", _path); 171 | FMDBRelease(_db); 172 | _db = 0x00; 173 | return 0x00; 174 | } 175 | } 176 | 177 | return _db; 178 | } 179 | 180 | - (void)inDatabase:(__attribute__((noescape)) void (^)(FMDatabase *db))block { 181 | #ifndef NDEBUG 182 | /* Get the currently executing queue (which should probably be nil, but in theory could be another DB queue 183 | * and then check it against self to make sure we're not about to deadlock. */ 184 | FMDatabaseQueue *currentSyncQueue = (__bridge id)dispatch_get_specific(kDispatchQueueSpecificKey); 185 | assert(currentSyncQueue != self && "inDatabase: was called reentrantly on the same queue, which would lead to a deadlock"); 186 | #endif 187 | 188 | FMDBRetain(self); 189 | 190 | dispatch_sync(_queue, ^() { 191 | 192 | FMDatabase *db = [self database]; 193 | 194 | block(db); 195 | 196 | if ([db hasOpenResultSets]) { 197 | NSLog(@"Warning: there is at least one open result set around after performing [FMDatabaseQueue inDatabase:]"); 198 | 199 | #if defined(DEBUG) && DEBUG 200 | NSSet *openSetCopy = FMDBReturnAutoreleased([[db valueForKey:@"_openResultSets"] copy]); 201 | for (NSValue *rsInWrappedInATastyValueMeal in openSetCopy) { 202 | FMResultSet *rs = (FMResultSet *)[rsInWrappedInATastyValueMeal pointerValue]; 203 | NSLog(@"query: '%@'", [rs query]); 204 | } 205 | #endif 206 | } 207 | }); 208 | 209 | FMDBRelease(self); 210 | } 211 | 212 | - (void)beginTransaction:(FMDBTransaction)transaction withBlock:(void (^)(FMDatabase *db, BOOL *rollback))block { 213 | FMDBRetain(self); 214 | dispatch_sync(_queue, ^() { 215 | 216 | BOOL shouldRollback = NO; 217 | 218 | switch (transaction) { 219 | case FMDBTransactionExclusive: 220 | [[self database] beginTransaction]; 221 | break; 222 | case FMDBTransactionDeferred: 223 | [[self database] beginDeferredTransaction]; 224 | break; 225 | case FMDBTransactionImmediate: 226 | [[self database] beginImmediateTransaction]; 227 | break; 228 | } 229 | 230 | block([self database], &shouldRollback); 231 | 232 | if (shouldRollback) { 233 | [[self database] rollback]; 234 | } 235 | else { 236 | [[self database] commit]; 237 | } 238 | }); 239 | 240 | FMDBRelease(self); 241 | } 242 | 243 | - (void)inTransaction:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block { 244 | [self beginTransaction:FMDBTransactionExclusive withBlock:block]; 245 | } 246 | 247 | - (void)inDeferredTransaction:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block { 248 | [self beginTransaction:FMDBTransactionDeferred withBlock:block]; 249 | } 250 | 251 | - (void)inExclusiveTransaction:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block { 252 | [self beginTransaction:FMDBTransactionExclusive withBlock:block]; 253 | } 254 | 255 | - (void)inImmediateTransaction:(__attribute__((noescape)) void (^)(FMDatabase * _Nonnull, BOOL * _Nonnull))block { 256 | [self beginTransaction:FMDBTransactionImmediate withBlock:block]; 257 | } 258 | 259 | - (NSError*)inSavePoint:(__attribute__((noescape)) void (^)(FMDatabase *db, BOOL *rollback))block { 260 | #if SQLITE_VERSION_NUMBER >= 3007000 261 | static unsigned long savePointIdx = 0; 262 | __block NSError *err = 0x00; 263 | FMDBRetain(self); 264 | dispatch_sync(_queue, ^() { 265 | 266 | NSString *name = [NSString stringWithFormat:@"savePoint%ld", savePointIdx++]; 267 | 268 | BOOL shouldRollback = NO; 269 | 270 | if ([[self database] startSavePointWithName:name error:&err]) { 271 | 272 | block([self database], &shouldRollback); 273 | 274 | if (shouldRollback) { 275 | // We need to rollback and release this savepoint to remove it 276 | [[self database] rollbackToSavePointWithName:name error:&err]; 277 | } 278 | [[self database] releaseSavePointWithName:name error:&err]; 279 | 280 | } 281 | }); 282 | FMDBRelease(self); 283 | return err; 284 | #else 285 | NSString *errorMessage = NSLocalizedStringFromTable(@"Save point functions require SQLite 3.7", @"FMDB", nil); 286 | if (_db.logsErrors) NSLog(@"%@", errorMessage); 287 | return [NSError errorWithDomain:@"FMDatabase" code:0 userInfo:@{NSLocalizedDescriptionKey : errorMessage}]; 288 | #endif 289 | } 290 | 291 | - (BOOL)checkpoint:(FMDBCheckpointMode)mode error:(NSError * __autoreleasing *)error 292 | { 293 | return [self checkpoint:mode name:nil logFrameCount:NULL checkpointCount:NULL error:error]; 294 | } 295 | 296 | - (BOOL)checkpoint:(FMDBCheckpointMode)mode name:(NSString *)name error:(NSError * __autoreleasing *)error 297 | { 298 | return [self checkpoint:mode name:name logFrameCount:NULL checkpointCount:NULL error:error]; 299 | } 300 | 301 | - (BOOL)checkpoint:(FMDBCheckpointMode)mode name:(NSString *)name logFrameCount:(int * _Nullable)logFrameCount checkpointCount:(int * _Nullable)checkpointCount error:(NSError * __autoreleasing _Nullable * _Nullable)error 302 | { 303 | __block BOOL result; 304 | __block NSError *blockError; 305 | 306 | FMDBRetain(self); 307 | dispatch_sync(_queue, ^() { 308 | result = [self.database checkpoint:mode name:name logFrameCount:NULL checkpointCount:NULL error:&blockError]; 309 | }); 310 | FMDBRelease(self); 311 | 312 | if (error) { 313 | *error = blockError; 314 | } 315 | return result; 316 | } 317 | 318 | @end 319 | -------------------------------------------------------------------------------- /Pods/FMDB/src/fmdb/FMResultSet.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | NS_ASSUME_NONNULL_BEGIN 4 | 5 | #ifndef __has_feature // Optional. 6 | #define __has_feature(x) 0 // Compatibility with non-clang compilers. 7 | #endif 8 | 9 | #ifndef NS_RETURNS_NOT_RETAINED 10 | #if __has_feature(attribute_ns_returns_not_retained) 11 | #define NS_RETURNS_NOT_RETAINED __attribute__((ns_returns_not_retained)) 12 | #else 13 | #define NS_RETURNS_NOT_RETAINED 14 | #endif 15 | #endif 16 | 17 | @class FMDatabase; 18 | @class FMStatement; 19 | 20 | /** Represents the results of executing a query on an ``. 21 | 22 | ### See also 23 | 24 | - `` 25 | */ 26 | 27 | @interface FMResultSet : NSObject 28 | 29 | @property (nonatomic, retain, nullable) FMDatabase *parentDB; 30 | 31 | ///----------------- 32 | /// @name Properties 33 | ///----------------- 34 | 35 | /** Executed query */ 36 | 37 | @property (atomic, retain, nullable) NSString *query; 38 | 39 | /** `NSMutableDictionary` mapping column names to numeric index */ 40 | 41 | @property (readonly) NSMutableDictionary *columnNameToIndexMap; 42 | 43 | /** `FMStatement` used by result set. */ 44 | 45 | @property (atomic, retain, nullable) FMStatement *statement; 46 | 47 | ///------------------------------------ 48 | /// @name Creating and closing a result set 49 | ///------------------------------------ 50 | 51 | /** Create result set from `` 52 | 53 | @param statement A `` to be performed 54 | 55 | @param aDB A `` to be used 56 | 57 | @return A `FMResultSet` on success; `nil` on failure 58 | */ 59 | 60 | + (instancetype)resultSetWithStatement:(FMStatement *)statement usingParentDatabase:(FMDatabase*)aDB; 61 | 62 | /** Close result set */ 63 | 64 | - (void)close; 65 | 66 | ///--------------------------------------- 67 | /// @name Iterating through the result set 68 | ///--------------------------------------- 69 | 70 | /** Retrieve next row for result set. 71 | 72 | You must always invoke `next` or `nextWithError` before attempting to access the values returned in a query, even if you're only expecting one. 73 | 74 | @return `YES` if row successfully retrieved; `NO` if end of result set reached 75 | 76 | @see hasAnotherRow 77 | */ 78 | 79 | - (BOOL)next; 80 | 81 | /** Retrieve next row for result set. 82 | 83 | You must always invoke `next` or `nextWithError` before attempting to access the values returned in a query, even if you're only expecting one. 84 | 85 | @param outErr A 'NSError' object to receive any error object (if any). 86 | 87 | @return 'YES' if row successfully retrieved; 'NO' if end of result set reached 88 | 89 | @see hasAnotherRow 90 | */ 91 | 92 | - (BOOL)nextWithError:(NSError * _Nullable *)outErr; 93 | 94 | /** Did the last call to `` succeed in retrieving another row? 95 | 96 | @return `YES` if the last call to `` succeeded in retrieving another record; `NO` if not. 97 | 98 | @see next 99 | 100 | @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. 101 | */ 102 | 103 | - (BOOL)hasAnotherRow; 104 | 105 | ///--------------------------------------------- 106 | /// @name Retrieving information from result set 107 | ///--------------------------------------------- 108 | 109 | /** How many columns in result set 110 | 111 | @return Integer value of the number of columns. 112 | */ 113 | 114 | @property (nonatomic, readonly) int columnCount; 115 | 116 | /** Column index for column name 117 | 118 | @param columnName `NSString` value of the name of the column. 119 | 120 | @return Zero-based index for column. 121 | */ 122 | 123 | - (int)columnIndexForName:(NSString*)columnName; 124 | 125 | /** Column name for column index 126 | 127 | @param columnIdx Zero-based index for column. 128 | 129 | @return columnName `NSString` value of the name of the column. 130 | */ 131 | 132 | - (NSString * _Nullable)columnNameForIndex:(int)columnIdx; 133 | 134 | /** Result set integer value for column. 135 | 136 | @param columnName `NSString` value of the name of the column. 137 | 138 | @return `int` value of the result set's column. 139 | */ 140 | 141 | - (int)intForColumn:(NSString*)columnName; 142 | 143 | /** Result set integer value for column. 144 | 145 | @param columnIdx Zero-based index for column. 146 | 147 | @return `int` value of the result set's column. 148 | */ 149 | 150 | - (int)intForColumnIndex:(int)columnIdx; 151 | 152 | /** Result set `long` value for column. 153 | 154 | @param columnName `NSString` value of the name of the column. 155 | 156 | @return `long` value of the result set's column. 157 | */ 158 | 159 | - (long)longForColumn:(NSString*)columnName; 160 | 161 | /** Result set long value for column. 162 | 163 | @param columnIdx Zero-based index for column. 164 | 165 | @return `long` value of the result set's column. 166 | */ 167 | 168 | - (long)longForColumnIndex:(int)columnIdx; 169 | 170 | /** Result set `long long int` value for column. 171 | 172 | @param columnName `NSString` value of the name of the column. 173 | 174 | @return `long long int` value of the result set's column. 175 | */ 176 | 177 | - (long long int)longLongIntForColumn:(NSString*)columnName; 178 | 179 | /** Result set `long long int` value for column. 180 | 181 | @param columnIdx Zero-based index for column. 182 | 183 | @return `long long int` value of the result set's column. 184 | */ 185 | 186 | - (long long int)longLongIntForColumnIndex:(int)columnIdx; 187 | 188 | /** Result set `unsigned long long int` value for column. 189 | 190 | @param columnName `NSString` value of the name of the column. 191 | 192 | @return `unsigned long long int` value of the result set's column. 193 | */ 194 | 195 | - (unsigned long long int)unsignedLongLongIntForColumn:(NSString*)columnName; 196 | 197 | /** Result set `unsigned long long int` value for column. 198 | 199 | @param columnIdx Zero-based index for column. 200 | 201 | @return `unsigned long long int` value of the result set's column. 202 | */ 203 | 204 | - (unsigned long long int)unsignedLongLongIntForColumnIndex:(int)columnIdx; 205 | 206 | /** Result set `BOOL` value for column. 207 | 208 | @param columnName `NSString` value of the name of the column. 209 | 210 | @return `BOOL` value of the result set's column. 211 | */ 212 | 213 | - (BOOL)boolForColumn:(NSString*)columnName; 214 | 215 | /** Result set `BOOL` value for column. 216 | 217 | @param columnIdx Zero-based index for column. 218 | 219 | @return `BOOL` value of the result set's column. 220 | */ 221 | 222 | - (BOOL)boolForColumnIndex:(int)columnIdx; 223 | 224 | /** Result set `double` value for column. 225 | 226 | @param columnName `NSString` value of the name of the column. 227 | 228 | @return `double` value of the result set's column. 229 | 230 | */ 231 | 232 | - (double)doubleForColumn:(NSString*)columnName; 233 | 234 | /** Result set `double` value for column. 235 | 236 | @param columnIdx Zero-based index for column. 237 | 238 | @return `double` value of the result set's column. 239 | 240 | */ 241 | 242 | - (double)doubleForColumnIndex:(int)columnIdx; 243 | 244 | /** Result set `NSString` value for column. 245 | 246 | @param columnName `NSString` value of the name of the column. 247 | 248 | @return String value of the result set's column. 249 | 250 | */ 251 | 252 | - (NSString * _Nullable)stringForColumn:(NSString*)columnName; 253 | 254 | /** Result set `NSString` value for column. 255 | 256 | @param columnIdx Zero-based index for column. 257 | 258 | @return String value of the result set's column. 259 | */ 260 | 261 | - (NSString * _Nullable)stringForColumnIndex:(int)columnIdx; 262 | 263 | /** Result set `NSDate` value for column. 264 | 265 | @param columnName `NSString` value of the name of the column. 266 | 267 | @return Date value of the result set's column. 268 | */ 269 | 270 | - (NSDate * _Nullable)dateForColumn:(NSString*)columnName; 271 | 272 | /** Result set `NSDate` value for column. 273 | 274 | @param columnIdx Zero-based index for column. 275 | 276 | @return Date value of the result set's column. 277 | 278 | */ 279 | 280 | - (NSDate * _Nullable)dateForColumnIndex:(int)columnIdx; 281 | 282 | /** Result set `NSData` value for column. 283 | 284 | This is useful when storing binary data in table (such as image or the like). 285 | 286 | @param columnName `NSString` value of the name of the column. 287 | 288 | @return Data value of the result set's column. 289 | 290 | */ 291 | 292 | - (NSData * _Nullable)dataForColumn:(NSString*)columnName; 293 | 294 | /** Result set `NSData` value for column. 295 | 296 | @param columnIdx Zero-based index for column. 297 | 298 | @return Data value of the result set's column. 299 | */ 300 | 301 | - (NSData * _Nullable)dataForColumnIndex:(int)columnIdx; 302 | 303 | /** Result set `(const unsigned char *)` value for column. 304 | 305 | @param columnName `NSString` value of the name of the column. 306 | 307 | @return `(const unsigned char *)` value of the result set's column. 308 | */ 309 | 310 | - (const unsigned char * _Nullable)UTF8StringForColumn:(NSString*)columnName; 311 | 312 | - (const unsigned char * _Nullable)UTF8StringForColumnName:(NSString*)columnName __deprecated_msg("Use UTF8StringForColumn instead"); 313 | 314 | /** Result set `(const unsigned char *)` value for column. 315 | 316 | @param columnIdx Zero-based index for column. 317 | 318 | @return `(const unsigned char *)` value of the result set's column. 319 | */ 320 | 321 | - (const unsigned char * _Nullable)UTF8StringForColumnIndex:(int)columnIdx; 322 | 323 | /** Result set object for column. 324 | 325 | @param columnName Name of the column. 326 | 327 | @return Either `NSNumber`, `NSString`, `NSData`, or `NSNull`. If the column was `NULL`, this returns `[NSNull null]` object. 328 | 329 | @see objectForKeyedSubscript: 330 | */ 331 | 332 | - (id _Nullable)objectForColumn:(NSString*)columnName; 333 | 334 | - (id _Nullable)objectForColumnName:(NSString*)columnName __deprecated_msg("Use objectForColumn instead"); 335 | 336 | /** Result set object for column. 337 | 338 | @param columnIdx Zero-based index for column. 339 | 340 | @return Either `NSNumber`, `NSString`, `NSData`, or `NSNull`. If the column was `NULL`, this returns `[NSNull null]` object. 341 | 342 | @see objectAtIndexedSubscript: 343 | */ 344 | 345 | - (id _Nullable)objectForColumnIndex:(int)columnIdx; 346 | 347 | /** Result set object for column. 348 | 349 | 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: 350 | 351 | id result = rs[@"employee_name"]; 352 | 353 | This simplified syntax is equivalent to calling: 354 | 355 | id result = [rs objectForKeyedSubscript:@"employee_name"]; 356 | 357 | which is, it turns out, equivalent to calling: 358 | 359 | id result = [rs objectForColumnName:@"employee_name"]; 360 | 361 | @param columnName `NSString` value of the name of the column. 362 | 363 | @return Either `NSNumber`, `NSString`, `NSData`, or `NSNull`. If the column was `NULL`, this returns `[NSNull null]` object. 364 | */ 365 | 366 | - (id _Nullable)objectForKeyedSubscript:(NSString *)columnName; 367 | 368 | /** Result set object for column. 369 | 370 | 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: 371 | 372 | id result = rs[0]; 373 | 374 | This simplified syntax is equivalent to calling: 375 | 376 | id result = [rs objectForKeyedSubscript:0]; 377 | 378 | which is, it turns out, equivalent to calling: 379 | 380 | id result = [rs objectForColumnName:0]; 381 | 382 | @param columnIdx Zero-based index for column. 383 | 384 | @return Either `NSNumber`, `NSString`, `NSData`, or `NSNull`. If the column was `NULL`, this returns `[NSNull null]` object. 385 | */ 386 | 387 | - (id _Nullable)objectAtIndexedSubscript:(int)columnIdx; 388 | 389 | /** Result set `NSData` value for column. 390 | 391 | @param columnName `NSString` value of the name of the column. 392 | 393 | @return Data value of the result set's column. 394 | 395 | @warning If you are going to use this data after you iterate over the next row, or after you close the 396 | result set, make sure to make a copy of the data first (or just use ``/``) 397 | If you don't, you're going to be in a world of hurt when you try and use the data. 398 | 399 | */ 400 | 401 | - (NSData * _Nullable)dataNoCopyForColumn:(NSString *)columnName NS_RETURNS_NOT_RETAINED; 402 | 403 | /** Result set `NSData` value for column. 404 | 405 | @param columnIdx Zero-based index for column. 406 | 407 | @return Data value of the result set's column. 408 | 409 | @warning If you are going to use this data after you iterate over the next row, or after you close the 410 | result set, make sure to make a copy of the data first (or just use ``/``) 411 | If you don't, you're going to be in a world of hurt when you try and use the data. 412 | 413 | */ 414 | 415 | - (NSData * _Nullable)dataNoCopyForColumnIndex:(int)columnIdx NS_RETURNS_NOT_RETAINED; 416 | 417 | /** Is the column `NULL`? 418 | 419 | @param columnIdx Zero-based index for column. 420 | 421 | @return `YES` if column is `NULL`; `NO` if not `NULL`. 422 | */ 423 | 424 | - (BOOL)columnIndexIsNull:(int)columnIdx; 425 | 426 | /** Is the column `NULL`? 427 | 428 | @param columnName `NSString` value of the name of the column. 429 | 430 | @return `YES` if column is `NULL`; `NO` if not `NULL`. 431 | */ 432 | 433 | - (BOOL)columnIsNull:(NSString*)columnName; 434 | 435 | 436 | /** Returns a dictionary of the row results mapped to case sensitive keys of the column names. 437 | 438 | @warning The keys to the dictionary are case sensitive of the column names. 439 | */ 440 | 441 | @property (nonatomic, readonly, nullable) NSDictionary *resultDictionary; 442 | 443 | /** Returns a dictionary of the row results 444 | 445 | @see resultDictionary 446 | 447 | @warning **Deprecated**: Please use `` instead. Also, beware that `` is case sensitive! 448 | */ 449 | 450 | - (NSDictionary * _Nullable)resultDict __deprecated_msg("Use resultDictionary instead"); 451 | 452 | ///----------------------------- 453 | /// @name Key value coding magic 454 | ///----------------------------- 455 | 456 | /** Performs `setValue` to yield support for key value observing. 457 | 458 | @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. 459 | 460 | */ 461 | 462 | - (void)kvcMagic:(id)object; 463 | 464 | 465 | @end 466 | 467 | NS_ASSUME_NONNULL_END 468 | -------------------------------------------------------------------------------- /Pods/FMDB/src/fmdb/FMResultSet.m: -------------------------------------------------------------------------------- 1 | #import "FMResultSet.h" 2 | #import "FMDatabase.h" 3 | #import 4 | 5 | #if FMDB_SQLITE_STANDALONE 6 | #import 7 | #else 8 | #import 9 | #endif 10 | 11 | @interface FMDatabase () 12 | - (void)resultSetDidClose:(FMResultSet *)resultSet; 13 | @end 14 | 15 | @interface FMResultSet () { 16 | NSMutableDictionary *_columnNameToIndexMap; 17 | } 18 | @end 19 | 20 | @implementation FMResultSet 21 | 22 | + (instancetype)resultSetWithStatement:(FMStatement *)statement usingParentDatabase:(FMDatabase*)aDB { 23 | 24 | FMResultSet *rs = [[FMResultSet alloc] init]; 25 | 26 | [rs setStatement:statement]; 27 | [rs setParentDB:aDB]; 28 | 29 | NSParameterAssert(![statement inUse]); 30 | [statement setInUse:YES]; // weak reference 31 | 32 | return FMDBReturnAutoreleased(rs); 33 | } 34 | 35 | #if ! __has_feature(objc_arc) 36 | - (void)finalize { 37 | [self close]; 38 | [super finalize]; 39 | } 40 | #endif 41 | 42 | - (void)dealloc { 43 | [self close]; 44 | 45 | FMDBRelease(_query); 46 | _query = nil; 47 | 48 | FMDBRelease(_columnNameToIndexMap); 49 | _columnNameToIndexMap = nil; 50 | 51 | #if ! __has_feature(objc_arc) 52 | [super dealloc]; 53 | #endif 54 | } 55 | 56 | - (void)close { 57 | [_statement reset]; 58 | FMDBRelease(_statement); 59 | _statement = nil; 60 | 61 | // we don't need this anymore... (i think) 62 | //[_parentDB setInUse:NO]; 63 | [_parentDB resultSetDidClose:self]; 64 | [self setParentDB:nil]; 65 | } 66 | 67 | - (int)columnCount { 68 | return sqlite3_column_count([_statement statement]); 69 | } 70 | 71 | - (NSMutableDictionary *)columnNameToIndexMap { 72 | if (!_columnNameToIndexMap) { 73 | int columnCount = sqlite3_column_count([_statement statement]); 74 | _columnNameToIndexMap = [[NSMutableDictionary alloc] initWithCapacity:(NSUInteger)columnCount]; 75 | int columnIdx = 0; 76 | for (columnIdx = 0; columnIdx < columnCount; columnIdx++) { 77 | [_columnNameToIndexMap setObject:[NSNumber numberWithInt:columnIdx] 78 | forKey:[[NSString stringWithUTF8String:sqlite3_column_name([_statement statement], columnIdx)] lowercaseString]]; 79 | } 80 | } 81 | return _columnNameToIndexMap; 82 | } 83 | 84 | - (void)kvcMagic:(id)object { 85 | 86 | int columnCount = sqlite3_column_count([_statement statement]); 87 | 88 | int columnIdx = 0; 89 | for (columnIdx = 0; columnIdx < columnCount; columnIdx++) { 90 | 91 | const char *c = (const char *)sqlite3_column_text([_statement statement], columnIdx); 92 | 93 | // check for a null row 94 | if (c) { 95 | NSString *s = [NSString stringWithUTF8String:c]; 96 | 97 | [object setValue:s forKey:[NSString stringWithUTF8String:sqlite3_column_name([_statement statement], columnIdx)]]; 98 | } 99 | } 100 | } 101 | 102 | #pragma clang diagnostic push 103 | #pragma clang diagnostic ignored "-Wdeprecated-implementations" 104 | 105 | - (NSDictionary *)resultDict { 106 | 107 | NSUInteger num_cols = (NSUInteger)sqlite3_data_count([_statement statement]); 108 | 109 | if (num_cols > 0) { 110 | NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:num_cols]; 111 | 112 | NSEnumerator *columnNames = [[self columnNameToIndexMap] keyEnumerator]; 113 | NSString *columnName = nil; 114 | while ((columnName = [columnNames nextObject])) { 115 | id objectValue = [self objectForColumnName:columnName]; 116 | [dict setObject:objectValue forKey:columnName]; 117 | } 118 | 119 | return FMDBReturnAutoreleased([dict copy]); 120 | } 121 | else { 122 | NSLog(@"Warning: There seem to be no columns in this set."); 123 | } 124 | 125 | return nil; 126 | } 127 | 128 | #pragma clang diagnostic pop 129 | 130 | - (NSDictionary*)resultDictionary { 131 | 132 | NSUInteger num_cols = (NSUInteger)sqlite3_data_count([_statement statement]); 133 | 134 | if (num_cols > 0) { 135 | NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:num_cols]; 136 | 137 | int columnCount = sqlite3_column_count([_statement statement]); 138 | 139 | int columnIdx = 0; 140 | for (columnIdx = 0; columnIdx < columnCount; columnIdx++) { 141 | 142 | NSString *columnName = [NSString stringWithUTF8String:sqlite3_column_name([_statement statement], columnIdx)]; 143 | id objectValue = [self objectForColumnIndex:columnIdx]; 144 | [dict setObject:objectValue forKey:columnName]; 145 | } 146 | 147 | return dict; 148 | } 149 | else { 150 | NSLog(@"Warning: There seem to be no columns in this set."); 151 | } 152 | 153 | return nil; 154 | } 155 | 156 | 157 | 158 | 159 | - (BOOL)next { 160 | return [self nextWithError:nil]; 161 | } 162 | 163 | - (BOOL)nextWithError:(NSError **)outErr { 164 | 165 | int rc = sqlite3_step([_statement statement]); 166 | 167 | if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) { 168 | NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [_parentDB databasePath]); 169 | NSLog(@"Database busy"); 170 | if (outErr) { 171 | *outErr = [_parentDB lastError]; 172 | } 173 | } 174 | else if (SQLITE_DONE == rc || SQLITE_ROW == rc) { 175 | // all is well, let's return. 176 | } 177 | else if (SQLITE_ERROR == rc) { 178 | NSLog(@"Error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle])); 179 | if (outErr) { 180 | *outErr = [_parentDB lastError]; 181 | } 182 | } 183 | else if (SQLITE_MISUSE == rc) { 184 | // uh oh. 185 | NSLog(@"Error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle])); 186 | if (outErr) { 187 | if (_parentDB) { 188 | *outErr = [_parentDB lastError]; 189 | } 190 | else { 191 | // If 'next' or 'nextWithError' is called after the result set is closed, 192 | // we need to return the appropriate error. 193 | NSDictionary* errorMessage = [NSDictionary dictionaryWithObject:@"parentDB does not exist" forKey:NSLocalizedDescriptionKey]; 194 | *outErr = [NSError errorWithDomain:@"FMDatabase" code:SQLITE_MISUSE userInfo:errorMessage]; 195 | } 196 | 197 | } 198 | } 199 | else { 200 | // wtf? 201 | NSLog(@"Unknown error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([_parentDB sqliteHandle])); 202 | if (outErr) { 203 | *outErr = [_parentDB lastError]; 204 | } 205 | } 206 | 207 | 208 | if (rc != SQLITE_ROW) { 209 | [self close]; 210 | } 211 | 212 | return (rc == SQLITE_ROW); 213 | } 214 | 215 | - (BOOL)hasAnotherRow { 216 | return sqlite3_errcode([_parentDB sqliteHandle]) == SQLITE_ROW; 217 | } 218 | 219 | - (int)columnIndexForName:(NSString*)columnName { 220 | columnName = [columnName lowercaseString]; 221 | 222 | NSNumber *n = [[self columnNameToIndexMap] objectForKey:columnName]; 223 | 224 | if (n != nil) { 225 | return [n intValue]; 226 | } 227 | 228 | NSLog(@"Warning: I could not find the column named '%@'.", columnName); 229 | 230 | return -1; 231 | } 232 | 233 | - (int)intForColumn:(NSString*)columnName { 234 | return [self intForColumnIndex:[self columnIndexForName:columnName]]; 235 | } 236 | 237 | - (int)intForColumnIndex:(int)columnIdx { 238 | return sqlite3_column_int([_statement statement], columnIdx); 239 | } 240 | 241 | - (long)longForColumn:(NSString*)columnName { 242 | return [self longForColumnIndex:[self columnIndexForName:columnName]]; 243 | } 244 | 245 | - (long)longForColumnIndex:(int)columnIdx { 246 | return (long)sqlite3_column_int64([_statement statement], columnIdx); 247 | } 248 | 249 | - (long long int)longLongIntForColumn:(NSString*)columnName { 250 | return [self longLongIntForColumnIndex:[self columnIndexForName:columnName]]; 251 | } 252 | 253 | - (long long int)longLongIntForColumnIndex:(int)columnIdx { 254 | return sqlite3_column_int64([_statement statement], columnIdx); 255 | } 256 | 257 | - (unsigned long long int)unsignedLongLongIntForColumn:(NSString*)columnName { 258 | return [self unsignedLongLongIntForColumnIndex:[self columnIndexForName:columnName]]; 259 | } 260 | 261 | - (unsigned long long int)unsignedLongLongIntForColumnIndex:(int)columnIdx { 262 | return (unsigned long long int)[self longLongIntForColumnIndex:columnIdx]; 263 | } 264 | 265 | - (BOOL)boolForColumn:(NSString*)columnName { 266 | return [self boolForColumnIndex:[self columnIndexForName:columnName]]; 267 | } 268 | 269 | - (BOOL)boolForColumnIndex:(int)columnIdx { 270 | return ([self intForColumnIndex:columnIdx] != 0); 271 | } 272 | 273 | - (double)doubleForColumn:(NSString*)columnName { 274 | return [self doubleForColumnIndex:[self columnIndexForName:columnName]]; 275 | } 276 | 277 | - (double)doubleForColumnIndex:(int)columnIdx { 278 | return sqlite3_column_double([_statement statement], columnIdx); 279 | } 280 | 281 | - (NSString *)stringForColumnIndex:(int)columnIdx { 282 | 283 | if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0) || columnIdx >= sqlite3_column_count([_statement statement])) { 284 | return nil; 285 | } 286 | 287 | const char *c = (const char *)sqlite3_column_text([_statement statement], columnIdx); 288 | 289 | if (!c) { 290 | // null row. 291 | return nil; 292 | } 293 | 294 | return [NSString stringWithUTF8String:c]; 295 | } 296 | 297 | - (NSString*)stringForColumn:(NSString*)columnName { 298 | return [self stringForColumnIndex:[self columnIndexForName:columnName]]; 299 | } 300 | 301 | - (NSDate*)dateForColumn:(NSString*)columnName { 302 | return [self dateForColumnIndex:[self columnIndexForName:columnName]]; 303 | } 304 | 305 | - (NSDate*)dateForColumnIndex:(int)columnIdx { 306 | 307 | if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0) || columnIdx >= sqlite3_column_count([_statement statement])) { 308 | return nil; 309 | } 310 | 311 | return [_parentDB hasDateFormatter] ? [_parentDB dateFromString:[self stringForColumnIndex:columnIdx]] : [NSDate dateWithTimeIntervalSince1970:[self doubleForColumnIndex:columnIdx]]; 312 | } 313 | 314 | 315 | - (NSData*)dataForColumn:(NSString*)columnName { 316 | return [self dataForColumnIndex:[self columnIndexForName:columnName]]; 317 | } 318 | 319 | - (NSData*)dataForColumnIndex:(int)columnIdx { 320 | 321 | if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0) || columnIdx >= sqlite3_column_count([_statement statement])) { 322 | return nil; 323 | } 324 | 325 | const char *dataBuffer = sqlite3_column_blob([_statement statement], columnIdx); 326 | int dataSize = sqlite3_column_bytes([_statement statement], columnIdx); 327 | 328 | if (dataBuffer == NULL) { 329 | return nil; 330 | } 331 | 332 | return [NSData dataWithBytes:(const void *)dataBuffer length:(NSUInteger)dataSize]; 333 | } 334 | 335 | 336 | - (NSData*)dataNoCopyForColumn:(NSString*)columnName { 337 | return [self dataNoCopyForColumnIndex:[self columnIndexForName:columnName]]; 338 | } 339 | 340 | - (NSData*)dataNoCopyForColumnIndex:(int)columnIdx { 341 | 342 | if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0) || columnIdx >= sqlite3_column_count([_statement statement])) { 343 | return nil; 344 | } 345 | 346 | const char *dataBuffer = sqlite3_column_blob([_statement statement], columnIdx); 347 | int dataSize = sqlite3_column_bytes([_statement statement], columnIdx); 348 | 349 | NSData *data = [NSData dataWithBytesNoCopy:(void *)dataBuffer length:(NSUInteger)dataSize freeWhenDone:NO]; 350 | 351 | return data; 352 | } 353 | 354 | 355 | - (BOOL)columnIndexIsNull:(int)columnIdx { 356 | return sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL; 357 | } 358 | 359 | - (BOOL)columnIsNull:(NSString*)columnName { 360 | return [self columnIndexIsNull:[self columnIndexForName:columnName]]; 361 | } 362 | 363 | - (const unsigned char *)UTF8StringForColumnIndex:(int)columnIdx { 364 | 365 | if (sqlite3_column_type([_statement statement], columnIdx) == SQLITE_NULL || (columnIdx < 0) || columnIdx >= sqlite3_column_count([_statement statement])) { 366 | return nil; 367 | } 368 | 369 | return sqlite3_column_text([_statement statement], columnIdx); 370 | } 371 | 372 | - (const unsigned char *)UTF8StringForColumn:(NSString*)columnName { 373 | return [self UTF8StringForColumnIndex:[self columnIndexForName:columnName]]; 374 | } 375 | 376 | - (const unsigned char *)UTF8StringForColumnName:(NSString*)columnName { 377 | return [self UTF8StringForColumn:columnName]; 378 | } 379 | 380 | - (id)objectForColumnIndex:(int)columnIdx { 381 | if (columnIdx < 0 || columnIdx >= sqlite3_column_count([_statement statement])) { 382 | return nil; 383 | } 384 | 385 | int columnType = sqlite3_column_type([_statement statement], columnIdx); 386 | 387 | id returnValue = nil; 388 | 389 | if (columnType == SQLITE_INTEGER) { 390 | returnValue = [NSNumber numberWithLongLong:[self longLongIntForColumnIndex:columnIdx]]; 391 | } 392 | else if (columnType == SQLITE_FLOAT) { 393 | returnValue = [NSNumber numberWithDouble:[self doubleForColumnIndex:columnIdx]]; 394 | } 395 | else if (columnType == SQLITE_BLOB) { 396 | returnValue = [self dataForColumnIndex:columnIdx]; 397 | } 398 | else { 399 | //default to a string for everything else 400 | returnValue = [self stringForColumnIndex:columnIdx]; 401 | } 402 | 403 | if (returnValue == nil) { 404 | returnValue = [NSNull null]; 405 | } 406 | 407 | return returnValue; 408 | } 409 | 410 | - (id)objectForColumnName:(NSString*)columnName { 411 | return [self objectForColumn:columnName]; 412 | } 413 | 414 | - (id)objectForColumn:(NSString*)columnName { 415 | return [self objectForColumnIndex:[self columnIndexForName:columnName]]; 416 | } 417 | 418 | // returns autoreleased NSString containing the name of the column in the result set 419 | - (NSString*)columnNameForIndex:(int)columnIdx { 420 | return [NSString stringWithUTF8String: sqlite3_column_name([_statement statement], columnIdx)]; 421 | } 422 | 423 | - (id)objectAtIndexedSubscript:(int)columnIdx { 424 | return [self objectForColumnIndex:columnIdx]; 425 | } 426 | 427 | - (id)objectForKeyedSubscript:(NSString *)columnName { 428 | return [self objectForColumn:columnName]; 429 | } 430 | 431 | 432 | @end 433 | -------------------------------------------------------------------------------- /Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - FMDB (2.7.5): 3 | - FMDB/standard (= 2.7.5) 4 | - FMDB/standard (2.7.5) 5 | 6 | DEPENDENCIES: 7 | - FMDB 8 | 9 | SPEC REPOS: 10 | https://github.com/cocoapods/specs.git: 11 | - FMDB 12 | 13 | SPEC CHECKSUMS: 14 | FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a 15 | 16 | PODFILE CHECKSUM: f13ff6581f135bc6c1597623f3e34035a55cb088 17 | 18 | COCOAPODS: 1.5.3 19 | -------------------------------------------------------------------------------- /Pods/Target Support Files/FMDB/FMDB-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_FMDB : NSObject 3 | @end 4 | @implementation PodsDummy_FMDB 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/FMDB/FMDB-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/FMDB/FMDB-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | #import "FMDatabase.h" 14 | #import "FMDatabaseAdditions.h" 15 | #import "FMDatabasePool.h" 16 | #import "FMDatabaseQueue.h" 17 | #import "FMDB.h" 18 | #import "FMResultSet.h" 19 | 20 | FOUNDATION_EXPORT double FMDBVersionNumber; 21 | FOUNDATION_EXPORT const unsigned char FMDBVersionString[]; 22 | 23 | -------------------------------------------------------------------------------- /Pods/Target Support Files/FMDB/FMDB.modulemap: -------------------------------------------------------------------------------- 1 | framework module FMDB { 2 | umbrella header "FMDB-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/FMDB/FMDB.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/FMDB 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | OTHER_LDFLAGS = -l"sqlite3" 4 | PODS_BUILD_DIR = ${BUILD_DIR} 5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_ROOT = ${SRCROOT} 7 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/FMDB 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | -------------------------------------------------------------------------------- /Pods/Target Support Files/FMDB/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 | FMWK 17 | CFBundleShortVersionString 18 | 2.7.5 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-YIIFMDB/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 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-YIIFMDB/Pods-YIIFMDB-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## FMDB 5 | 6 | If you are using FMDB in your project, I'd love to hear about it. Let Gus know 7 | by sending an email to gus@flyingmeat.com. 8 | 9 | And if you happen to come across either Gus Mueller or Rob Ryan in a bar, you 10 | might consider purchasing a drink of their choosing if FMDB has been useful to 11 | you. 12 | 13 | Finally, and shortly, this is the MIT License. 14 | 15 | Copyright (c) 2008-2014 Flying Meat Inc. 16 | 17 | Permission is hereby granted, free of charge, to any person obtaining a copy 18 | of this software and associated documentation files (the "Software"), to deal 19 | in the Software without restriction, including without limitation the rights 20 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 21 | copies of the Software, and to permit persons to whom the Software is 22 | furnished to do so, subject to the following conditions: 23 | 24 | The above copyright notice and this permission notice shall be included in 25 | all copies or substantial portions of the Software. 26 | 27 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 28 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 29 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 30 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 31 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 32 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 33 | THE SOFTWARE. 34 | Generated by CocoaPods - https://cocoapods.org 35 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-YIIFMDB/Pods-YIIFMDB-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | If you are using FMDB in your project, I'd love to hear about it. Let Gus know 18 | by sending an email to gus@flyingmeat.com. 19 | 20 | And if you happen to come across either Gus Mueller or Rob Ryan in a bar, you 21 | might consider purchasing a drink of their choosing if FMDB has been useful to 22 | you. 23 | 24 | Finally, and shortly, this is the MIT License. 25 | 26 | Copyright (c) 2008-2014 Flying Meat Inc. 27 | 28 | Permission is hereby granted, free of charge, to any person obtaining a copy 29 | of this software and associated documentation files (the "Software"), to deal 30 | in the Software without restriction, including without limitation the rights 31 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 32 | copies of the Software, and to permit persons to whom the Software is 33 | furnished to do so, subject to the following conditions: 34 | 35 | The above copyright notice and this permission notice shall be included in 36 | all copies or substantial portions of the Software. 37 | 38 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 39 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 40 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 41 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 42 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 43 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 44 | THE SOFTWARE. 45 | License 46 | MIT 47 | Title 48 | FMDB 49 | Type 50 | PSGroupSpecifier 51 | 52 | 53 | FooterText 54 | Generated by CocoaPods - https://cocoapods.org 55 | Title 56 | 57 | Type 58 | PSGroupSpecifier 59 | 60 | 61 | StringsTable 62 | Acknowledgements 63 | Title 64 | Acknowledgements 65 | 66 | 67 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-YIIFMDB/Pods-YIIFMDB-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_YIIFMDB : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_YIIFMDB 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-YIIFMDB/Pods-YIIFMDB-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then 7 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy 8 | # frameworks to, so exit 0 (signalling the script phase was successful). 9 | exit 0 10 | fi 11 | 12 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 13 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 14 | 15 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" 16 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 17 | 18 | # Used as a return value for each invocation of `strip_invalid_archs` function. 19 | STRIP_BINARY_RETVAL=0 20 | 21 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 22 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 23 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 24 | 25 | # Copies and strips a vendored framework 26 | install_framework() 27 | { 28 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 29 | local source="${BUILT_PRODUCTS_DIR}/$1" 30 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 31 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 32 | elif [ -r "$1" ]; then 33 | local source="$1" 34 | fi 35 | 36 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 37 | 38 | if [ -L "${source}" ]; then 39 | echo "Symlinked..." 40 | source="$(readlink "${source}")" 41 | fi 42 | 43 | # Use filter instead of exclude so missing patterns don't throw errors. 44 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 45 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 46 | 47 | local basename 48 | basename="$(basename -s .framework "$1")" 49 | binary="${destination}/${basename}.framework/${basename}" 50 | if ! [ -r "$binary" ]; then 51 | binary="${destination}/${basename}" 52 | fi 53 | 54 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 55 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 56 | strip_invalid_archs "$binary" 57 | fi 58 | 59 | # Resign the code if required by the build settings to avoid unstable apps 60 | code_sign_if_enabled "${destination}/$(basename "$1")" 61 | 62 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 63 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 64 | local swift_runtime_libs 65 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 66 | for lib in $swift_runtime_libs; do 67 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 68 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 69 | code_sign_if_enabled "${destination}/${lib}" 70 | done 71 | fi 72 | } 73 | 74 | # Copies and strips a vendored dSYM 75 | install_dsym() { 76 | local source="$1" 77 | if [ -r "$source" ]; then 78 | # Copy the dSYM into a the targets temp dir. 79 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 80 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 81 | 82 | local basename 83 | basename="$(basename -s .framework.dSYM "$source")" 84 | binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" 85 | 86 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 87 | if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then 88 | strip_invalid_archs "$binary" 89 | fi 90 | 91 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then 92 | # Move the stripped file into its final destination. 93 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 94 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 95 | else 96 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 97 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" 98 | fi 99 | fi 100 | } 101 | 102 | # Signs a framework with the provided identity 103 | code_sign_if_enabled() { 104 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 105 | # Use the current code_sign_identitiy 106 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 107 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" 108 | 109 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 110 | code_sign_cmd="$code_sign_cmd &" 111 | fi 112 | echo "$code_sign_cmd" 113 | eval "$code_sign_cmd" 114 | fi 115 | } 116 | 117 | # Strip invalid architectures 118 | strip_invalid_archs() { 119 | binary="$1" 120 | # Get architectures for current target binary 121 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 122 | # Intersect them with the architectures we are building for 123 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 124 | # If there are no archs supported by this binary then warn the user 125 | if [[ -z "$intersected_archs" ]]; then 126 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 127 | STRIP_BINARY_RETVAL=0 128 | return 129 | fi 130 | stripped="" 131 | for arch in $binary_archs; do 132 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 133 | # Strip non-valid architectures in-place 134 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 135 | stripped="$stripped $arch" 136 | fi 137 | done 138 | if [[ "$stripped" ]]; then 139 | echo "Stripped $binary of architectures:$stripped" 140 | fi 141 | STRIP_BINARY_RETVAL=1 142 | } 143 | 144 | 145 | if [[ "$CONFIGURATION" == "Debug" ]]; then 146 | install_framework "${BUILT_PRODUCTS_DIR}/FMDB/FMDB.framework" 147 | fi 148 | if [[ "$CONFIGURATION" == "Release" ]]; then 149 | install_framework "${BUILT_PRODUCTS_DIR}/FMDB/FMDB.framework" 150 | fi 151 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 152 | wait 153 | fi 154 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-YIIFMDB/Pods-YIIFMDB-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | if [ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH+x} ]; then 7 | # If UNLOCALIZED_RESOURCES_FOLDER_PATH is not set, then there's nowhere for us to copy 8 | # resources to, so exit 0 (signalling the script phase was successful). 9 | exit 0 10 | fi 11 | 12 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 13 | 14 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 15 | > "$RESOURCES_TO_COPY" 16 | 17 | XCASSET_FILES=() 18 | 19 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 20 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 21 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 22 | 23 | case "${TARGETED_DEVICE_FAMILY:-}" in 24 | 1,2) 25 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 26 | ;; 27 | 1) 28 | TARGET_DEVICE_ARGS="--target-device iphone" 29 | ;; 30 | 2) 31 | TARGET_DEVICE_ARGS="--target-device ipad" 32 | ;; 33 | 3) 34 | TARGET_DEVICE_ARGS="--target-device tv" 35 | ;; 36 | 4) 37 | TARGET_DEVICE_ARGS="--target-device watch" 38 | ;; 39 | *) 40 | TARGET_DEVICE_ARGS="--target-device mac" 41 | ;; 42 | esac 43 | 44 | install_resource() 45 | { 46 | if [[ "$1" = /* ]] ; then 47 | RESOURCE_PATH="$1" 48 | else 49 | RESOURCE_PATH="${PODS_ROOT}/$1" 50 | fi 51 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 52 | cat << EOM 53 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 54 | EOM 55 | exit 1 56 | fi 57 | case $RESOURCE_PATH in 58 | *.storyboard) 59 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 60 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 61 | ;; 62 | *.xib) 63 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 64 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 65 | ;; 66 | *.framework) 67 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 68 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 69 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 70 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 71 | ;; 72 | *.xcdatamodel) 73 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true 74 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 75 | ;; 76 | *.xcdatamodeld) 77 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true 78 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 79 | ;; 80 | *.xcmappingmodel) 81 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true 82 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 83 | ;; 84 | *.xcassets) 85 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 86 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 87 | ;; 88 | *) 89 | echo "$RESOURCE_PATH" || true 90 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 91 | ;; 92 | esac 93 | } 94 | 95 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 96 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 97 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 98 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 99 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 100 | fi 101 | rm -f "$RESOURCES_TO_COPY" 102 | 103 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ] 104 | then 105 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 106 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 107 | while read line; do 108 | if [[ $line != "${PODS_ROOT}*" ]]; then 109 | XCASSET_FILES+=("$line") 110 | fi 111 | done <<<"$OTHER_XCASSETS" 112 | 113 | if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then 114 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 115 | else 116 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_TEMP_DIR}/assetcatalog_generated_info_cocoapods.plist" 117 | fi 118 | fi 119 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-YIIFMDB/Pods-YIIFMDB-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_YIIFMDBVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_YIIFMDBVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-YIIFMDB/Pods-YIIFMDB.debug.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FMDB" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/FMDB/FMDB.framework/Headers" 5 | OTHER_LDFLAGS = $(inherited) -framework "FMDB" 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 9 | PODS_ROOT = ${SRCROOT}/Pods 10 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-YIIFMDB/Pods-YIIFMDB.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_YIIFMDB { 2 | umbrella header "Pods-YIIFMDB-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-YIIFMDB/Pods-YIIFMDB.release.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FMDB" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/FMDB/FMDB.framework/Headers" 5 | OTHER_LDFLAGS = $(inherited) -framework "FMDB" 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 9 | PODS_ROOT = ${SRCROOT}/Pods 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # YIIFMDB 介绍 2 | 基于FMDB的进一步封装:纯面向对象(其思想源自php的[yii 2架构](https://www.yiichina.com/doc/guide/2.0)),实现了model与数据库的一一映射,并且在大多数情况下,对数据库的操作比如增删改查等操作,完全不需要写sql语句。 3 | 4 | blog:https://www.jianshu.com/p/482ef2ad3e66 5 | 6 | NSUserDefaultsModel:https://github.com/liuchongfaye/NSUserDefaultsModel (NSUserDefaults改进方案) 7 | 8 | 其中主要有两个类:YIIParameters和YIIFMDB,具体用法如下: 9 | 10 | ## YIIParameters 用法 11 | 12 | **YIIParameters类** 13 | YIIParameters是对sql语句当中**where**之后的参数进行封装的类,其中涉及到的操作有**and**,**or**,**limit**,**order by**,分别代表着**与**,**或**,**限制**,**排序**,具体用法如下: 14 | 15 | **and(与操作)** 16 | > -(void)andWhere:(NSString * _Nonnull)column value:(id _Nonnull)value relationType:(YIIParametersRelationType)relationType 17 | 18 | 在sql语句的where后面增加一个and操作,比如筛选出年龄(age)在10-20之间的参数,代码如下: 19 | ``` 20 | // 配置age在大于10且小于20的参数 21 | YIIParameters *parameters = [[YIIParameters alloc] init]; 22 | [parameters andWhere:@"age" value:@"10" relationType:YIIParametersRelationTypeGreaterThan]; 23 | [parameters andWhere:@"age" value:@"20" relationType:YIIParametersRelationTypeLessThan]; 24 | ``` 25 | 其中这个**YIIParametersRelationType**这个枚举决定方法当中的key和value之间的关系,包括=,!=,>,>=,<,<=,这些都有其对应的枚举值,但是这些只适用于字段属性是**integer**时有用。除了以上这些还有一个**like**,用于字符串运算,比如筛选名称(name)跟"lc"相类似的可以设置为: 26 | ``` 27 | [parameters andWhere:@"name" value:@"lc" relationType:YIIParametersRelationTypeLike]; 28 | ``` 29 | 30 | **or(或操作)** 31 | > -(void)orWhere:(NSString * _Nonnull)column value:(id _Nonnull)value relationType:(YIIParametersRelationType)relationType 32 | 33 | 在sql语句的where后面增加一个or操作,比如执行以上and操作之后,在加上一个或者年龄(age)大于30之间的参数,代码如下: 34 | ``` 35 | // 配置age在大于30的参数 36 | [parameters andWhere:@"age" value:@"30" relationType:YIIParametersRelationTypeGreaterThan]; 37 | ``` 38 | **Warning**:值得一提的是,倘或where之后只有一个条件,那么**and**和**or**是一样的。比如只是仅仅只是帅选出年龄(age)大于30之间的参数。 39 | 40 | **limit(数量限制)** 41 | > @property (nonatomic, assign) NSInteger limitCount; 42 | 在sql语句的where后面增加筛选数据的数量限制,比如将数量限制在10,那么代码如下: 43 | ``` 44 | parameters.limitCount = 10; 45 | ``` 46 | 47 | **order by(排序)** 48 | > -(void)orderByColumn:(NSString * _Nonnull)column orderType:(YIIParametersOrderType)orderType 49 | 50 | 在sql语句的where后面增加一个排序限制,比如根据年龄(age)进行升序,代码如下: 51 | ``` 52 | [parameters orderByColumn:@"age" orderType:YIIParametersOrderTypeAsc]; 53 | ``` 54 | 这里涉及到一个枚举**YIIParametersOrderType**,其中**YIIParametersOrderTypeAsc为升序,YIIParametersOrderTypeDesc为降序**. 55 | 56 | 根据**and**,**or**,**limit**,**order by**这四个操作可以实现比较复杂的语句,比如:请筛选出年龄age在10到20之间,或者age>30,并且根据年纪选出最大的10人,代码如下: 57 | ``` 58 | YIIParameters *parameters = [[YIIParameters alloc] init]; 59 | // 配置age在大于10且小于20的参数,两个and操作 60 | [parameters andWhere:@"age" value:@"10" relationType:YIIParametersRelationTypeGreaterThan]; 61 | [parameters andWhere:@"age" value:@"20" relationType:YIIParametersRelationTypeLessThan]; 62 | // 配置age在大于30的参数,or操作 63 | [parameteror orWhere:@"age" value:@"30" relationType:YIIParametersRelationTypeGreaterThan]; 64 | // 数量限制在10个 65 | parameters.limitCount = 10; 66 | // 根据年龄进行降序 67 | [parameters orderByColumn:@"age" orderType:YIIParametersOrderTypeDesc]; 68 | ``` 69 | 70 | **最终形成的where参数** 71 | > @property (nonatomic, copy) NSString *whereParameters; 72 | 73 | 最终形成的where之后的参数可以从**YIIParameters**这个类的**whereParameters**属性获得。 74 | 如果都不能满足,那么自己设置这个参数。 75 | 76 | 这里YIIParameters介绍完了,以下是YIIFMDB类的使用: 77 | 78 | ### YIIFMDB 类 79 | 以上的YIIParameters介绍了sql语句当中where之后参数的设置,而YIIFMDB类则是对数据库操作的封装,比如增删改查等,具体用法如下: 80 | 81 | **获取单例** 82 | ``` 83 | YIIFMDB *db = [YIIFMDB shareDatabase]; // 推荐使用 84 | // 或者 85 | YIIFMDB *db = [YIIFMDB shareDatabaseForName:@"ABC.sqlite" path:path]; // 自定义数据库名字和路径,在第一次实例的时候传入,以后使用上面方法即可。 86 | ``` 87 | 88 | **主键的字段** 89 | > @property (nonatomic, readonly, copy) NSString *primaryKey; // 返回"yii_pkID",我自己在创建数据库是配置的主键字段 90 | 91 | **是否打印log** 92 | > @property (nonatomic, assign) BOOL shouldOpenDebugLog; // 默认为NO,如果设为YES,那么会在控制器后台打印数据库操作相关的一些信息 93 | 94 | **创建一张表** 95 | ``` 96 | [[YIIFMDB shareDatabase] createTableWithModelClass:[LCVideoModel class] excludedProperties:nil tableName:@"Video"]; 97 | ``` 98 | 此方法是创建一张名为@"Video"表,并且,表里面的字段也就是LCVideoModel里面的属性,字段的数据类型也对应LCVideoModel里面的数据类型 99 | 100 | **插入一条数据(增)** 101 | ``` 102 | LCVideoModel *model = [[LCVideoModel alloc] init]; 103 | model.name = [NSString stringWithFormat:@"lc%d", (arc4random() % 100)]; 104 | model.gender = arc4random() % 2; 105 | model.age = arc4random() % 80; 106 | model.floatNumber = (arc4random() % 20) / 100.0; 107 | model.doubleNumber = (arc4random() % 20) / 100.0; 108 | model.isMan = arc4random() % 2; 109 | model.number = @(arc4random() % 10); 110 | 111 | YIIFMDB *db = [YIIFMDB shareDatabase]; 112 | BOOL isSuccess = [db insertWithModel:model tableName:tableName]; //插入一条数据 113 | [db insertWithModels:@[model] tableName:tableName]; // 批量插入数据 114 | ``` 115 | 116 | **删除数据(删)** 117 | > -(BOOL)deleteFromTable:(NSString * _Nonnull)tableName whereParameters:(YIIParameters *)parameters; // 根据参数删除一条数据,YIIParameters参考上面 118 | -(BOOL)deleteAllDataFromTable:(NSString * _Nonnull)tableName; // 删除表中的所有数据 119 | 120 | ``` 121 | YIIFMDB *db = [YIIFMDB shareDatabase]; 122 | YIIParameters *parameters = [[YIIParameters alloc] init]; 123 | // db.primaryKey 是数据库的主键,这条语句意思是删除主键 = 1的那条数据 124 | [parameters andWhere:db.primaryKey value:@"1" relationType:YIIParametersRelationTypeEqualTo]; 125 | [db deleteFromTable:tableName whereParameters:parameters]; 126 | ``` 127 | 128 | **更改数据(改)** 129 | > -(BOOL)updateTable:(NSString * _Nonnull)tableName dictionary:(NSDictionary * _Nonnull)dictionary whereParameters:(YIIParameters *)parameters; // 更新一条数据 130 | 131 | ``` 132 | YIIFMDB *db = [YIIFMDB shareDatabase]; 133 | YIIParameters *parameters = [[YIIParameters alloc] init]; 134 | // 参数设置为主键 = 10 135 | [parameters andWhere:db.primaryKey value:@"10" relationType:YIIParametersRelationTypeEqualTo]; 136 | // 将主键为10的那条数据的name更改为monkey 137 | [db updateTable:tableName dictionary:@{@"name": @"monkey"} whereParameters:parameters]; 138 | ``` 139 | 140 | **查询数据** 141 | > -(NSArray *)queryFromTable:(NSString * _Nonnull)tableName model:(Class _Nonnull)modelClass whereParameters:(YIIParameters *)parameters; // 根据YIIParameters条件从表为tableName的查询数据 142 | 143 | ``` 144 | YIIFMDB *db = [YIIFMDB shareDatabase]; 145 | YIIParameters *parameters = [[YIIParameters alloc] init]; 146 | [parameters andWhere:db.primaryKey value:@"5" relationType:YIIParametersRelationTypeLessThan]; 147 | NSLog(@"主键值小于5的所有数据:%@", [db queryFromTable:tableName model:[LCVideoModel class] whereParameters:parameters]); 148 | ``` 149 | **表是否存在** 150 | > -(BOOL)existTable:(NSString * _Nonnull)tableName; // tableName为表的名字 151 | 152 | **为表增加一个属性** 153 | > -(BOOL)alterTable:(NSString * _Nonnull)tableName column:(NSString * _Nonnull)column type:(YIIFMDBValueType)type; 154 | 155 | 其中YIIFMDBValueType为表支持的类型,包含**字符串(YIIFMDBValueTypeString)**,**整型(YIIFMDBValueTypeInteger)**,**浮点型(YIIFMDBValueTypeFloat)**,**二进制(YIIFMDBValueTypeData)**。 156 | 157 | **删除一张表(不推荐使用)** 158 | > -(BOOL)dropTable:(NSString * _Nonnull)tableName; 159 | 160 | **获取表中所有字段名** 161 | > -(NSArray *)getAllColumnsFromTable:(NSString * _Nonnull)tableName; 162 | 163 | **表中的数据个数** 164 | > -(long long int)numberOfItemsFromTable:(NSString * _Nonnull)tableName whereParameters:(YIIParameters * _Nullable)parameters; // parameters表示筛选参数,参考上面的**YIIParameters** 165 | 166 | **数学相关操作** 167 | > -(double)numberWithMathType:(YIIFMDBMathType)type table:(NSString * _Nonnull)tableName column:(NSString * _Nonnull)column whereParameters:(YIIParameters * _Nullable)parameters; // 对表中的某一个字段进行数学相关运算,比如求和,平均值,最大值,最小值 168 | 169 | 其中YIIFMDBMathType是一个枚举,分别对应着**求和(YIIFMDBMathTypeSum)**,**平均值(YIIFMDBMathTypeAvg)**,**最大值(YIIFMDBMathTypeMax)**,**最小值(YIIFMDBMathTypeMin)**. 170 | 171 | **线程安全操作(队列和事务)** 172 | 173 | 由于FMDB本身就是是不安全的,上面的方法也是不安全的,为了保证其安全则需要结合队列和事务操作,参考FMDB的队列和事务。 174 | > -(void)inDatabase:(dispatch_block_t)block; // 将数据库相关操作写在block里可保证线程安全 175 | 176 | ``` 177 | YIIFMDB *db = [YIIFMDB shareDatabase]; 178 | [db inDatabase:^{ 179 | // 增删改查放在此代码块里执行则可以保证线程安全 180 | }]; 181 | ``` 182 | 183 | > -(void)inTransaction:(void(^)(BOOL *rollback))block; // 在block里写入代码可执行回滚操作 184 | ``` 185 | YIIFMDB *db = [YIIFMDB shareDatabase]; 186 | [db inTransaction:^(BOOL *rollback) { 187 | // 如果某一个操作失误,则可以执行回滚操作 188 | BOOL isSuccess = YES; // 数据库操作是否操作成功 189 | if (!isSuccess) { 190 | *rollback = YES; // 回滚操作 191 | 192 | return ; 193 | } 194 | }]; 195 | ``` 196 | 197 | #### 安装 198 | 199 | cocoapods安装:在podfile里写入: 200 | 201 | > pod 'YIIFMDB' 202 | 203 | 然后在执行pod install,等待安装完毕,在需要使用的类当中导入当前库,比如```@import YIIFMDB;```。 204 | 205 | 如果未使用cocoapods,那么就下载此工程,将包含YIIFMDB类和YIIParameters类的文件夹导入到工程当中,随后在使用的类当中导入引入YIIFMDB.h的头文件即刻,比如```#import "YIIFMDB.h"```。 206 | 207 | -------------------------------------------------------------------------------- /YIIFMDB.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = "YIIFMDB" 4 | s.version = "1.0.3" 5 | s.summary = "基于FMDB的再一次封装,纯面向对象,直接操作Model,不需要写sqlite语句" 6 | s.homepage = "https://github.com/liuchongfaye/YIIFMDB" 7 | s.license = "MIT" 8 | s.author = { "刘冲" => "liuchongfaye@163.com" } 9 | s.platform = :ios 10 | s.ios.deployment_target = "7.0" 11 | s.source = { :git => "https://github.com/liuchongfaye/YIIFMDB.git", :tag => "#{s.version}" } 12 | s.source_files = "YIIFMDB/YIIFMDB/*.{h,m}" 13 | s.requires_arc = true 14 | s.framework = "Foundation" 15 | s.dependency "FMDB" 16 | 17 | end 18 | -------------------------------------------------------------------------------- /YIIFMDB.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 44C2082A22B3BEC6008B8512 /* LCVideoModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 44C2082922B3BEC6008B8512 /* LCVideoModel.m */; }; 11 | 44FAF47321869C09003925D4 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 44FAF47221869C09003925D4 /* AppDelegate.m */; }; 12 | 44FAF47621869C09003925D4 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 44FAF47521869C09003925D4 /* ViewController.m */; }; 13 | 44FAF47921869C09003925D4 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 44FAF47721869C09003925D4 /* Main.storyboard */; }; 14 | 44FAF47B21869C0A003925D4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 44FAF47A21869C0A003925D4 /* Assets.xcassets */; }; 15 | 44FAF47E21869C0A003925D4 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 44FAF47C21869C0A003925D4 /* LaunchScreen.storyboard */; }; 16 | 44FAF48121869C0A003925D4 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 44FAF48021869C0A003925D4 /* main.m */; }; 17 | 44FAF48B21869C0A003925D4 /* YIIFMDBUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 44FAF48A21869C0A003925D4 /* YIIFMDBUITests.m */; }; 18 | 44FAF49A21869D9A003925D4 /* YIIParameters.m in Sources */ = {isa = PBXBuildFile; fileRef = 44FAF49721869D99003925D4 /* YIIParameters.m */; }; 19 | 44FAF49B21869D9A003925D4 /* YIIFMDB.m in Sources */ = {isa = PBXBuildFile; fileRef = 44FAF49921869D9A003925D4 /* YIIFMDB.m */; }; 20 | 61B641D883B0CCCA57313E8F /* Pods_YIIFMDB.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F1A38E5A73752E47BFC2BFA9 /* Pods_YIIFMDB.framework */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXContainerItemProxy section */ 24 | 44FAF48721869C0A003925D4 /* PBXContainerItemProxy */ = { 25 | isa = PBXContainerItemProxy; 26 | containerPortal = 44FAF46621869C09003925D4 /* Project object */; 27 | proxyType = 1; 28 | remoteGlobalIDString = 44FAF46D21869C09003925D4; 29 | remoteInfo = YIIFMDB; 30 | }; 31 | /* End PBXContainerItemProxy section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | 44C2082822B3BEC6008B8512 /* LCVideoModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCVideoModel.h; sourceTree = ""; }; 35 | 44C2082922B3BEC6008B8512 /* LCVideoModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCVideoModel.m; sourceTree = ""; }; 36 | 44FAF46E21869C09003925D4 /* YIIFMDB.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = YIIFMDB.app; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | 44FAF47121869C09003925D4 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 38 | 44FAF47221869C09003925D4 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 39 | 44FAF47421869C09003925D4 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 40 | 44FAF47521869C09003925D4 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 41 | 44FAF47821869C09003925D4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 42 | 44FAF47A21869C0A003925D4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 43 | 44FAF47D21869C0A003925D4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 44 | 44FAF47F21869C0A003925D4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 45 | 44FAF48021869C0A003925D4 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 46 | 44FAF48621869C0A003925D4 /* YIIFMDBUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = YIIFMDBUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 47 | 44FAF48A21869C0A003925D4 /* YIIFMDBUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = YIIFMDBUITests.m; sourceTree = ""; }; 48 | 44FAF48C21869C0A003925D4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 49 | 44FAF49621869D99003925D4 /* YIIParameters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YIIParameters.h; sourceTree = ""; }; 50 | 44FAF49721869D99003925D4 /* YIIParameters.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YIIParameters.m; sourceTree = ""; }; 51 | 44FAF49821869D99003925D4 /* YIIFMDB.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YIIFMDB.h; sourceTree = ""; }; 52 | 44FAF49921869D9A003925D4 /* YIIFMDB.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YIIFMDB.m; sourceTree = ""; }; 53 | 69A669F5AAF16E7F8D6CEB65 /* Pods-YIIFMDB.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-YIIFMDB.debug.xcconfig"; path = "Pods/Target Support Files/Pods-YIIFMDB/Pods-YIIFMDB.debug.xcconfig"; sourceTree = ""; }; 54 | F1A38E5A73752E47BFC2BFA9 /* Pods_YIIFMDB.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_YIIFMDB.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 55 | F3313952066C3D8F065D78C1 /* Pods-YIIFMDB.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-YIIFMDB.release.xcconfig"; path = "Pods/Target Support Files/Pods-YIIFMDB/Pods-YIIFMDB.release.xcconfig"; sourceTree = ""; }; 56 | /* End PBXFileReference section */ 57 | 58 | /* Begin PBXFrameworksBuildPhase section */ 59 | 44FAF46B21869C09003925D4 /* Frameworks */ = { 60 | isa = PBXFrameworksBuildPhase; 61 | buildActionMask = 2147483647; 62 | files = ( 63 | 61B641D883B0CCCA57313E8F /* Pods_YIIFMDB.framework in Frameworks */, 64 | ); 65 | runOnlyForDeploymentPostprocessing = 0; 66 | }; 67 | 44FAF48321869C0A003925D4 /* Frameworks */ = { 68 | isa = PBXFrameworksBuildPhase; 69 | buildActionMask = 2147483647; 70 | files = ( 71 | ); 72 | runOnlyForDeploymentPostprocessing = 0; 73 | }; 74 | /* End PBXFrameworksBuildPhase section */ 75 | 76 | /* Begin PBXGroup section */ 77 | 44FAF46521869C09003925D4 = { 78 | isa = PBXGroup; 79 | children = ( 80 | 44FAF47021869C09003925D4 /* YIIFMDB */, 81 | 44FAF48921869C0A003925D4 /* YIIFMDBUITests */, 82 | 44FAF46F21869C09003925D4 /* Products */, 83 | D6D77B71D56C0298F5FC8848 /* Pods */, 84 | F4B7E1654F8E52323B95D66B /* Frameworks */, 85 | ); 86 | sourceTree = ""; 87 | }; 88 | 44FAF46F21869C09003925D4 /* Products */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | 44FAF46E21869C09003925D4 /* YIIFMDB.app */, 92 | 44FAF48621869C0A003925D4 /* YIIFMDBUITests.xctest */, 93 | ); 94 | name = Products; 95 | sourceTree = ""; 96 | }; 97 | 44FAF47021869C09003925D4 /* YIIFMDB */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 44FAF49521869CE1003925D4 /* YIIFMDB */, 101 | 44FAF47121869C09003925D4 /* AppDelegate.h */, 102 | 44FAF47221869C09003925D4 /* AppDelegate.m */, 103 | 44FAF47421869C09003925D4 /* ViewController.h */, 104 | 44FAF47521869C09003925D4 /* ViewController.m */, 105 | 44C2082822B3BEC6008B8512 /* LCVideoModel.h */, 106 | 44C2082922B3BEC6008B8512 /* LCVideoModel.m */, 107 | 44FAF47721869C09003925D4 /* Main.storyboard */, 108 | 44FAF47A21869C0A003925D4 /* Assets.xcassets */, 109 | 44FAF47C21869C0A003925D4 /* LaunchScreen.storyboard */, 110 | 44FAF47F21869C0A003925D4 /* Info.plist */, 111 | 44FAF48021869C0A003925D4 /* main.m */, 112 | ); 113 | path = YIIFMDB; 114 | sourceTree = ""; 115 | }; 116 | 44FAF48921869C0A003925D4 /* YIIFMDBUITests */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | 44FAF48A21869C0A003925D4 /* YIIFMDBUITests.m */, 120 | 44FAF48C21869C0A003925D4 /* Info.plist */, 121 | ); 122 | path = YIIFMDBUITests; 123 | sourceTree = ""; 124 | }; 125 | 44FAF49521869CE1003925D4 /* YIIFMDB */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | 44FAF49821869D99003925D4 /* YIIFMDB.h */, 129 | 44FAF49921869D9A003925D4 /* YIIFMDB.m */, 130 | 44FAF49621869D99003925D4 /* YIIParameters.h */, 131 | 44FAF49721869D99003925D4 /* YIIParameters.m */, 132 | ); 133 | path = YIIFMDB; 134 | sourceTree = ""; 135 | }; 136 | D6D77B71D56C0298F5FC8848 /* Pods */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | 69A669F5AAF16E7F8D6CEB65 /* Pods-YIIFMDB.debug.xcconfig */, 140 | F3313952066C3D8F065D78C1 /* Pods-YIIFMDB.release.xcconfig */, 141 | ); 142 | name = Pods; 143 | sourceTree = ""; 144 | }; 145 | F4B7E1654F8E52323B95D66B /* Frameworks */ = { 146 | isa = PBXGroup; 147 | children = ( 148 | F1A38E5A73752E47BFC2BFA9 /* Pods_YIIFMDB.framework */, 149 | ); 150 | name = Frameworks; 151 | sourceTree = ""; 152 | }; 153 | /* End PBXGroup section */ 154 | 155 | /* Begin PBXNativeTarget section */ 156 | 44FAF46D21869C09003925D4 /* YIIFMDB */ = { 157 | isa = PBXNativeTarget; 158 | buildConfigurationList = 44FAF48F21869C0A003925D4 /* Build configuration list for PBXNativeTarget "YIIFMDB" */; 159 | buildPhases = ( 160 | A5CB46048577BBB2975AEA16 /* [CP] Check Pods Manifest.lock */, 161 | 44FAF46A21869C09003925D4 /* Sources */, 162 | 44FAF46B21869C09003925D4 /* Frameworks */, 163 | 44FAF46C21869C09003925D4 /* Resources */, 164 | 62B3AA224DF8289451F55874 /* [CP] Embed Pods Frameworks */, 165 | ); 166 | buildRules = ( 167 | ); 168 | dependencies = ( 169 | ); 170 | name = YIIFMDB; 171 | productName = YIIFMDB; 172 | productReference = 44FAF46E21869C09003925D4 /* YIIFMDB.app */; 173 | productType = "com.apple.product-type.application"; 174 | }; 175 | 44FAF48521869C0A003925D4 /* YIIFMDBUITests */ = { 176 | isa = PBXNativeTarget; 177 | buildConfigurationList = 44FAF49221869C0A003925D4 /* Build configuration list for PBXNativeTarget "YIIFMDBUITests" */; 178 | buildPhases = ( 179 | 44FAF48221869C0A003925D4 /* Sources */, 180 | 44FAF48321869C0A003925D4 /* Frameworks */, 181 | 44FAF48421869C0A003925D4 /* Resources */, 182 | ); 183 | buildRules = ( 184 | ); 185 | dependencies = ( 186 | 44FAF48821869C0A003925D4 /* PBXTargetDependency */, 187 | ); 188 | name = YIIFMDBUITests; 189 | productName = YIIFMDBUITests; 190 | productReference = 44FAF48621869C0A003925D4 /* YIIFMDBUITests.xctest */; 191 | productType = "com.apple.product-type.bundle.ui-testing"; 192 | }; 193 | /* End PBXNativeTarget section */ 194 | 195 | /* Begin PBXProject section */ 196 | 44FAF46621869C09003925D4 /* Project object */ = { 197 | isa = PBXProject; 198 | attributes = { 199 | LastUpgradeCheck = 0940; 200 | ORGANIZATIONNAME = lc; 201 | TargetAttributes = { 202 | 44FAF46D21869C09003925D4 = { 203 | CreatedOnToolsVersion = 9.4.1; 204 | }; 205 | 44FAF48521869C0A003925D4 = { 206 | CreatedOnToolsVersion = 9.4.1; 207 | TestTargetID = 44FAF46D21869C09003925D4; 208 | }; 209 | }; 210 | }; 211 | buildConfigurationList = 44FAF46921869C09003925D4 /* Build configuration list for PBXProject "YIIFMDB" */; 212 | compatibilityVersion = "Xcode 9.3"; 213 | developmentRegion = en; 214 | hasScannedForEncodings = 0; 215 | knownRegions = ( 216 | en, 217 | Base, 218 | ); 219 | mainGroup = 44FAF46521869C09003925D4; 220 | productRefGroup = 44FAF46F21869C09003925D4 /* Products */; 221 | projectDirPath = ""; 222 | projectRoot = ""; 223 | targets = ( 224 | 44FAF46D21869C09003925D4 /* YIIFMDB */, 225 | 44FAF48521869C0A003925D4 /* YIIFMDBUITests */, 226 | ); 227 | }; 228 | /* End PBXProject section */ 229 | 230 | /* Begin PBXResourcesBuildPhase section */ 231 | 44FAF46C21869C09003925D4 /* Resources */ = { 232 | isa = PBXResourcesBuildPhase; 233 | buildActionMask = 2147483647; 234 | files = ( 235 | 44FAF47E21869C0A003925D4 /* LaunchScreen.storyboard in Resources */, 236 | 44FAF47B21869C0A003925D4 /* Assets.xcassets in Resources */, 237 | 44FAF47921869C09003925D4 /* Main.storyboard in Resources */, 238 | ); 239 | runOnlyForDeploymentPostprocessing = 0; 240 | }; 241 | 44FAF48421869C0A003925D4 /* Resources */ = { 242 | isa = PBXResourcesBuildPhase; 243 | buildActionMask = 2147483647; 244 | files = ( 245 | ); 246 | runOnlyForDeploymentPostprocessing = 0; 247 | }; 248 | /* End PBXResourcesBuildPhase section */ 249 | 250 | /* Begin PBXShellScriptBuildPhase section */ 251 | 62B3AA224DF8289451F55874 /* [CP] Embed Pods Frameworks */ = { 252 | isa = PBXShellScriptBuildPhase; 253 | buildActionMask = 2147483647; 254 | files = ( 255 | ); 256 | inputPaths = ( 257 | "${SRCROOT}/Pods/Target Support Files/Pods-YIIFMDB/Pods-YIIFMDB-frameworks.sh", 258 | "${BUILT_PRODUCTS_DIR}/FMDB/FMDB.framework", 259 | ); 260 | name = "[CP] Embed Pods Frameworks"; 261 | outputPaths = ( 262 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FMDB.framework", 263 | ); 264 | runOnlyForDeploymentPostprocessing = 0; 265 | shellPath = /bin/sh; 266 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-YIIFMDB/Pods-YIIFMDB-frameworks.sh\"\n"; 267 | showEnvVarsInLog = 0; 268 | }; 269 | A5CB46048577BBB2975AEA16 /* [CP] Check Pods Manifest.lock */ = { 270 | isa = PBXShellScriptBuildPhase; 271 | buildActionMask = 2147483647; 272 | files = ( 273 | ); 274 | inputPaths = ( 275 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 276 | "${PODS_ROOT}/Manifest.lock", 277 | ); 278 | name = "[CP] Check Pods Manifest.lock"; 279 | outputPaths = ( 280 | "$(DERIVED_FILE_DIR)/Pods-YIIFMDB-checkManifestLockResult.txt", 281 | ); 282 | runOnlyForDeploymentPostprocessing = 0; 283 | shellPath = /bin/sh; 284 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 285 | showEnvVarsInLog = 0; 286 | }; 287 | /* End PBXShellScriptBuildPhase section */ 288 | 289 | /* Begin PBXSourcesBuildPhase section */ 290 | 44FAF46A21869C09003925D4 /* Sources */ = { 291 | isa = PBXSourcesBuildPhase; 292 | buildActionMask = 2147483647; 293 | files = ( 294 | 44FAF47621869C09003925D4 /* ViewController.m in Sources */, 295 | 44FAF49A21869D9A003925D4 /* YIIParameters.m in Sources */, 296 | 44C2082A22B3BEC6008B8512 /* LCVideoModel.m in Sources */, 297 | 44FAF48121869C0A003925D4 /* main.m in Sources */, 298 | 44FAF47321869C09003925D4 /* AppDelegate.m in Sources */, 299 | 44FAF49B21869D9A003925D4 /* YIIFMDB.m in Sources */, 300 | ); 301 | runOnlyForDeploymentPostprocessing = 0; 302 | }; 303 | 44FAF48221869C0A003925D4 /* Sources */ = { 304 | isa = PBXSourcesBuildPhase; 305 | buildActionMask = 2147483647; 306 | files = ( 307 | 44FAF48B21869C0A003925D4 /* YIIFMDBUITests.m in Sources */, 308 | ); 309 | runOnlyForDeploymentPostprocessing = 0; 310 | }; 311 | /* End PBXSourcesBuildPhase section */ 312 | 313 | /* Begin PBXTargetDependency section */ 314 | 44FAF48821869C0A003925D4 /* PBXTargetDependency */ = { 315 | isa = PBXTargetDependency; 316 | target = 44FAF46D21869C09003925D4 /* YIIFMDB */; 317 | targetProxy = 44FAF48721869C0A003925D4 /* PBXContainerItemProxy */; 318 | }; 319 | /* End PBXTargetDependency section */ 320 | 321 | /* Begin PBXVariantGroup section */ 322 | 44FAF47721869C09003925D4 /* Main.storyboard */ = { 323 | isa = PBXVariantGroup; 324 | children = ( 325 | 44FAF47821869C09003925D4 /* Base */, 326 | ); 327 | name = Main.storyboard; 328 | sourceTree = ""; 329 | }; 330 | 44FAF47C21869C0A003925D4 /* LaunchScreen.storyboard */ = { 331 | isa = PBXVariantGroup; 332 | children = ( 333 | 44FAF47D21869C0A003925D4 /* Base */, 334 | ); 335 | name = LaunchScreen.storyboard; 336 | sourceTree = ""; 337 | }; 338 | /* End PBXVariantGroup section */ 339 | 340 | /* Begin XCBuildConfiguration section */ 341 | 44FAF48D21869C0A003925D4 /* Debug */ = { 342 | isa = XCBuildConfiguration; 343 | buildSettings = { 344 | ALWAYS_SEARCH_USER_PATHS = NO; 345 | CLANG_ANALYZER_NONNULL = YES; 346 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 347 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 348 | CLANG_CXX_LIBRARY = "libc++"; 349 | CLANG_ENABLE_MODULES = YES; 350 | CLANG_ENABLE_OBJC_ARC = YES; 351 | CLANG_ENABLE_OBJC_WEAK = YES; 352 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 353 | CLANG_WARN_BOOL_CONVERSION = YES; 354 | CLANG_WARN_COMMA = YES; 355 | CLANG_WARN_CONSTANT_CONVERSION = YES; 356 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 357 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 358 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 359 | CLANG_WARN_EMPTY_BODY = YES; 360 | CLANG_WARN_ENUM_CONVERSION = YES; 361 | CLANG_WARN_INFINITE_RECURSION = YES; 362 | CLANG_WARN_INT_CONVERSION = YES; 363 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 364 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 365 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 366 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 367 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 368 | CLANG_WARN_STRICT_PROTOTYPES = YES; 369 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 370 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 371 | CLANG_WARN_UNREACHABLE_CODE = YES; 372 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 373 | CODE_SIGN_IDENTITY = "iPhone Developer"; 374 | COPY_PHASE_STRIP = NO; 375 | DEBUG_INFORMATION_FORMAT = dwarf; 376 | ENABLE_STRICT_OBJC_MSGSEND = YES; 377 | ENABLE_TESTABILITY = YES; 378 | GCC_C_LANGUAGE_STANDARD = gnu11; 379 | GCC_DYNAMIC_NO_PIC = NO; 380 | GCC_NO_COMMON_BLOCKS = YES; 381 | GCC_OPTIMIZATION_LEVEL = 0; 382 | GCC_PREPROCESSOR_DEFINITIONS = ( 383 | "DEBUG=1", 384 | "$(inherited)", 385 | ); 386 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 387 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 388 | GCC_WARN_UNDECLARED_SELECTOR = YES; 389 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 390 | GCC_WARN_UNUSED_FUNCTION = YES; 391 | GCC_WARN_UNUSED_VARIABLE = YES; 392 | IPHONEOS_DEPLOYMENT_TARGET = 11.4; 393 | MTL_ENABLE_DEBUG_INFO = YES; 394 | ONLY_ACTIVE_ARCH = YES; 395 | SDKROOT = iphoneos; 396 | }; 397 | name = Debug; 398 | }; 399 | 44FAF48E21869C0A003925D4 /* Release */ = { 400 | isa = XCBuildConfiguration; 401 | buildSettings = { 402 | ALWAYS_SEARCH_USER_PATHS = NO; 403 | CLANG_ANALYZER_NONNULL = YES; 404 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 405 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 406 | CLANG_CXX_LIBRARY = "libc++"; 407 | CLANG_ENABLE_MODULES = YES; 408 | CLANG_ENABLE_OBJC_ARC = YES; 409 | CLANG_ENABLE_OBJC_WEAK = YES; 410 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 411 | CLANG_WARN_BOOL_CONVERSION = YES; 412 | CLANG_WARN_COMMA = YES; 413 | CLANG_WARN_CONSTANT_CONVERSION = YES; 414 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 415 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 416 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 417 | CLANG_WARN_EMPTY_BODY = YES; 418 | CLANG_WARN_ENUM_CONVERSION = YES; 419 | CLANG_WARN_INFINITE_RECURSION = YES; 420 | CLANG_WARN_INT_CONVERSION = YES; 421 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 422 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 423 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 424 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 425 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 426 | CLANG_WARN_STRICT_PROTOTYPES = YES; 427 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 428 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 429 | CLANG_WARN_UNREACHABLE_CODE = YES; 430 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 431 | CODE_SIGN_IDENTITY = "iPhone Developer"; 432 | COPY_PHASE_STRIP = NO; 433 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 434 | ENABLE_NS_ASSERTIONS = NO; 435 | ENABLE_STRICT_OBJC_MSGSEND = YES; 436 | GCC_C_LANGUAGE_STANDARD = gnu11; 437 | GCC_NO_COMMON_BLOCKS = YES; 438 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 439 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 440 | GCC_WARN_UNDECLARED_SELECTOR = YES; 441 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 442 | GCC_WARN_UNUSED_FUNCTION = YES; 443 | GCC_WARN_UNUSED_VARIABLE = YES; 444 | IPHONEOS_DEPLOYMENT_TARGET = 11.4; 445 | MTL_ENABLE_DEBUG_INFO = NO; 446 | SDKROOT = iphoneos; 447 | VALIDATE_PRODUCT = YES; 448 | }; 449 | name = Release; 450 | }; 451 | 44FAF49021869C0A003925D4 /* Debug */ = { 452 | isa = XCBuildConfiguration; 453 | baseConfigurationReference = 69A669F5AAF16E7F8D6CEB65 /* Pods-YIIFMDB.debug.xcconfig */; 454 | buildSettings = { 455 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 456 | CODE_SIGN_STYLE = Automatic; 457 | INFOPLIST_FILE = YIIFMDB/Info.plist; 458 | LD_RUNPATH_SEARCH_PATHS = ( 459 | "$(inherited)", 460 | "@executable_path/Frameworks", 461 | ); 462 | PRODUCT_BUNDLE_IDENTIFIER = lc.YIIFMDB; 463 | PRODUCT_NAME = "$(TARGET_NAME)"; 464 | TARGETED_DEVICE_FAMILY = "1,2"; 465 | }; 466 | name = Debug; 467 | }; 468 | 44FAF49121869C0A003925D4 /* Release */ = { 469 | isa = XCBuildConfiguration; 470 | baseConfigurationReference = F3313952066C3D8F065D78C1 /* Pods-YIIFMDB.release.xcconfig */; 471 | buildSettings = { 472 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 473 | CODE_SIGN_STYLE = Automatic; 474 | INFOPLIST_FILE = YIIFMDB/Info.plist; 475 | LD_RUNPATH_SEARCH_PATHS = ( 476 | "$(inherited)", 477 | "@executable_path/Frameworks", 478 | ); 479 | PRODUCT_BUNDLE_IDENTIFIER = lc.YIIFMDB; 480 | PRODUCT_NAME = "$(TARGET_NAME)"; 481 | TARGETED_DEVICE_FAMILY = "1,2"; 482 | }; 483 | name = Release; 484 | }; 485 | 44FAF49321869C0A003925D4 /* Debug */ = { 486 | isa = XCBuildConfiguration; 487 | buildSettings = { 488 | CODE_SIGN_STYLE = Automatic; 489 | INFOPLIST_FILE = YIIFMDBUITests/Info.plist; 490 | LD_RUNPATH_SEARCH_PATHS = ( 491 | "$(inherited)", 492 | "@executable_path/Frameworks", 493 | "@loader_path/Frameworks", 494 | ); 495 | PRODUCT_BUNDLE_IDENTIFIER = lc.YIIFMDBUITests; 496 | PRODUCT_NAME = "$(TARGET_NAME)"; 497 | TARGETED_DEVICE_FAMILY = "1,2"; 498 | TEST_TARGET_NAME = YIIFMDB; 499 | }; 500 | name = Debug; 501 | }; 502 | 44FAF49421869C0A003925D4 /* Release */ = { 503 | isa = XCBuildConfiguration; 504 | buildSettings = { 505 | CODE_SIGN_STYLE = Automatic; 506 | INFOPLIST_FILE = YIIFMDBUITests/Info.plist; 507 | LD_RUNPATH_SEARCH_PATHS = ( 508 | "$(inherited)", 509 | "@executable_path/Frameworks", 510 | "@loader_path/Frameworks", 511 | ); 512 | PRODUCT_BUNDLE_IDENTIFIER = lc.YIIFMDBUITests; 513 | PRODUCT_NAME = "$(TARGET_NAME)"; 514 | TARGETED_DEVICE_FAMILY = "1,2"; 515 | TEST_TARGET_NAME = YIIFMDB; 516 | }; 517 | name = Release; 518 | }; 519 | /* End XCBuildConfiguration section */ 520 | 521 | /* Begin XCConfigurationList section */ 522 | 44FAF46921869C09003925D4 /* Build configuration list for PBXProject "YIIFMDB" */ = { 523 | isa = XCConfigurationList; 524 | buildConfigurations = ( 525 | 44FAF48D21869C0A003925D4 /* Debug */, 526 | 44FAF48E21869C0A003925D4 /* Release */, 527 | ); 528 | defaultConfigurationIsVisible = 0; 529 | defaultConfigurationName = Release; 530 | }; 531 | 44FAF48F21869C0A003925D4 /* Build configuration list for PBXNativeTarget "YIIFMDB" */ = { 532 | isa = XCConfigurationList; 533 | buildConfigurations = ( 534 | 44FAF49021869C0A003925D4 /* Debug */, 535 | 44FAF49121869C0A003925D4 /* Release */, 536 | ); 537 | defaultConfigurationIsVisible = 0; 538 | defaultConfigurationName = Release; 539 | }; 540 | 44FAF49221869C0A003925D4 /* Build configuration list for PBXNativeTarget "YIIFMDBUITests" */ = { 541 | isa = XCConfigurationList; 542 | buildConfigurations = ( 543 | 44FAF49321869C0A003925D4 /* Debug */, 544 | 44FAF49421869C0A003925D4 /* Release */, 545 | ); 546 | defaultConfigurationIsVisible = 0; 547 | defaultConfigurationName = Release; 548 | }; 549 | /* End XCConfigurationList section */ 550 | }; 551 | rootObject = 44FAF46621869C09003925D4 /* Project object */; 552 | } 553 | -------------------------------------------------------------------------------- /YIIFMDB.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /YIIFMDB.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /YIIFMDB.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /YIIFMDB.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /YIIFMDB/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // YIIFMDB 4 | // 5 | // Created by 刘冲 on 2018/10/29. 6 | // Copyright © 2018年 lc. 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 | -------------------------------------------------------------------------------- /YIIFMDB/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // YIIFMDB 4 | // 5 | // Created by 刘冲 on 2018/10/29. 6 | // Copyright © 2018年 lc. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /YIIFMDB/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /YIIFMDB/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /YIIFMDB/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 | -------------------------------------------------------------------------------- /YIIFMDB/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 | -------------------------------------------------------------------------------- /YIIFMDB/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /YIIFMDB/LCVideoModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // LCVideoModel.h 3 | // YIIFMDB_Demo 4 | // 5 | // Created by 刘冲 on 2018/10/29. 6 | // Copyright © 2018年 lc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LCVideoModel : NSObject 12 | 13 | @property (nonatomic, copy) NSString *name; 14 | @property (nonatomic, assign) NSInteger gender; 15 | @property (nonatomic, assign) long age; 16 | @property (nonatomic, assign) float floatNumber; 17 | @property (nonatomic, assign) double doubleNumber; 18 | @property (nonatomic, assign) BOOL isMan; 19 | @property (nonatomic, strong) NSNumber *number; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /YIIFMDB/LCVideoModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // LCVideoModel.m 3 | // YIIFMDB_Demo 4 | // 5 | // Created by 刘冲 on 2018/10/29. 6 | // Copyright © 2018年 lc. All rights reserved. 7 | // 8 | 9 | #import "LCVideoModel.h" 10 | 11 | @implementation LCVideoModel 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /YIIFMDB/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // YIIFMDB 4 | // 5 | // Created by 刘冲 on 2018/10/29. 6 | // Copyright © 2018年 lc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /YIIFMDB/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // YIIFMDB_Demo 4 | // 5 | // Created by 刘冲 on 2018/10/29. 6 | // Copyright © 2018年 lc. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "LCVideoModel.h" 11 | #import "YIIFMDB.h" 12 | 13 | @interface ViewController () 14 | 15 | @property (nonatomic, strong) NSArray *> *objects; 16 | 17 | @end 18 | 19 | @implementation ViewController 20 | 21 | - (void)viewDidLoad { 22 | [super viewDidLoad]; 23 | // Do any additional setup after loading the view, typically from a nib. 24 | 25 | self.title = @"YIIFMDB使用,以LCVideoModel为例"; 26 | 27 | // 初始化数据源 28 | self.objects = @[ @[@"sql语句where配置:and(&&,与)", 29 | @"sql语句where配置:or(||,或)", 30 | @"sql语句where配置:count(数量)", 31 | @"sql语句where配置:order(排序)", 32 | @"sql语句where配置:综合运用", 33 | ], 34 | @[@"基于LCVideoModel创建一张表", 35 | @"基于LCVideoModel插入一条数据", 36 | @"基于LCVideoModel批量插入100条数据", 37 | @"删除一条数据(YIIParameters配置参数)", 38 | @"基于LCVideoModel更改一条数据", 39 | @"查询数据(YIIParameters配置参数)", 40 | @"表是否存在", 41 | @"为数据库增加一个字段", 42 | @"获取表中所有字段", 43 | @"获取表中符合条件的个数(YIIParameters配置参数)"], 44 | @[@"某一个字段的总和(sum,以主键为例)", 45 | @"某一个字段的平均值(avg,以主键为例)", 46 | @"某一个字段的最大值(max,以主键为例)", 47 | @"某一个字段的最小值(min,以主键为例)"], 48 | @[@"队列", 49 | @"事务"], 50 | ]; 51 | 52 | UITableView *tableView = [[UITableView alloc] initWithFrame:self.view.frame style:UITableViewStyleGrouped]; 53 | tableView.delegate = self; 54 | tableView.dataSource = self; 55 | [self.view addSubview:tableView]; 56 | 57 | if (@available(iOS 11.0, *)) { 58 | tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentAutomatic; 59 | } 60 | } 61 | 62 | 63 | - (void)didReceiveMemoryWarning { 64 | [super didReceiveMemoryWarning]; 65 | // Dispose of any resources that can be recreated. 66 | } 67 | 68 | 69 | #pragma mark - UITableView 代理 70 | 71 | - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { 72 | if (section == 0) { 73 | return @"sql语句where之后的参数配置"; 74 | } 75 | 76 | if (section == 1) { 77 | return @"数据库相关操作(包括增删改查)"; 78 | } 79 | 80 | if (section == 2) { 81 | return @"数据库数学运算"; 82 | } 83 | 84 | if (section == 3) { 85 | return @"线程安全操作(队列与事务)"; 86 | } 87 | 88 | return nil; 89 | } 90 | 91 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 92 | return self.objects.count; 93 | } 94 | 95 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 96 | return self.objects[section].count; 97 | } 98 | 99 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 100 | static NSString *cellID = @"ID"; 101 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID]; 102 | 103 | // Configure the cell... 104 | if (cell == nil) { 105 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID]; 106 | } 107 | 108 | cell.textLabel.text = self.objects[indexPath.section][indexPath.row]; 109 | 110 | return cell; 111 | } 112 | 113 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 114 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 115 | 116 | if (indexPath.section == 0) { 117 | if (indexPath.row == 0) { 118 | // 配置yii_pkID在大于10且小于20的参数 119 | YIIParameters *parameters = [[YIIParameters alloc] init]; 120 | [parameters andWhere:@"yii_pkID" value:@"10" relationType:YIIParametersRelationTypeGreaterThan]; 121 | [parameters andWhere:@"yii_pkID" value:@"20" relationType:YIIParametersRelationTypeLessThan]; 122 | NSLog(@"where参数为:%@", parameters.whereParameters); 123 | } 124 | 125 | if (indexPath.row == 1) { 126 | // 配置yii_pkID在小于10或者大于20的参数 127 | YIIParameters *parameters = [[YIIParameters alloc] init]; 128 | [parameters orWhere:@"yii_pkID" value:@"10" relationType:YIIParametersRelationTypeLessThan]; 129 | [parameters orWhere:@"yii_pkID" value:@"20" relationType:YIIParametersRelationTypeGreaterThan]; 130 | NSLog(@"where参数为:%@", parameters.whereParameters); 131 | } 132 | 133 | if (indexPath.row == 2) { 134 | // 配置选取数据的数量为10 135 | YIIParameters *parameters = [[YIIParameters alloc] init]; 136 | parameters.limitCount = 10; 137 | NSLog(@"where参数为:%@", parameters.whereParameters); 138 | } 139 | 140 | if (indexPath.row == 3) { 141 | // 根据某一个字段进行排序(升序为YIIParametersOrderTypeAsc,降序为YIIParametersOrderTypeDesc) 142 | YIIParameters *parameters = [[YIIParameters alloc] init]; 143 | [parameters orderByColumn:@"yii_pkID" orderType:YIIParametersOrderTypeAsc]; 144 | NSLog(@"where参数为:%@", parameters.whereParameters); 145 | } 146 | 147 | if (indexPath.row == 4) { 148 | // 参数设置:获取yii_pkID在10到50之间的数据,或者yii_pkID大于100,并且yii_pkID进行降序排列,还要将数量限制在20个 149 | YIIParameters *parameters = [[YIIParameters alloc] init]; 150 | // yii_pkID在10-50配置(and操作) 151 | [parameters andWhere:@"yii_pkID" value:@"10" relationType:YIIParametersRelationTypeGreaterThan]; 152 | [parameters andWhere:@"yii_pkID" value:@"50" relationType:YIIParametersRelationTypeLessThan]; 153 | // yii_pkID大于100(or操作) 154 | [parameters orWhere:@"yii_pkID" value:@"100" relationType:YIIParametersRelationTypeGreaterThan]; 155 | // yii_pkID降序排列 156 | [parameters orderByColumn:@"yii_pkID" orderType:YIIParametersOrderTypeDesc]; 157 | // 数量限制在20个 158 | parameters.limitCount = 20; 159 | 160 | NSLog(@"where参数为:%@", parameters.whereParameters); 161 | } 162 | } 163 | 164 | if (indexPath.section == 1) { 165 | NSString *tableName = @"video"; 166 | if (indexPath.row == 0) { 167 | YIIFMDB *db = [YIIFMDB shareDatabase]; 168 | BOOL isSuccess = [db createTableWithModelClass:[LCVideoModel class] excludedProperties:nil tableName:tableName]; 169 | if (isSuccess) { 170 | NSLog(@"创建一张表成功"); 171 | } else { 172 | NSLog(@"创建一张表失败"); 173 | } 174 | } 175 | 176 | if (indexPath.row == 1) { 177 | LCVideoModel *model = [[LCVideoModel alloc] init]; 178 | model.name = [NSString stringWithFormat:@"lc%d", arc4random() % 100]; 179 | model.gender = arc4random() % 2; 180 | model.age = arc4random() % 80; 181 | model.floatNumber = (arc4random() % 20) / 100.0; 182 | model.doubleNumber = (arc4random() % 20) / 100.0; 183 | model.isMan = arc4random() % 2; 184 | model.number = @(arc4random() % 10); 185 | 186 | YIIFMDB *db = [YIIFMDB shareDatabase]; 187 | BOOL isSuccess = [db insertWithModel:model tableName:tableName]; 188 | if (isSuccess) { 189 | NSLog(@"插入一条数据成功"); 190 | } else { 191 | NSLog(@"插入一条数据失败"); 192 | } 193 | } 194 | 195 | if (indexPath.row == 2) { 196 | NSMutableArray *array = [NSMutableArray array]; 197 | for (int index = 0; index < 100; index++) { 198 | LCVideoModel *model = [[LCVideoModel alloc] init]; 199 | model.name = [NSString stringWithFormat:@"lc%d", (arc4random() % 100)]; 200 | model.gender = arc4random() % 2; 201 | model.age = arc4random() % 80; 202 | model.floatNumber = (arc4random() % 20) / 100.0; 203 | model.doubleNumber = (arc4random() % 20) / 100.0; 204 | model.isMan = arc4random() % 2; 205 | model.number = @(arc4random() % 10); 206 | [array addObject:model]; 207 | } 208 | 209 | YIIFMDB *db = [YIIFMDB shareDatabase]; 210 | [db insertWithModels:array tableName:tableName]; 211 | } 212 | 213 | if (indexPath.row == 3) { 214 | YIIFMDB *db = [YIIFMDB shareDatabase]; 215 | YIIParameters *parameters = [[YIIParameters alloc] init]; 216 | parameters.whereParameters = @"name = 'lc17'"; 217 | // db.primaryKey 是数据库的主键,这条语句意思是删除主键 = 1的那条数据 218 | // [parameters andWhere:db.primaryKey value:@"1" relationType:YIIParametersRelationTypeEqualTo]; 219 | BOOL isSuccess = [db deleteFromTable:tableName whereParameters:parameters]; 220 | if (isSuccess) { 221 | NSLog(@"删除数据成功"); 222 | } else { 223 | NSLog(@"删除数据失败"); 224 | } 225 | } 226 | 227 | if (indexPath.row == 4) { 228 | YIIFMDB *db = [YIIFMDB shareDatabase]; 229 | YIIParameters *parameters = [[YIIParameters alloc] init]; 230 | [parameters andWhere:db.primaryKey value:@"10" relationType:YIIParametersRelationTypeEqualTo]; 231 | 232 | 233 | BOOL isSuccess = [db updateTable:tableName dictionary:@{@"name": @"monkey"} whereParameters:parameters]; 234 | if (isSuccess) { 235 | NSLog(@"更新数据成功"); 236 | } else { 237 | NSLog(@"更新数据失败"); 238 | } 239 | } 240 | 241 | if (indexPath.row == 5) { 242 | YIIFMDB *db = [YIIFMDB shareDatabase]; 243 | YIIParameters *parameters = [[YIIParameters alloc] init]; 244 | [parameters andWhere:db.primaryKey value:@"5" relationType:YIIParametersRelationTypeLessThan]; 245 | 246 | NSLog(@"主键值小于5的所有数据:%@", [db queryFromTable:tableName model:[LCVideoModel class] whereParameters:parameters]); 247 | } 248 | 249 | if (indexPath.row == 6) { 250 | YIIFMDB *db = [YIIFMDB shareDatabase]; 251 | BOOL isExist = [db existTable:tableName]; 252 | if (isExist) { 253 | NSLog(@"表存在"); 254 | } else { 255 | NSLog(@"表不存在"); 256 | } 257 | } 258 | 259 | if (indexPath.row == 7) { 260 | // 为数据库增加一个字段:字段名为description,字符串类型,类型参考YIIFMDBValueType 261 | YIIFMDB *db = [YIIFMDB shareDatabase]; 262 | BOOL isSuccess = [db alterTable:tableName column:@"description" type:YIIFMDBValueTypeString]; 263 | if (isSuccess) { 264 | NSLog(@"增加一个字段成功"); 265 | } else { 266 | NSLog(@"增加一个字段失败"); 267 | } 268 | } 269 | 270 | if (indexPath.row == 8) { 271 | YIIFMDB *db = [YIIFMDB shareDatabase]; 272 | NSLog(@"表中所有字段为:%@", [db getAllColumnsFromTable:tableName]); 273 | } 274 | 275 | if (indexPath.row == 9) { 276 | // 获取主键>10的数据个数 277 | YIIFMDB *db = [YIIFMDB shareDatabase]; 278 | YIIParameters *parameters = [[YIIParameters alloc] init]; 279 | [parameters andWhere:db.primaryKey value:@"10" relationType:YIIParametersRelationTypeGreaterThan]; 280 | NSLog(@"获取主键>10的数据个数为:%lld", [db numberOfItemsFromTable:tableName whereParameters:parameters]); 281 | } 282 | } 283 | 284 | if (indexPath.section == 2) { 285 | NSString *tableName = @"video"; 286 | if (indexPath.row == 0) { 287 | // 左右主键的值加起来的综合 288 | YIIFMDB *db = [YIIFMDB shareDatabase]; 289 | NSLog(@"主键值的总和为:%f", [db numberWithMathType:YIIFMDBMathTypeSum table:tableName column:db.primaryKey whereParameters:nil]); 290 | } 291 | 292 | if (indexPath.row == 1) { 293 | // 左右主键的值的平均值 294 | YIIFMDB *db = [YIIFMDB shareDatabase]; 295 | NSLog(@"主键值的平均值为:%f", [db numberWithMathType:YIIFMDBMathTypeAvg table:tableName column:db.primaryKey whereParameters:nil]); 296 | } 297 | 298 | if (indexPath.row == 2) { 299 | // 左右主键的最大值 300 | YIIFMDB *db = [YIIFMDB shareDatabase]; 301 | NSLog(@"主键值的最大值为:%f", [db numberWithMathType:YIIFMDBMathTypeMax table:tableName column:db.primaryKey whereParameters:nil]); 302 | } 303 | 304 | if (indexPath.row == 3) { 305 | // 左右主键的最小值 306 | YIIFMDB *db = [YIIFMDB shareDatabase]; 307 | NSLog(@"主键值的最小值为:%f", [db numberWithMathType:YIIFMDBMathTypeMin table:tableName column:db.primaryKey whereParameters:nil]); 308 | } 309 | } 310 | 311 | if (indexPath.section == 3) { 312 | if (indexPath.row == 0) { 313 | YIIFMDB *db = [YIIFMDB shareDatabase]; 314 | [db inDatabase:^{ 315 | // 增删改查放在此代码块里执行则可以保证线程安全 316 | }]; 317 | } 318 | 319 | if (indexPath.row == 1) { 320 | YIIFMDB *db = [YIIFMDB shareDatabase]; 321 | [db inTransaction:^(BOOL *rollback) { 322 | // 如果某一个操作失误,则可以执行回滚操作 323 | BOOL isSuccess = YES; // 数据库操作是否操作成功 324 | if (!isSuccess) { 325 | *rollback = YES; 326 | 327 | return ; 328 | } 329 | }]; 330 | } 331 | } 332 | } 333 | 334 | @end 335 | -------------------------------------------------------------------------------- /YIIFMDB/YIIFMDB/YIIFMDB.h: -------------------------------------------------------------------------------- 1 | // 2 | // YIIFMDB.h 3 | // FMDataBaseManager 4 | // 5 | // Created by 刘冲 on 2018/10/22. 6 | // Copyright © 2018年 lc. All rights reserved. 7 | // 8 | 9 | // Github: https://github.com/liuchongfaye/YIIFMDB 10 | 11 | #import 12 | #import "YIIParameters.h" 13 | @class FMDatabase; 14 | 15 | // 数据库支持的类型(如果不满足条件以下条件,那么在后续会增加) 16 | typedef NS_ENUM(NSUInteger, YIIFMDBValueType) { 17 | YIIFMDBValueTypeString, // 字符串 18 | YIIFMDBValueTypeInteger, // 整型,长整型,bool值也可以作为integer 19 | YIIFMDBValueTypeFloat, // 浮点型,double 20 | YIIFMDBValueTypeData, // 二进制数据 21 | }; 22 | 23 | // 数学运算的类型 24 | typedef NS_ENUM(NSUInteger, YIIFMDBMathType) { 25 | YIIFMDBMathTypeSum, // 总和 26 | YIIFMDBMathTypeAvg, // 平均值 27 | YIIFMDBMathTypeMax, // 最大值 28 | YIIFMDBMathTypeMin, // 最小值 29 | }; 30 | 31 | @class YIIFMDB; 32 | 33 | typedef void(^YIICompletionBlock)(YIIFMDB *db, NSDictionary *unrecognizedProperties); 34 | 35 | @interface YIIFMDB : NSObject 36 | 37 | #pragma mark - 创建单例 38 | /** 39 | * 此三个方法是通过单例来创建数据库,路径默认是在NSDocumentDirectory(),名称为YIIFMDB.sqlite,此三个方法是一个单例方法,为此,只要使用任何一个创建成功之后,返回值都是同一个实例对象 40 | * 推荐使用@selector(shareDatabase) 41 | dataBaseName 数据库的名称,比如可以设置为"Project.sqlite",也可以设置为nil。但是,如果dataBaseName == nil,那么数据库的名称就会默认是YIIFMDB.sqlite 42 | dataBasePath 数据库的文件夹路径,如果dataBasePath == nil,那么默认就是NSDocumentDirectory() 43 | * @return 返回当前的单例对象 44 | */ 45 | + (instancetype)shareDatabase; 46 | + (instancetype)shareDatabaseForName:(NSString * _Nullable)dataBaseName path:(NSString * _Nullable)dataBasePath; 47 | 48 | #pragma mark - 数据库相关属性 49 | 50 | /** 51 | * 当前的数据库 52 | */ 53 | @property (nonatomic, readonly, copy) FMDatabase *currentDatabase; 54 | 55 | /** 56 | * 当前的数据库所在路径 57 | */ 58 | @property (nonatomic, readonly, copy) NSString *currentDatabasePath; 59 | 60 | /** 61 | * 获取到数据库中的主键的key,返回"yii_pkID",在配置YIIParameters时可能会用到 62 | */ 63 | @property (nonatomic, readonly, copy) NSString *primaryKey; 64 | 65 | #pragma mark - 是否打印log 66 | /** 67 | * 是否打印log,默认是NO 68 | */ 69 | @property (nonatomic, assign) BOOL shouldOpenDebugLog; 70 | 71 | #pragma mark - 根据ModelClass去创建表 72 | 73 | /** 74 | * 根据传入的Model去创建表(推荐使用此方法) 75 | * @param modelClass 根据传入的Model的class去创建表,其中表名就是model的类名。 76 | 同时,model里面的属性名称就作为表的key值,属性的value的类型也就是表里面的value的类型,如value可以是NSString,integer,float,bool等,详情请参考YIIFMDBValueType 77 | * @param excludedProperties 被排除掉属性,这些属性被排除掉之后则不会存在数据库当中 78 | * @param tableName 表名,不可以为nil 79 | * @return 是否创建成功 80 | */ 81 | - (BOOL)createTableWithModelClass:(Class _Nonnull)modelClass excludedProperties:(NSArray * _Nullable)excludedProperties tableName:(NSString * _Nonnull)tableName; 82 | 83 | #pragma mark - 插入数据 84 | 85 | /** 86 | * 插入一条数据(推荐使用) 87 | * @param model 需要插入Model 88 | * @param tableName 表名,不可以为nil 89 | * @return 是否插入成功 90 | */ 91 | - (BOOL)insertWithModel:(id _Nonnull)model tableName:(NSString * _Nonnull)tableName; 92 | 93 | /** 94 | * 插入多条数据 95 | * @param models 需要插入的存放Model的数组。其中必须要保证数组内的Model都是同一类型的Model 96 | * @param tableName 表名,不可以为nil 97 | * 在连续插入多条数据的时候,很有可能会出现插入不成功的情况,如果想要联调,请将shouldOpenDebugLog设为YES 98 | */ 99 | - (void)insertWithModels:(NSArray *)models tableName:(NSString * _Nonnull)tableName; 100 | 101 | #pragma mark - 删除数据 102 | 103 | /** 104 | * 根据参数删除表中的数据 105 | * @param tableName 表的名字。如果是自定义的表名,那么就传入自定义的表名,如果未自定义,那么传入model的类名,如NSStringFromClass([Model class]).不可以为nil 106 | * @param parameters 参数,YIIParameters决定了sql语句"where"后面的参数。具体用法参考YIIParameters类. 107 | * 如果parameters = nil,或者parameters仅仅是一个实例对象,而没有执行YIIParameters的方法进行参数配置,那么parameters.whereParameters就会不存在默认删除数据 108 | * @return 是否删除成功 109 | */ 110 | - (BOOL)deleteFromTable:(NSString * _Nonnull)tableName whereParameters:(YIIParameters *)parameters; 111 | 112 | /** 113 | * 删除所有数据 114 | * @param tableName 同上 115 | * @return 同上 116 | */ 117 | - (BOOL)deleteAllDataFromTable:(NSString * _Nonnull)tableName; 118 | 119 | #pragma mark - 更改数据 120 | 121 | /** 122 | * 根据参数删除表中的数据 123 | * @param tableName 表的名字,不可以为nil 124 | * @param dictionary 要更新的key-value.在我经验来看,更改数据只是更新部分数据,而不是全部,所以这里使用的是字典,而不是传入的model,而且这样还会增加效率 125 | * @param parameters 参数,YIIParameters决定了sql语句"where"后面的参数。具体用法参考YIIParameters类 126 | */ 127 | - (BOOL)updateTable:(NSString * _Nonnull)tableName dictionary:(NSDictionary * _Nonnull)dictionary whereParameters:(YIIParameters *)parameters; 128 | 129 | #pragma mark - 查询数据 130 | 131 | /** 132 | * 根据参数删除表中的数据 133 | * @param tableName 表的名字,不可以为nil 134 | * @param modelClass modelClass里面的属性名称就作为表的key值,属性的value的类型也就是表里面的value的类型,如value可以是NSString,integer,float,bool等,详情请参考YIIFMDBValueType 135 | * @param parameters 参数,YIIParameters决定了sql语句"where"后面的参数。具体用法参考YIIParameters类 136 | * @return 返回所有符合条件的数据 137 | */ 138 | - (NSArray *)queryFromTable:(NSString * _Nonnull)tableName model:(Class _Nonnull)modelClass whereParameters:(YIIParameters *)parameters; 139 | 140 | #pragma mark - 除去增删改查之外常用的功能 141 | 142 | /** 143 | * 打开数据库 144 | */ 145 | - (BOOL)openDatabase; 146 | 147 | /** 148 | * 关闭数据库 149 | */ 150 | - (BOOL)closeDatabase; 151 | 152 | /** 153 | * 表是否存在 154 | * @param tableName 表的名字 155 | * @return 表是否存在 156 | */ 157 | - (BOOL)existTable:(NSString * _Nonnull)tableName; 158 | 159 | /** 160 | * 为一个表增加字段 161 | * @param tableName 表的名字 162 | * @param column 要增加的字段 163 | * @param type 增加的字段类型 164 | * @return 是否添加成功 165 | */ 166 | - (BOOL)alterTable:(NSString * _Nonnull)tableName column:(NSString * _Nonnull)column type:(YIIFMDBValueType)type; 167 | 168 | /** 169 | * 删除一张表 170 | * @param tableName 表的名字 171 | * @return 是否删除成功 172 | */ 173 | - (BOOL)dropTable:(NSString * _Nonnull)tableName; 174 | 175 | /** 176 | * 获取某一个表中所有的字段名 177 | * @param tableName 表的名字 178 | * @return 所有字段名 179 | */ 180 | - (NSArray *)getAllColumnsFromTable:(NSString * _Nonnull)tableName; 181 | 182 | /** 183 | * 获取表中有多少条数据 184 | * @param tableName 表的名字 185 | * @param parameters 参数,YIIParameters决定了sql语句"where"后面的参数。具体用法参考YIIParameters类.如果parameters = nil,或者parameters.whereParameters为空,那么就是获得列表中所有的数据个数 186 | * @return 数据的个数 187 | */ 188 | - (long long int)numberOfItemsFromTable:(NSString * _Nonnull)tableName whereParameters:(YIIParameters * _Nullable)parameters; 189 | 190 | /** 191 | * 数学相关操作 192 | * @param type 数学运算的type,决定如何运算,请参考YIIFMDBMathType枚举 193 | * @param tableName 表的名字 194 | * @param parameters 参数,YIIParameters决定了sql语句"where"后面的参数。具体用法参考YIIParameters类.如果parameters = nil,或者parameters.whereParameters为空,那么默认是列表中所有符合条件的数据 195 | * @return 计算的值 196 | */ 197 | - (double)numberWithMathType:(YIIFMDBMathType)type table:(NSString * _Nonnull)tableName column:(NSString * _Nonnull)column whereParameters:(YIIParameters * _Nullable)parameters; 198 | 199 | #pragma mark - 线程安全操作 200 | 201 | // FMDB所提供的接口并不是线程安全的,而在使用过程当中,为了线程安全的操作,必须要与队列相关联才行。使用以下两个方法可以是的线程安全 202 | 203 | /** 204 | * 线程队列的使用 205 | * @param block block,将数据库操作放到block里执行可以保证线程安全 206 | */ 207 | - (void)inDatabase:(dispatch_block_t)block; 208 | 209 | /** 210 | * 事务的使用 211 | * @param block block,将数据库操作放到block里执行可以保证线程安全 212 | */ 213 | - (void)inTransaction:(void(^)(BOOL *rollback))block; 214 | 215 | @end 216 | -------------------------------------------------------------------------------- /YIIFMDB/YIIFMDB/YIIFMDB.m: -------------------------------------------------------------------------------- 1 | // 2 | // YIIFMDB.m 3 | // FMDataBaseManager 4 | // 5 | // Created by 刘冲 on 2018/10/22. 6 | // Copyright © 2018年 lc. All rights reserved. 7 | // 8 | 9 | #import "YIIFMDB.h" 10 | #import "FMDB.h" 11 | #import 12 | 13 | @interface YIIFMDB () 14 | 15 | @property (nonatomic, copy) NSString *databasePath; 16 | @property (nonatomic, strong) FMDatabaseQueue *databaseQueue; 17 | @property (nonatomic, strong) FMDatabase *database; 18 | 19 | @end 20 | 21 | @implementation YIIFMDB { 22 | // 保证创建sql语句时的线程安全 23 | dispatch_semaphore_t _sqlLock; 24 | } 25 | 26 | static NSString * const yii_primary_key = @"yii_pkID"; // 主键 27 | static NSString * const yii_sql_text = @"text"; // 字符串 28 | static NSString * const yii_sql_real = @"real"; // 浮点型 29 | static NSString * const yii_sql_blob = @"blob"; // 二进制 30 | static NSString * const yii_sql_integer = @"integer"; // 整型 31 | 32 | #pragma mark - Override Methods 33 | 34 | - (FMDatabaseQueue *)databaseQueue { 35 | if (!_databaseQueue) { 36 | _databaseQueue = [FMDatabaseQueue databaseQueueWithPath:self.databasePath]; 37 | // 关闭当前数据库 38 | [self.database close]; 39 | // 将FMDatabaseQueue当中的数据库替换掉当前的数据库 40 | self.database = [_databaseQueue valueForKey:@"_db"]; 41 | } 42 | 43 | return _databaseQueue; 44 | } 45 | 46 | #pragma mark - 创建单例 47 | 48 | + (instancetype)shareDatabase { 49 | return [self shareDatabaseForName:nil path:nil]; 50 | } 51 | 52 | + (instancetype)shareDatabaseForName:(NSString *)dataBaseName path:(NSString *)dataBasePath { 53 | static dispatch_once_t onceToken; 54 | static YIIFMDB *yiiFMDB = nil; 55 | dispatch_once(&onceToken, ^{ 56 | yiiFMDB = [[YIIFMDB alloc] init]; 57 | 58 | NSString *dbName = dataBaseName ? : @"YIIFMDB.sqlite"; 59 | NSString *dbPath = nil; 60 | if (dataBasePath) { 61 | dbPath = [dataBasePath stringByAppendingPathComponent:dbName]; 62 | } else { 63 | dbPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:dbName]; 64 | } 65 | 66 | FMDatabase *dataBase = [FMDatabase databaseWithPath:dbPath]; 67 | yiiFMDB.database = dataBase; 68 | yiiFMDB.databasePath = dbPath; 69 | }); 70 | 71 | if (![yiiFMDB.database open]) { 72 | [yiiFMDB log:@"数据库未能打开"]; 73 | } 74 | 75 | return yiiFMDB; 76 | } 77 | 78 | - (instancetype)init { 79 | if (self = [super init]) { 80 | _sqlLock = dispatch_semaphore_create(1); 81 | } 82 | 83 | return self; 84 | } 85 | 86 | #pragma mark - 数据库相关属性 87 | 88 | - (FMDatabase *)currentDatabase { 89 | return self.database; 90 | } 91 | 92 | - (NSString *)currentDatabasePath { 93 | return self.databasePath; 94 | } 95 | 96 | - (NSString *)primaryKey { 97 | return yii_primary_key; 98 | } 99 | 100 | #pragma mark - 根据ModelClass去创建表 101 | 102 | - (BOOL)createTableWithModelClass:(Class)modelClass excludedProperties:(NSArray *)excludedProperties tableName:(NSString *)tableName { 103 | if (!YIIIsStringValid(tableName)) { 104 | [self log:@"tableName必须是字符串,且不能为nil"]; 105 | 106 | return NO; 107 | } 108 | 109 | YIILock(_sqlLock); 110 | NSString *pkID = yii_primary_key; 111 | NSMutableString *sqliteString = [NSMutableString stringWithFormat:@"create table if not exists %@ (%@ integer primary key", tableName, pkID]; 112 | YIIUnlock(_sqlLock); 113 | 114 | // 基于runtime获取model的所有属性以及类型 115 | NSDictionary *properties = [self getPropertiesWithModel:modelClass]; 116 | for (NSString *key in properties) { 117 | if ([excludedProperties containsObject:key]) { 118 | continue; 119 | } 120 | 121 | [sqliteString appendFormat:@", %@ %@", key, properties[key]]; 122 | } 123 | [sqliteString appendString:@")"]; 124 | 125 | BOOL isSuccess = [self.database executeUpdate:sqliteString]; 126 | 127 | return isSuccess; 128 | } 129 | 130 | #pragma mark - 插入 131 | 132 | - (BOOL)insertWithModel:(id)model tableName:(NSString *)tableName { 133 | if (!YIIIsStringValid(tableName)) { 134 | [self log:@"tableName必须是字符串,且不能为nil"]; 135 | 136 | return NO; 137 | } 138 | 139 | if (model) { 140 | YIILock(_sqlLock); 141 | NSMutableString *sqliteString = [NSMutableString stringWithFormat:@"insert into %@ (", tableName]; 142 | NSArray *columns = [self getAllColumnsFromTable:tableName dataBase:self.database isIncludingPrimaryKey:NO]; 143 | NSMutableArray *values = [NSMutableArray array]; 144 | for (int index = 0; index < columns.count; index++) { 145 | [values addObject:@"?"]; 146 | } 147 | [sqliteString appendFormat:@"%@) values (%@)", [columns componentsJoinedByString:@","], [values componentsJoinedByString:@","]]; 148 | YIIUnlock(_sqlLock); 149 | 150 | NSArray *arguments = [self getValuesFromModel:model columns:columns]; 151 | BOOL isSuccess = [self.database executeUpdate:sqliteString withArgumentsInArray:arguments]; 152 | 153 | if (!isSuccess) { 154 | [self log:[NSString stringWithFormat:@"插入数据失败,错误的model = %@", model]]; 155 | } 156 | 157 | return isSuccess; 158 | } else { 159 | 160 | return NO; 161 | } 162 | } 163 | 164 | - (void)insertWithModels:(NSArray *)models tableName:(NSString *)tableName { 165 | if (!YIIIsStringValid(tableName)) { 166 | [self log:@"tableName必须是字符串,且不能为nil"]; 167 | 168 | return; 169 | } 170 | 171 | if (models && [models isKindOfClass:[NSArray class]] && models.count > 0) { 172 | // 这里实际上可以与上面的方法混合使用,但是这个样子的话,初始化sqlite语句的时候就会出现多次运算,为了效率,这里与上面的方法进行了解耦 173 | YIILock(_sqlLock); 174 | NSMutableString *sqliteString = [NSMutableString stringWithFormat:@"insert into %@ (", tableName]; 175 | 176 | NSArray *columns = [self getAllColumnsFromTable:tableName dataBase:self.database isIncludingPrimaryKey:NO]; 177 | NSMutableArray *values = [NSMutableArray array]; 178 | for (int index = 0; index < columns.count; index++) { 179 | [values addObject:@"?"]; 180 | } 181 | [sqliteString appendFormat:@"%@) values (%@)", [columns componentsJoinedByString:@","], [values componentsJoinedByString:@","]]; 182 | YIIUnlock(_sqlLock); 183 | 184 | for (id model in models) { 185 | NSArray *arguments = [self getValuesFromModel:model columns:columns]; 186 | 187 | BOOL isSuccess = [self.database executeUpdate:sqliteString withArgumentsInArray:arguments]; 188 | if (!isSuccess) { 189 | [self log:[NSString stringWithFormat:@"插入数据失败,错误的model = %@", model]]; 190 | } 191 | } 192 | } else { 193 | [self log:@"插入数据的数据源有误"]; 194 | } 195 | } 196 | 197 | #pragma mark - 删除 198 | 199 | - (BOOL)deleteFromTable:(NSString *)tableName whereParameters:(YIIParameters *)parameters { 200 | if (!YIIIsStringValid(tableName)) { 201 | [self log:@"tableName必须是字符串,且不能为nil"]; 202 | 203 | return NO; 204 | } 205 | 206 | YIILock(_sqlLock); 207 | NSMutableString *sqliteString = [NSMutableString stringWithFormat:@"delete from %@", tableName]; 208 | if (parameters && YIIIsStringValid(parameters.whereParameters)) { 209 | [sqliteString appendFormat:@" where %@", parameters.whereParameters]; 210 | } 211 | YIIUnlock(_sqlLock); 212 | 213 | BOOL isSuccess = [self.database executeUpdate:sqliteString]; 214 | 215 | return isSuccess; 216 | } 217 | 218 | - (BOOL)deleteAllDataFromTable:(NSString *)tableName { 219 | return [self deleteFromTable:tableName whereParameters:nil]; 220 | } 221 | 222 | #pragma mark - 更改数据 223 | 224 | - (BOOL)updateTable:(NSString *)tableName dictionary:(NSDictionary *)dictionary whereParameters:(YIIParameters *)parameters { 225 | if (!YIIIsStringValid(tableName)) { 226 | [self log:@"tableName必须是字符串,且不能为nil"]; 227 | 228 | return NO; 229 | } 230 | 231 | if (dictionary.allKeys.count <= 0) { 232 | [self log:@"要更新的数据不能为nil"]; 233 | return NO; 234 | } 235 | 236 | YIILock(_sqlLock); 237 | NSMutableString *sqliteString = [NSMutableString stringWithFormat:@"update %@ set ", tableName]; 238 | NSMutableArray *values = [NSMutableArray array]; 239 | for (NSString *key in dictionary) { 240 | if ([key isEqualToString:yii_primary_key]) { 241 | continue; 242 | } 243 | 244 | [sqliteString appendFormat:@"%@ = ? ", key]; 245 | [values addObject:dictionary[key]]; 246 | } 247 | YIIUnlock(_sqlLock); 248 | 249 | if (values.count > 0) { 250 | if (YIIIsStringValid(parameters.whereParameters)) { 251 | [sqliteString appendFormat:@"where %@", parameters.whereParameters]; 252 | } else { 253 | [self log:@"sql语句当中,where后面的参数为nil"]; 254 | [sqliteString deleteCharactersInRange:NSMakeRange(sqliteString.length-1, 1)]; 255 | } 256 | 257 | return [self.database executeUpdate:sqliteString withArgumentsInArray:values]; 258 | } else { 259 | [self log:@"要更新的数据不能仅仅含有主键"]; 260 | 261 | return NO; 262 | } 263 | } 264 | 265 | #pragma mark - 查询数据 266 | 267 | - (NSArray *)queryFromTable:(NSString *)tableName model:(Class)modelClass whereParameters:(YIIParameters *)parameters { 268 | if (!YIIIsStringValid(tableName)) { 269 | [self log:@"tableName必须是字符串,且不能为nil"]; 270 | 271 | return nil; 272 | } 273 | 274 | YIILock(_sqlLock); 275 | NSMutableArray *array = [NSMutableArray array]; 276 | 277 | NSMutableString *sqliteString = [NSMutableString stringWithFormat:@"select * from %@", tableName]; 278 | if (parameters && YIIIsStringValid(parameters.whereParameters)) { 279 | [sqliteString appendFormat:@" where %@", parameters.whereParameters]; 280 | } 281 | YIIUnlock(_sqlLock); 282 | 283 | NSDictionary *properties = [self getPropertiesWithModel:modelClass]; 284 | FMResultSet *resultSet = [self.database executeQuery:sqliteString]; 285 | while ([resultSet next]) { 286 | NSMutableDictionary *dict = [NSMutableDictionary dictionary]; 287 | for (NSString *key in properties) { 288 | NSString *type = properties[key]; 289 | // 根据数据类型从数据库当中获取数据 290 | if ([type isEqualToString:yii_sql_text]) { 291 | // 字符串 292 | dict[key] = [resultSet stringForColumn:key] ? : @""; 293 | } else if ([type isEqualToString:yii_sql_integer]) { 294 | // 整型 295 | dict[key] = @([resultSet longLongIntForColumn:key]); 296 | } else if ([type isEqualToString:yii_sql_real]) { 297 | // 浮点型 298 | dict[key] = @([resultSet doubleForColumn:key]); 299 | } else if ([type isEqualToString:yii_sql_blob]) { 300 | // 二进制 301 | id value = [resultSet dataForColumn:key]; 302 | if (value) { 303 | dict[key] = value; 304 | } 305 | } 306 | } 307 | 308 | [array addObject:dict]; 309 | } 310 | 311 | return (array.count > 0 ? array : nil); 312 | } 313 | 314 | #pragma mark - 除去增删改查之外常用的功能 315 | 316 | - (BOOL)openDatabase { 317 | return [self.database open]; 318 | } 319 | 320 | - (BOOL)closeDatabase { 321 | return [self.database close]; 322 | } 323 | 324 | - (BOOL)existTable:(NSString *)tableName { 325 | if (YIIIsStringValid(tableName)) { 326 | FMResultSet *resultSet = [self.database executeQuery:@"select count(*) as 'count' from sqlite_master where type ='table' and name = ?", tableName]; 327 | while ([resultSet next]) 328 | { 329 | NSInteger count = [resultSet intForColumn:@"count"]; 330 | return ((count == 0) ? NO : YES); 331 | } 332 | 333 | return NO; 334 | } else { 335 | [self log:@"tableName必须是字符串,且不能为nil"]; 336 | 337 | return NO; 338 | } 339 | } 340 | 341 | - (BOOL)alterTable:(NSString *)tableName column:(NSString *)column type:(YIIFMDBValueType)type { 342 | if (!YIIIsStringValid(tableName)) { 343 | [self log:@"tableName必须是字符串,且不能为nil"]; 344 | 345 | return NO; 346 | } 347 | 348 | if (!YIIIsStringValid(column)) { 349 | [self log:@"要新增的column必须是字符串,且不能为nil"]; 350 | 351 | return NO; 352 | } 353 | 354 | YIILock(_sqlLock); 355 | NSString *typeString = nil; 356 | switch (type) { 357 | case YIIFMDBValueTypeString: 358 | typeString = yii_sql_text; 359 | break; 360 | case YIIFMDBValueTypeInteger: 361 | typeString = yii_sql_integer; 362 | break; 363 | case YIIFMDBValueTypeFloat: 364 | typeString = yii_sql_real; 365 | break; 366 | case YIIFMDBValueTypeData: 367 | typeString = yii_sql_blob; 368 | break; 369 | default: 370 | typeString = @""; 371 | break; 372 | } 373 | NSString *sqliteString = [NSString stringWithFormat:@"alter table %@ add column %@ %@", tableName, column, typeString]; 374 | YIIUnlock(_sqlLock); 375 | 376 | return [self.database executeUpdate:sqliteString]; 377 | } 378 | 379 | - (BOOL)dropTable:(NSString *)tableName { 380 | if (!YIIIsStringValid(tableName)) { 381 | [self log:@"tableName必须是字符串,且不能为nil"]; 382 | 383 | return NO; 384 | } 385 | 386 | YIILock(_sqlLock); 387 | NSString *sqliteString = [NSString stringWithFormat:@"drop table %@", tableName]; 388 | YIIUnlock(_sqlLock); 389 | 390 | return [self.database executeUpdate:sqliteString]; 391 | } 392 | 393 | - (NSArray *)getAllColumnsFromTable:(NSString *)tableName { 394 | return [self getAllColumnsFromTable:tableName dataBase:self.database isIncludingPrimaryKey:YES]; 395 | } 396 | 397 | - (long long int)numberOfItemsFromTable:(NSString *)tableName whereParameters:(YIIParameters * _Nullable)parameters { 398 | if (!YIIIsStringValid(tableName)) { 399 | [self log:@"tableName必须是字符串,且不能为nil"]; 400 | } 401 | 402 | YIILock(_sqlLock); 403 | NSMutableString *sqliteString = [NSMutableString stringWithFormat:@"select count(*) as 'count' from %@", tableName]; 404 | if (parameters && YIIIsStringValid(parameters.whereParameters)) { 405 | [sqliteString appendFormat:@" where %@", parameters.whereParameters]; 406 | } 407 | YIIUnlock(_sqlLock); 408 | FMResultSet *resultSet = [self.database executeQuery:sqliteString]; 409 | while ([resultSet next]) { 410 | return [resultSet longLongIntForColumn:@"count"]; 411 | } 412 | 413 | return 0; 414 | } 415 | 416 | - (double)numberWithMathType:(YIIFMDBMathType)type table:(NSString *)tableName column:(NSString *)column whereParameters:(YIIParameters *)parameters { 417 | if (!YIIIsStringValid(tableName)) { 418 | [self log:@"tableName必须是字符串,且不能为nil"]; 419 | 420 | return 0.0; 421 | } 422 | 423 | if (!YIIIsStringValid(column)) { 424 | [self log:@"要新增的column必须是字符串,且不能为nil"]; 425 | 426 | return 0.0; 427 | } 428 | 429 | YIILock(_sqlLock); 430 | NSMutableString *sqliteString = nil; 431 | NSString *operation = nil; 432 | switch (type) { 433 | case YIIFMDBMathTypeSum: 434 | operation = @"sum"; 435 | break; 436 | case YIIFMDBMathTypeAvg: 437 | operation = @"avg"; 438 | break; 439 | case YIIFMDBMathTypeMax: 440 | operation = @"max"; 441 | break; 442 | case YIIFMDBMathTypeMin: 443 | operation = @"min"; 444 | break; 445 | default: 446 | break; 447 | } 448 | if (YIIIsStringValid(operation)) { 449 | sqliteString = [NSMutableString stringWithFormat:@"select %@(%@) %@Count from %@", operation, column, operation, tableName]; 450 | } else { 451 | [self log:@"不支持当前运算"]; 452 | } 453 | 454 | if (parameters && YIIIsStringValid(parameters.whereParameters)) { 455 | [sqliteString appendFormat:@" where %@", parameters.whereParameters]; 456 | } 457 | YIIUnlock(_sqlLock); 458 | FMResultSet *resultSet = [self.database executeQuery:sqliteString]; 459 | double value = 0.0; 460 | while ([resultSet next]) { 461 | value = [resultSet doubleForColumn:[NSString stringWithFormat:@"%@Count", operation]]; 462 | } 463 | 464 | return value; 465 | } 466 | 467 | #pragma mark - 线程安全操作 468 | 469 | - (void)inDatabase:(dispatch_block_t)block { 470 | [self.databaseQueue inDatabase:^(FMDatabase * _Nonnull db) { 471 | if (block) { 472 | block(); 473 | } 474 | }]; 475 | } 476 | 477 | - (void)inTransaction:(void (^)(BOOL *))block { 478 | [self.databaseQueue inTransaction:^(FMDatabase * _Nonnull db, BOOL * _Nonnull rollback) { 479 | if (block) { 480 | block(rollback); 481 | } 482 | }]; 483 | } 484 | 485 | #pragma mark - 数据库相关操作 486 | 487 | // 获取数据库里的所有元素 488 | - (NSArray *)getAllColumnsFromTable:(NSString *)tableName dataBase:(FMDatabase *)dataBase isIncludingPrimaryKey:(BOOL)isIncluding { 489 | NSMutableArray *columns = [NSMutableArray array]; 490 | 491 | FMResultSet *resultSet = [dataBase getTableSchema:tableName]; 492 | while ([resultSet next]) { 493 | NSString *columnName = [resultSet stringForColumn:@"name"]; 494 | if ([columnName isEqualToString:yii_primary_key] && !isIncluding) { 495 | continue; 496 | } 497 | [columns addObject:columnName]; 498 | } 499 | 500 | return columns; 501 | } 502 | 503 | #pragma mark - Private Method 504 | 505 | /** 506 | * 基于runtime获取model的所有属性以及类型 507 | * 根据传入的ModelClass去获取所有的属性的key以及类型type,返回值的字典的key就是modelClass的属性,value就是modelClass的属性对应的type 508 | */ 509 | - (NSDictionary *)getPropertiesWithModel:(Class)modelClass { 510 | NSMutableDictionary *dict = [NSMutableDictionary dictionary]; 511 | 512 | unsigned int count; 513 | objc_property_t *propertyList = class_copyPropertyList(modelClass, &count); 514 | for (int index = 0; index < count; index++) { 515 | objc_property_t property = propertyList[index]; 516 | NSString *key = [NSString stringWithFormat:@"%s", property_getName(property)]; 517 | NSString *type = nil; 518 | NSString *attributes = [NSString stringWithFormat:@"%s", property_getAttributes(property)]; 519 | 520 | if ([attributes hasPrefix:@"T@\"NSString\""]) { 521 | type = yii_sql_text; 522 | } else if ([attributes hasPrefix:@"Tf"] || [attributes hasPrefix:@"Td"]) { 523 | type = yii_sql_real; 524 | } else if ([attributes hasPrefix:@"T@\"NSData\""]) { 525 | type = yii_sql_blob; 526 | } else if ([attributes hasPrefix:@"Ti"] || [attributes hasPrefix:@"TI"] || [attributes hasPrefix:@"Tl"] || [attributes hasPrefix:@"TL"] || [attributes hasPrefix:@"Tq"] || [attributes hasPrefix:@"TQ"] || [attributes hasPrefix:@"Ts"] || [attributes hasPrefix:@"TS"] || [attributes hasPrefix:@"TB"] || [attributes hasPrefix:@"T@\"NSNumber\""]) { 527 | type = yii_sql_integer; 528 | } 529 | 530 | if (type) { 531 | [dict setObject:type forKey:key]; 532 | } else { 533 | [self log:[NSString stringWithFormat:@"不支持的属性:key = %@, attributes = %@", key, attributes]]; 534 | } 535 | } 536 | 537 | free(propertyList); 538 | 539 | return dict; 540 | } 541 | 542 | // 根据keys获取到model里面的所有values 543 | - (NSArray *)getValuesFromModel:(id _Nonnull)model columns:(NSArray *)columns { 544 | NSMutableArray *array = [NSMutableArray array]; 545 | for (NSString *column in columns) { 546 | id value = [model valueForKey:column]; 547 | [array addObject:value ? : @""]; 548 | } 549 | 550 | return array; 551 | } 552 | 553 | BOOL YIIIsStringValid(id object) { 554 | return [object isKindOfClass:[NSString class]] && ((NSString*)object).length > 0; 555 | } 556 | 557 | // 加锁 558 | void YIILock(dispatch_semaphore_t semaphore) { 559 | dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); 560 | } 561 | 562 | // 解锁 563 | void YIIUnlock(dispatch_semaphore_t semaphore) { 564 | dispatch_semaphore_signal(semaphore); 565 | } 566 | 567 | // 打印log 568 | - (void)log:(NSString *)string { 569 | if (self.shouldOpenDebugLog) { 570 | NSLog(@"%@", string); 571 | } 572 | } 573 | 574 | @end 575 | -------------------------------------------------------------------------------- /YIIFMDB/YIIFMDB/YIIParameters.h: -------------------------------------------------------------------------------- 1 | // 2 | // YIIParameters.h 3 | // FMDataBaseManager 4 | // 5 | // Created by 刘冲 on 2018/10/22. 6 | // Copyright © 2018年 lc. All rights reserved. 7 | // 8 | 9 | // Github: https://github.com/liuchongfaye/YIIFMDB 10 | 11 | #import 12 | 13 | // 参数相关的关系 14 | typedef NS_ENUM(NSUInteger, YIIParametersRelationType) { 15 | YIIParametersRelationTypeEqualTo, // 数学运算@"=",等于 16 | YIIParametersRelationTypeUnequalTo, // 数学运算@"!=",不等于 17 | YIIParametersRelationTypeGreaterThan, // 数学运算@">",大于 18 | YIIParametersRelationTypeGreaterThanOrEqualTo, // 数学运算@">=",大于等于 19 | YIIParametersRelationTypeLessThan, // 数学运算@"<",小于 20 | YIIParametersRelationTypeLessThanOrEqualTo, // 数学运算@"<=",小于等于 21 | YIIParametersRelationTypeLike, // 字符串运算@"like",模糊查询 22 | }; 23 | 24 | // 排序顺序 25 | typedef NS_ENUM(NSUInteger, YIIParametersOrderType) { 26 | YIIParametersOrderTypeAsc, // 升序 27 | YIIParametersOrderTypeDesc, // 降序 28 | }; 29 | 30 | @interface YIIParameters : NSObject 31 | 32 | #pragma mark - sql语句当中为where之后的条件增加参数 33 | 34 | /** 35 | * 筛选条件的数量限制 36 | */ 37 | @property (nonatomic, assign) NSInteger limitCount; 38 | 39 | /** 40 | * and(&&,与)操作 41 | * @param column 数据库中表的key值 42 | * @param value column值对应的value值 43 | * @param relationType column与value之间的关系 44 | * 比如只执行[andWhere:@"age" value:18 relationType:YIIParametersRelationTypeGreaterThan],那么where后面的参数会变成"age > 18" 45 | */ 46 | - (void)andWhere:(NSString * _Nonnull)column value:(id _Nonnull)value relationType:(YIIParametersRelationType)relationType; 47 | 48 | /** 49 | * or(||,或)操作 50 | * @param column 数据库中表的key值 51 | * @param value column值对应的value值 52 | * @param relationType column与value之间的关系 53 | * 比如只执行[andWhere:@"age" value:18 relationType:YIIParametersRelationTypeGreaterThan],那么where后面的参数会变成"age > 18" 54 | */ 55 | - (void)orWhere:(NSString * _Nonnull)column value:(id _Nonnull)value relationType:(YIIParametersRelationType)relationType; 56 | 57 | /** 58 | * 设置排序结果 59 | * @param column 排序的字段 60 | * @param orderType 排序选择,有升序和降序 61 | * 比如执行[ orderByColumn:@"yii_pkID" orderType:YIIParametersOrderTypeAsc],那么对应的sql语句就是@"order by yii_pkID asc",意思就是根据"yii_plID"来进行升序排列 62 | */ 63 | - (void)orderByColumn:(NSString * _Nonnull)column orderType:(YIIParametersOrderType)orderType; 64 | 65 | /** 66 | * sql语句的参数,也就是sql语句当中,where之后的参数. 67 | * 值得一提的是,如果设置了这个参数,那么在属性whereParameters上面的方法都无效 68 | * 如果不设置这个参数,那么调用此属性的get方法则会获取到以上的方法所形成的sql语句 69 | */ 70 | @property (nonatomic, copy) NSString *whereParameters; 71 | 72 | @end 73 | -------------------------------------------------------------------------------- /YIIFMDB/YIIFMDB/YIIParameters.m: -------------------------------------------------------------------------------- 1 | // 2 | // YIIParameters.m 3 | // FMDataBaseManager 4 | // 5 | // Created by 刘冲 on 2018/10/22. 6 | // Copyright © 2018年 lc. All rights reserved. 7 | // 8 | 9 | #import "YIIParameters.h" 10 | 11 | @interface YIIParameters () 12 | 13 | @property (nonatomic, strong) NSMutableArray *andParameters; // and参数 14 | @property (nonatomic, strong) NSMutableArray *orParameters; // or参数 15 | @property (nonatomic, copy) NSString *orderString; // 排序语句 16 | 17 | 18 | @end 19 | 20 | @implementation YIIParameters 21 | 22 | #pragma mark - Override Methods 23 | 24 | - (NSMutableArray *)andParameters { 25 | if (!_andParameters) { 26 | _andParameters = [NSMutableArray array]; 27 | } 28 | 29 | return _andParameters; 30 | } 31 | 32 | - (NSMutableArray *)orParameters { 33 | if (!_orParameters) { 34 | _orParameters = [NSMutableArray array]; 35 | } 36 | 37 | return _orParameters; 38 | } 39 | 40 | - (NSString *)whereParameters { 41 | if (_whereParameters) { 42 | return _whereParameters; 43 | } else { 44 | NSMutableString *string = [NSMutableString string]; 45 | NSString *andString = [self.andParameters componentsJoinedByString:@" and "]; 46 | NSString *orString = [self.orParameters componentsJoinedByString:@" or "]; 47 | if (andString && andString.length > 0) { 48 | [string appendFormat:@"%@", andString]; 49 | } 50 | 51 | if (orString && orString.length > 0) { 52 | [string appendFormat:@"%@%@", (string.length > 0 ? @" or " : @""), orString]; 53 | } 54 | 55 | if (self.orderString) { 56 | [string appendFormat:@" %@", self.orderString]; 57 | } 58 | 59 | if (self.limitCount > 0) { 60 | [string appendFormat:@" limit %ld", (long)self.limitCount]; 61 | } 62 | 63 | return (NSString *)(string.length > 0 ? string : nil); 64 | } 65 | } 66 | 67 | #pragma mark - 配置参数 68 | 69 | - (void)andWhere:(NSString *)column value:(id)value relationType:(YIIParametersRelationType)relationType { 70 | NSString *string = nil; 71 | switch (relationType) { 72 | case YIIParametersRelationTypeEqualTo: 73 | string = [NSString stringWithFormat:@"%@ = \"%@\"", column, value]; 74 | break; 75 | case YIIParametersRelationTypeUnequalTo: 76 | string = [NSString stringWithFormat:@"%@ != \"%@\"", column, value]; 77 | break; 78 | case YIIParametersRelationTypeGreaterThan: 79 | string = [NSString stringWithFormat:@"%@ > \"%@\"", column, value]; 80 | break; 81 | case YIIParametersRelationTypeGreaterThanOrEqualTo: 82 | string = [NSString stringWithFormat:@"%@ >= \"%@\"", column, value]; 83 | break; 84 | case YIIParametersRelationTypeLessThan: 85 | string = [NSString stringWithFormat:@"%@ < \"%@\"", column, value]; 86 | break; 87 | case YIIParametersRelationTypeLessThanOrEqualTo: 88 | string = [NSString stringWithFormat:@"%@ <= \"%@\"", column, value]; 89 | break; 90 | case YIIParametersRelationTypeLike: 91 | string = [NSString stringWithFormat:@"%@ like '%@'", column, value]; 92 | break; 93 | default: 94 | break; 95 | } 96 | if (string) { 97 | [self.andParameters addObject:string]; 98 | } 99 | } 100 | 101 | - (void)orWhere:(NSString *)column value:(id)value relationType:(YIIParametersRelationType)relationType { 102 | NSString *string = nil; 103 | switch (relationType) { 104 | case YIIParametersRelationTypeEqualTo: 105 | string = [NSString stringWithFormat:@"%@ = \"%@\"", column, value]; 106 | break; 107 | case YIIParametersRelationTypeUnequalTo: 108 | string = [NSString stringWithFormat:@"%@ != \"%@\"", column, value]; 109 | break; 110 | case YIIParametersRelationTypeGreaterThan: 111 | string = [NSString stringWithFormat:@"%@ > \"%@\"", column, value]; 112 | break; 113 | case YIIParametersRelationTypeGreaterThanOrEqualTo: 114 | string = [NSString stringWithFormat:@"%@ >= \"%@\"", column, value]; 115 | break; 116 | case YIIParametersRelationTypeLessThan: 117 | string = [NSString stringWithFormat:@"%@ < \"%@\"", column, value]; 118 | break; 119 | case YIIParametersRelationTypeLessThanOrEqualTo: 120 | string = [NSString stringWithFormat:@"%@ <= \"%@\"", column, value]; 121 | break; 122 | default: 123 | break; 124 | } 125 | if (string) { 126 | [self.orParameters addObject:string]; 127 | } 128 | } 129 | 130 | - (void)orderByColumn:(NSString *)column orderType:(YIIParametersOrderType)orderType { 131 | if (orderType == YIIParametersOrderTypeAsc) { 132 | self.orderString = [NSString stringWithFormat:@"order by %@ asc", column]; 133 | } else if (orderType == YIIParametersOrderTypeDesc) { 134 | self.orderString = [NSString stringWithFormat:@"order by %@ desc", column]; 135 | } 136 | } 137 | 138 | @end 139 | -------------------------------------------------------------------------------- /YIIFMDB/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // YIIFMDB 4 | // 5 | // Created by 刘冲 on 2018/10/29. 6 | // Copyright © 2018年 lc. 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 | -------------------------------------------------------------------------------- /YIIFMDBUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /YIIFMDBUITests/YIIFMDBUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // YIIFMDBUITests.m 3 | // YIIFMDBUITests 4 | // 5 | // Created by 刘冲 on 2018/10/29. 6 | // Copyright © 2018年 lc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YIIFMDBUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation YIIFMDBUITests 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 | --------------------------------------------------------------------------------