From 0d8e01caf0e9a70627b7a2cba8cf1bc0b746b05c Mon Sep 17 00:00:00 2001 From: jianglongjian <2511067577@qq.com> Date: Thu, 28 Jun 2018 18:57:28 +0800 Subject: [PATCH 01/17] add statement cache and test case --- CTPersistance.xcodeproj/project.pbxproj | 4 ++ .../QueryCommand/CTPersistanceQueryCommand.m | 47 ++++++++++++++++- .../Statement/CTPersistanceSqlStatement.h | 12 +++++ .../Statement/CTPersistanceSqlStatement.m | 26 ++++++---- .../CTPersistenceTestStatementCache.m | 51 +++++++++++++++++++ 5 files changed, 130 insertions(+), 10 deletions(-) create mode 100644 CTPersistanceTests/CTPersistenceTestStatementCache.m diff --git a/CTPersistance.xcodeproj/project.pbxproj b/CTPersistance.xcodeproj/project.pbxproj index b37d288..f345632 100644 --- a/CTPersistance.xcodeproj/project.pbxproj +++ b/CTPersistance.xcodeproj/project.pbxproj @@ -81,6 +81,7 @@ 4AF38F2D1F6905DF00801322 /* TestRecord.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E00BA1F2CC25A00E6A4DA /* TestRecord.m */; }; 4AF38F2F1F69068700801322 /* CTPersistanceTestTransaction.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AF38F2E1F69068700801322 /* CTPersistanceTestTransaction.m */; }; 6284DE2109EF43B7A3D56F18 /* libPods-CTPersistance.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 24975DE09A62B809BFED181C /* libPods-CTPersistance.a */; }; + 78B4B74020E4F16C001836CF /* CTPersistenceTestStatementCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 78B4B73F20E4F16C001836CF /* CTPersistenceTestStatementCache.m */; }; DAEEF1BE411F617114764D0F /* libPods-CTPersistanceTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D31FADFF56BDD50C7FD9C94 /* libPods-CTPersistanceTests.a */; }; /* End PBXBuildFile section */ @@ -233,6 +234,7 @@ 4AE5541320AD1F190020BAC7 /* CTPersistanceTestUpsert.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTPersistanceTestUpsert.m; sourceTree = ""; }; 4AF38F2E1F69068700801322 /* CTPersistanceTestTransaction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CTPersistanceTestTransaction.m; sourceTree = ""; }; 4D31FADFF56BDD50C7FD9C94 /* libPods-CTPersistanceTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-CTPersistanceTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 78B4B73F20E4F16C001836CF /* CTPersistenceTestStatementCache.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTPersistenceTestStatementCache.m; sourceTree = ""; }; 89D41C6489863CB58D4966E5 /* Pods-CTPersistance.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CTPersistance.debug.xcconfig"; path = "Pods/Target Support Files/Pods-CTPersistance/Pods-CTPersistance.debug.xcconfig"; sourceTree = ""; }; AD102503C09A04454242FAC0 /* Pods-CTPersistanceTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CTPersistanceTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-CTPersistanceTests/Pods-CTPersistanceTests.debug.xcconfig"; sourceTree = ""; }; D9D5304E203B48D737D87E6F /* Pods-CTPersistanceTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CTPersistanceTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-CTPersistanceTests/Pods-CTPersistanceTests.release.xcconfig"; sourceTree = ""; }; @@ -349,6 +351,7 @@ 4AC74A891FBEAEBD0092B636 /* CTPersistanceTestTableIndex.m */, 4A76096620A16C82009908A6 /* CTPersistanceTestChangeKey.m */, 4AE5541320AD1F190020BAC7 /* CTPersistanceTestUpsert.m */, + 78B4B73F20E4F16C001836CF /* CTPersistenceTestStatementCache.m */, 4A7844C41F32C1F500CF4809 /* MigrationTestModel */, 4A0E00441F2C384E00E6A4DA /* Info.plist */, ); @@ -1239,6 +1242,7 @@ files = ( 4AC74A8A1FBEAEBD0092B636 /* CTPersistanceTestTableIndex.m in Sources */, 4A17A96C1F32D0C0009356D7 /* TestMiagratorVersion_3_to_4.m in Sources */, + 78B4B74020E4F16C001836CF /* CTPersistenceTestStatementCache.m in Sources */, 4A17A9941F32F392009356D7 /* TestTableVersion1.m in Sources */, 47C1D71920A145DE00020AB5 /* Target_TestDatabase.m in Sources */, 4A17A9951F32F392009356D7 /* TestRecordVersion2.m in Sources */, diff --git a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceQueryCommand.m b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceQueryCommand.m index d656672..5297d74 100644 --- a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceQueryCommand.m +++ b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceQueryCommand.m @@ -17,6 +17,8 @@ @interface CTPersistanceQueryCommand () @property (nonatomic, strong) NSString *databaseName; @property (nonatomic, assign) BOOL shouldKeepDatabase; +@property (nonatomic, strong) NSMutableDictionary *cachedStatements; + @end @implementation CTPersistanceQueryCommand @@ -26,6 +28,7 @@ - (instancetype)initWithDatabase:(CTPersistanceDataBase *)database { self = [super init]; if (self) { + self.cachedStatements = [NSMutableDictionary dictionary]; self.shouldKeepDatabase = YES; self.database = database; } @@ -36,6 +39,7 @@ - (instancetype)initWithDatabaseName:(NSString *)databaseName { self = [super init]; if (self) { + self.cachedStatements = [NSMutableDictionary dictionary]; self.shouldKeepDatabase = NO; self.databaseName = databaseName; } @@ -44,10 +48,51 @@ - (instancetype)initWithDatabaseName:(NSString *)databaseName - (CTPersistanceSqlStatement *)compileSqlString:(NSString *)sqlString bindValueList:(NSMutableArray *)bindValueList error:(NSError *__autoreleasing *)error { - CTPersistanceSqlStatement *statement = [[CTPersistanceSqlStatement alloc] initWithSqlString:sqlString bindValueList:bindValueList database:self.database error:error]; + CTPersistanceSqlStatement *statement = nil; + + statement = [self cachedStatementForSqlString:sqlString]; + + if (statement == nil) { + + statement = [[CTPersistanceSqlStatement alloc] initWithSqlString:sqlString bindValueList:bindValueList database:self.database error:error]; + [self setCachedStatement:statement forSqlString:sqlString]; + + } else { + + sqlite3_stmt *stmt = statement.statement; + + [bindValueList enumerateObjectsUsingBlock:^(NSInvocation * _Nonnull bindInvocation, NSUInteger idx, BOOL * _Nonnull stop) { + [bindInvocation setArgument:(void *)&stmt atIndex:2]; + [bindInvocation invoke]; + }]; + [bindValueList removeAllObjects]; + + + } + return statement; } +#pragma mark - statement cache +- (CTPersistanceSqlStatement *)cachedStatementForSqlString:(NSString *)sqlString +{ + return [self.cachedStatements objectForKey:sqlString]; +} + +- (void)setCachedStatement:(CTPersistanceSqlStatement *)statement forSqlString:(NSString *)sqlString +{ + [self.cachedStatements setObject:statement forKey:sqlString]; +} + +- (void)clearCachedStatements +{ + for (CTPersistanceSqlStatement *statement in [self.cachedStatements objectEnumerator]) { + [statement close]; + } + + [self.cachedStatements removeAllObjects]; +} + #pragma mark - getters and setters - (CTPersistanceDataBase *)database { diff --git a/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.h b/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.h index 7dba16b..5957415 100644 --- a/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.h +++ b/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.h @@ -31,4 +31,16 @@ */ - (NSArray *)fetchWithError:(NSError **)error; +/** + close statement + */ +- (void)close; + +/** + reset statement + */ +- (void)reset; + +@property (nonatomic, assign, readonly) long useCount; +@property (nonatomic, unsafe_unretained, readonly) sqlite3_stmt *statement; @end diff --git a/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m b/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m index 12b5c48..9e2e037 100644 --- a/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m +++ b/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m @@ -12,7 +12,9 @@ @interface CTPersistanceSqlStatement () -@property (nonatomic, unsafe_unretained) sqlite3_stmt *statement; +@property (nonatomic, assign, readwrite) long useCount; +@property (nonatomic, unsafe_unretained, readwrite) sqlite3_stmt *statement; + @property (nonatomic, weak) CTPersistanceDataBase *database; @end @@ -50,11 +52,20 @@ - (instancetype)initWithSqlString:(NSString *)sqlString bindValueList:(NSMutable return self; } +- (void)close { + sqlite3_finalize(self.statement); + self.statement = nil; +} + +- (void)reset { + self.useCount += 1; + sqlite3_reset(self.statement); +} + - (BOOL)executeWithError:(NSError *__autoreleasing *)error { if (error != NULL && *error != nil) { - sqlite3_finalize(self.statement); - self.statement = nil; + [self close]; return NO; } @@ -70,8 +81,7 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error return NO; } - sqlite3_finalize(statement); - self.statement = nil; + [self reset]; return YES; } @@ -79,8 +89,7 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error - (NSArray *)fetchWithError:(NSError *__autoreleasing *)error { if (error != NULL && *error != nil) { - sqlite3_finalize(self.statement); - self.statement = nil; + [self close]; return nil; } @@ -145,8 +154,7 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error [resultsArray addObject:result]; } - sqlite3_finalize(statement); - self.statement = nil; + [self reset]; return resultsArray; } diff --git a/CTPersistanceTests/CTPersistenceTestStatementCache.m b/CTPersistanceTests/CTPersistenceTestStatementCache.m new file mode 100644 index 0000000..349f40e --- /dev/null +++ b/CTPersistanceTests/CTPersistenceTestStatementCache.m @@ -0,0 +1,51 @@ +// +// CTPersistenceTestStatementCache.m +// CTPersistanceTests +// +// Created by zl on 2018/6/28. +// Copyright © 2018年 casa. All rights reserved. +// + +#import +#import "CTPersistance.h" + +@interface CTPersistenceTestStatementCache : XCTestCase + +@end + +@implementation CTPersistenceTestStatementCache + +- (void)setUp { + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + + CTPersistanceQueryCommand *queryCommand = [[CTPersistanceQueryCommand alloc] initWithDatabaseName:@"testStatementCache.sqlite"]; + NSString *tableName = @"test_statement_cache_t"; + + NSString *deleteSqlString = [NSString stringWithFormat:@"delete from `%@`;",tableName]; + [[queryCommand compileSqlString:deleteSqlString bindValueList:nil error:NULL] executeWithError:NULL]; +} + +- (void)testStatementCache { + CTPersistanceQueryCommand *queryCommand = [[CTPersistanceQueryCommand alloc] initWithDatabaseName:@"testStatementCache.sqlite"]; + NSDictionary *columnInfo = @{@"book_id": @"INTEGER"}; + NSString *tableName = @"test_statement_cache_t"; + + [[queryCommand createTable:tableName columnInfo:columnInfo error:NULL] executeWithError:NULL]; + + NSDictionary *item0 = @{@"book_id" : @0}; + NSDictionary *item1 = @{@"book_id" : @1}; + NSDictionary *item2 = @{@"book_id" : @2}; + + CTPersistanceSqlStatement *statement = [queryCommand insertTable:tableName columnInfo:columnInfo dataList:@[item0] error:NULL]; + [statement executeWithError:NULL]; + + [[queryCommand insertTable:tableName columnInfo:columnInfo dataList:@[item1] error:NULL] executeWithError:NULL]; + [[queryCommand insertTable:tableName columnInfo:columnInfo dataList:@[item2] error:NULL] executeWithError:NULL]; + + XCTAssertEqual([statement useCount], 3); +} + +@end From a106db8624a4ca93b44b742085ca71447f1d6362 Mon Sep 17 00:00:00 2001 From: Casa Taloyum Date: Mon, 2 Jul 2018 11:40:51 +0800 Subject: [PATCH 02/17] modification --- CTPersistanceTests/CTPersistanceTestUpsert.m | 38 ++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/CTPersistanceTests/CTPersistanceTestUpsert.m b/CTPersistanceTests/CTPersistanceTestUpsert.m index 636b4fa..b46f44d 100644 --- a/CTPersistanceTests/CTPersistanceTestUpsert.m +++ b/CTPersistanceTests/CTPersistanceTestUpsert.m @@ -43,6 +43,44 @@ - (void)tearDown { [super tearDown]; } +- (void)testUpdateToEmptyTable +{ + // clean all data + [self.testTable truncate]; + NSUInteger recordCount = [self.testTable countTotalRecord]; + XCTAssertEqual(recordCount, 0); + + TestRecord *testRecord = [[TestRecord alloc] init]; + testRecord.age = @(999); + testRecord.name = @"casa999"; + testRecord.avatar = [testRecord.name dataUsingEncoding:NSUTF8StringEncoding]; + testRecord.progress = @(999); + testRecord.isCelebrity = @(999 % 2); + testRecord.nilValue = nil; + testRecord.timeStamp = 123; + + NSError *error = nil; + [self.testTable upsertRecord:testRecord uniqKeyName:@"age" shouldUpdateNilValueToDatabase:YES error:&error]; + + XCTAssertNil(error); + recordCount = [self.testTable countTotalRecord]; + XCTAssertEqual(recordCount, 1); + + // recorver data + NSInteger count = 10; + while (count --> 0) { + TestRecord *testRecord = [[TestRecord alloc] init]; + testRecord.age = @(count); + testRecord.name = [NSString stringWithFormat:@"casa%ld", (long)count]; + testRecord.avatar = [testRecord.name dataUsingEncoding:NSUTF8StringEncoding]; + testRecord.progress = @(count); + testRecord.isCelebrity = @(count % 2); + testRecord.nilValue = nil; + testRecord.timeStamp = 123; + [self.testTable insertRecord:testRecord error:NULL]; + } +} + - (void)testTransaction { [CTPersistanceTransaction performTranscationWithBlock:^(BOOL *shouldRollback) { NSInteger count = [self.testTable countTotalRecord]; From 723c8787aa7201d74740541176fa011eb97bf5ba Mon Sep 17 00:00:00 2001 From: Casa Taloyum Date: Mon, 2 Jul 2018 11:45:53 +0800 Subject: [PATCH 03/17] 182 --- CTPersistance.podspec | 2 +- .../Table/Categories/Upsert/CTPersistanceTable+Upsert.m | 3 +++ CTPersistanceTests/CTPersistanceTestUpsert.m | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CTPersistance.podspec b/CTPersistance.podspec index 10cbd3b..957bb88 100644 --- a/CTPersistance.podspec +++ b/CTPersistance.podspec @@ -16,7 +16,7 @@ Pod::Spec.new do |s| # s.name = "CTPersistance" - s.version = "181" + s.version = "182" s.summary = "Objective-C Model Layer with SQLite." s.description = <<-DESC diff --git a/CTPersistance/CTPersistance/Table/Categories/Upsert/CTPersistanceTable+Upsert.m b/CTPersistance/CTPersistance/Table/Categories/Upsert/CTPersistanceTable+Upsert.m index 570fc1c..30161d4 100644 --- a/CTPersistance/CTPersistance/Table/Categories/Upsert/CTPersistanceTable+Upsert.m +++ b/CTPersistance/CTPersistance/Table/Categories/Upsert/CTPersistanceTable+Upsert.m @@ -84,6 +84,9 @@ - (void)upsertRecord:(NSObject *)record uniqKeyName [weakSelf updateKeyValueList:valueToUpdate primaryKeyValue:[record valueForKey:weakSelf.child.primaryKeyName] error:error]; + if (weakSelf.queryCommand.rowsChanged.integerValue == 0) { + [weakSelf insertRecord:record error:error]; + } } } diff --git a/CTPersistanceTests/CTPersistanceTestUpsert.m b/CTPersistanceTests/CTPersistanceTestUpsert.m index b46f44d..481892a 100644 --- a/CTPersistanceTests/CTPersistanceTestUpsert.m +++ b/CTPersistanceTests/CTPersistanceTestUpsert.m @@ -51,6 +51,7 @@ - (void)testUpdateToEmptyTable XCTAssertEqual(recordCount, 0); TestRecord *testRecord = [[TestRecord alloc] init]; + testRecord.primaryKey = @(1); testRecord.age = @(999); testRecord.name = @"casa999"; testRecord.avatar = [testRecord.name dataUsingEncoding:NSUTF8StringEncoding]; From 84b3d5835df41702cedab577f1c6e183d743fc2c Mon Sep 17 00:00:00 2001 From: Casa Taloyum Date: Mon, 2 Jul 2018 11:46:27 +0800 Subject: [PATCH 04/17] 183 --- CTPersistance.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CTPersistance.podspec b/CTPersistance.podspec index 957bb88..1f3cc64 100644 --- a/CTPersistance.podspec +++ b/CTPersistance.podspec @@ -16,7 +16,7 @@ Pod::Spec.new do |s| # s.name = "CTPersistance" - s.version = "182" + s.version = "183" s.summary = "Objective-C Model Layer with SQLite." s.description = <<-DESC From dacff522aed16e8e75c0e4f5d4db8b0eb5e438ce Mon Sep 17 00:00:00 2001 From: Casa Taloyum Date: Mon, 2 Jul 2018 11:47:33 +0800 Subject: [PATCH 05/17] 184 --- CTPersistance.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CTPersistance.podspec b/CTPersistance.podspec index 1f3cc64..6f11a6a 100644 --- a/CTPersistance.podspec +++ b/CTPersistance.podspec @@ -16,7 +16,7 @@ Pod::Spec.new do |s| # s.name = "CTPersistance" - s.version = "183" + s.version = "184" s.summary = "Objective-C Model Layer with SQLite." s.description = <<-DESC From 9b714e9b64983f173cb0eb29670e26b4c1c94407 Mon Sep 17 00:00:00 2001 From: jianglongjian <2511067577@qq.com> Date: Tue, 10 Jul 2018 17:18:59 +0800 Subject: [PATCH 06/17] create statementCacheManager to manager statement cache; --- CTPersistance.xcodeproj/project.pbxproj | 10 +- .../QueryCommand/CTPersistanceQueryCommand.m | 48 +------- .../CTPersistanceStatementCacheManager.h | 19 ++++ .../CTPersistanceStatementCacheManager.m | 103 ++++++++++++++++++ .../Statement/CTPersistanceSqlStatement.h | 12 -- .../Statement/CTPersistanceSqlStatement.m | 76 +++++++++---- .../CTPersistenceTestStatementCache.m | 51 --------- 7 files changed, 185 insertions(+), 134 deletions(-) create mode 100644 CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.h create mode 100644 CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m delete mode 100644 CTPersistanceTests/CTPersistenceTestStatementCache.m diff --git a/CTPersistance.xcodeproj/project.pbxproj b/CTPersistance.xcodeproj/project.pbxproj index f345632..9d5dce3 100644 --- a/CTPersistance.xcodeproj/project.pbxproj +++ b/CTPersistance.xcodeproj/project.pbxproj @@ -81,7 +81,7 @@ 4AF38F2D1F6905DF00801322 /* TestRecord.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E00BA1F2CC25A00E6A4DA /* TestRecord.m */; }; 4AF38F2F1F69068700801322 /* CTPersistanceTestTransaction.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AF38F2E1F69068700801322 /* CTPersistanceTestTransaction.m */; }; 6284DE2109EF43B7A3D56F18 /* libPods-CTPersistance.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 24975DE09A62B809BFED181C /* libPods-CTPersistance.a */; }; - 78B4B74020E4F16C001836CF /* CTPersistenceTestStatementCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 78B4B73F20E4F16C001836CF /* CTPersistenceTestStatementCache.m */; }; + 7866EEC620F0ABB300819E75 /* CTPersistanceStatementCacheManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 7866EEC520F0ABB300819E75 /* CTPersistanceStatementCacheManager.m */; }; DAEEF1BE411F617114764D0F /* libPods-CTPersistanceTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D31FADFF56BDD50C7FD9C94 /* libPods-CTPersistanceTests.a */; }; /* End PBXBuildFile section */ @@ -234,7 +234,8 @@ 4AE5541320AD1F190020BAC7 /* CTPersistanceTestUpsert.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTPersistanceTestUpsert.m; sourceTree = ""; }; 4AF38F2E1F69068700801322 /* CTPersistanceTestTransaction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CTPersistanceTestTransaction.m; sourceTree = ""; }; 4D31FADFF56BDD50C7FD9C94 /* libPods-CTPersistanceTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-CTPersistanceTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 78B4B73F20E4F16C001836CF /* CTPersistenceTestStatementCache.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTPersistenceTestStatementCache.m; sourceTree = ""; }; + 7866EEC420F0ABB300819E75 /* CTPersistanceStatementCacheManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CTPersistanceStatementCacheManager.h; sourceTree = ""; }; + 7866EEC520F0ABB300819E75 /* CTPersistanceStatementCacheManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTPersistanceStatementCacheManager.m; sourceTree = ""; }; 89D41C6489863CB58D4966E5 /* Pods-CTPersistance.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CTPersistance.debug.xcconfig"; path = "Pods/Target Support Files/Pods-CTPersistance/Pods-CTPersistance.debug.xcconfig"; sourceTree = ""; }; AD102503C09A04454242FAC0 /* Pods-CTPersistanceTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CTPersistanceTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-CTPersistanceTests/Pods-CTPersistanceTests.debug.xcconfig"; sourceTree = ""; }; D9D5304E203B48D737D87E6F /* Pods-CTPersistanceTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CTPersistanceTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-CTPersistanceTests/Pods-CTPersistanceTests.release.xcconfig"; sourceTree = ""; }; @@ -351,7 +352,6 @@ 4AC74A891FBEAEBD0092B636 /* CTPersistanceTestTableIndex.m */, 4A76096620A16C82009908A6 /* CTPersistanceTestChangeKey.m */, 4AE5541320AD1F190020BAC7 /* CTPersistanceTestUpsert.m */, - 78B4B73F20E4F16C001836CF /* CTPersistenceTestStatementCache.m */, 4A7844C41F32C1F500CF4809 /* MigrationTestModel */, 4A0E00441F2C384E00E6A4DA /* Info.plist */, ); @@ -474,6 +474,8 @@ 4A0E00741F2CBD6300E6A4DA /* QueryCommand */ = { isa = PBXGroup; children = ( + 7866EEC420F0ABB300819E75 /* CTPersistanceStatementCacheManager.h */, + 7866EEC520F0ABB300819E75 /* CTPersistanceStatementCacheManager.m */, 4A5E3BEE1F403A3200946749 /* Statement */, 4A0E00751F2CBD6300E6A4DA /* Categories */, 4A0E00821F2CBD6300E6A4DA /* CTPersistanceQueryCommand.h */, @@ -1217,6 +1219,7 @@ 4A0238741F3FFBA800EE91C7 /* TestTable.m in Sources */, 4AC74A631FBE7DC60092B636 /* ViewRecordAndMergeDemoViewController.m in Sources */, 4AC74A731FBE7EC90092B636 /* ItemDetailTable.m in Sources */, + 7866EEC620F0ABB300819E75 /* CTPersistanceStatementCacheManager.m in Sources */, 4A0E002A1F2C384E00E6A4DA /* main.m in Sources */, 4A5E3BF11F403A4A00946749 /* CTPersistanceSqlStatement.m in Sources */, 4ACE635B1F340E54007F3E6D /* NSDictionary+KeyValueBind.m in Sources */, @@ -1242,7 +1245,6 @@ files = ( 4AC74A8A1FBEAEBD0092B636 /* CTPersistanceTestTableIndex.m in Sources */, 4A17A96C1F32D0C0009356D7 /* TestMiagratorVersion_3_to_4.m in Sources */, - 78B4B74020E4F16C001836CF /* CTPersistenceTestStatementCache.m in Sources */, 4A17A9941F32F392009356D7 /* TestTableVersion1.m in Sources */, 47C1D71920A145DE00020AB5 /* Target_TestDatabase.m in Sources */, 4A17A9951F32F392009356D7 /* TestRecordVersion2.m in Sources */, diff --git a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceQueryCommand.m b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceQueryCommand.m index 5297d74..214c332 100644 --- a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceQueryCommand.m +++ b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceQueryCommand.m @@ -17,8 +17,6 @@ @interface CTPersistanceQueryCommand () @property (nonatomic, strong) NSString *databaseName; @property (nonatomic, assign) BOOL shouldKeepDatabase; -@property (nonatomic, strong) NSMutableDictionary *cachedStatements; - @end @implementation CTPersistanceQueryCommand @@ -28,7 +26,6 @@ - (instancetype)initWithDatabase:(CTPersistanceDataBase *)database { self = [super init]; if (self) { - self.cachedStatements = [NSMutableDictionary dictionary]; self.shouldKeepDatabase = YES; self.database = database; } @@ -39,7 +36,6 @@ - (instancetype)initWithDatabaseName:(NSString *)databaseName { self = [super init]; if (self) { - self.cachedStatements = [NSMutableDictionary dictionary]; self.shouldKeepDatabase = NO; self.databaseName = databaseName; } @@ -48,49 +44,7 @@ - (instancetype)initWithDatabaseName:(NSString *)databaseName - (CTPersistanceSqlStatement *)compileSqlString:(NSString *)sqlString bindValueList:(NSMutableArray *)bindValueList error:(NSError *__autoreleasing *)error { - CTPersistanceSqlStatement *statement = nil; - - statement = [self cachedStatementForSqlString:sqlString]; - - if (statement == nil) { - - statement = [[CTPersistanceSqlStatement alloc] initWithSqlString:sqlString bindValueList:bindValueList database:self.database error:error]; - [self setCachedStatement:statement forSqlString:sqlString]; - - } else { - - sqlite3_stmt *stmt = statement.statement; - - [bindValueList enumerateObjectsUsingBlock:^(NSInvocation * _Nonnull bindInvocation, NSUInteger idx, BOOL * _Nonnull stop) { - [bindInvocation setArgument:(void *)&stmt atIndex:2]; - [bindInvocation invoke]; - }]; - [bindValueList removeAllObjects]; - - - } - - return statement; -} - -#pragma mark - statement cache -- (CTPersistanceSqlStatement *)cachedStatementForSqlString:(NSString *)sqlString -{ - return [self.cachedStatements objectForKey:sqlString]; -} - -- (void)setCachedStatement:(CTPersistanceSqlStatement *)statement forSqlString:(NSString *)sqlString -{ - [self.cachedStatements setObject:statement forKey:sqlString]; -} - -- (void)clearCachedStatements -{ - for (CTPersistanceSqlStatement *statement in [self.cachedStatements objectEnumerator]) { - [statement close]; - } - - [self.cachedStatements removeAllObjects]; + return [[CTPersistanceSqlStatement alloc] initWithSqlString:sqlString bindValueList:bindValueList database:self.database error:error]; } #pragma mark - getters and setters diff --git a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.h b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.h new file mode 100644 index 0000000..be04d26 --- /dev/null +++ b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.h @@ -0,0 +1,19 @@ +// +// CTPersistanceStatementCacheManager.h +// CTPersistance +// +// Created by longjianjiang on 2018/7/7. +// Copyright © 2018年 casa. All rights reserved. +// + +#import +#import + +@interface CTPersistanceStatementCacheManager : NSObject + ++ (instancetype)sharedInstance; + +- (sqlite3_stmt *)getCachedStatementWithSQLString:(NSString *)sqlString; +- (void)setCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString; + +@end diff --git a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m new file mode 100644 index 0000000..9f1fa37 --- /dev/null +++ b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m @@ -0,0 +1,103 @@ +// +// CTPersistanceStatementCacheManager.m +// CTPersistance +// +// Created by zl on 2018/7/7. +// Copyright © 2018年 casa. All rights reserved. +// + +#import "CTPersistanceStatementCacheManager.h" + +@interface CTPersistanceStatementCacheManager() + +@property (nonatomic, strong) NSMutableDictionary *cachedStatements; + +@property (nonatomic, strong) NSMutableDictionary *useCount; + +@end + + +@implementation CTPersistanceStatementCacheManager + +#pragma mark - public methods ++ (instancetype)sharedInstance +{ + static CTPersistanceStatementCacheManager *sharedInstance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedInstance = [[CTPersistanceStatementCacheManager alloc] init]; + }); + return sharedInstance; +} + +- (sqlite3_stmt *)getCachedStatementWithSQLString:(NSString *)sqlString +{ + if (![self.cachedStatements objectForKey:sqlString]) { + return NULL; + } + + @synchronized(self) { + sqlite3_stmt *pStmt = [[self.cachedStatements objectForKey:sqlString] pointerValue]; + + NSInteger count = [[self.useCount objectForKey:sqlString] integerValue] + 1; + [self.useCount setObject:@(count) forKey:sqlString]; +#ifdef DEBUG + NSLog(@" sqlstring %@ , use count is %@", sqlString, [self.useCount objectForKey:sqlString]); +#endif + if (count >= 55) { + return NULL; + } + return pStmt; + } + +} + +- (void)setCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString +{ + if ([self.cachedStatements objectForKey:sqlString]) { + return; + } + + NSArray *notCachedSQL = @[@"PRAGMA"]; + BOOL shouldCache = YES; + for (NSString *sql in notCachedSQL) { + if ([[sqlString uppercaseString] rangeOfString:sql].length) { + shouldCache = NO; + break; + } + } + + if (1) { + @synchronized(self) { + [self.cachedStatements setObject:[NSValue valueWithPointer:pStmt] forKey:sqlString]; + [self.useCount setObject:@(0) forKey:sqlString]; + } + } + +} + +#pragma mark - life cycle +- (instancetype)init +{ + self = [super init]; + if (self) { + self.cachedStatements = [NSMutableDictionary dictionary]; + self.useCount = [NSMutableDictionary dictionary]; + } + return self; +} + +- (void)dealloc +{ + @synchronized(self) { + for (id statement in [self.cachedStatements objectEnumerator]) { + sqlite3_finalize((sqlite3_stmt *)[statement pointerValue]); + } + + [self.cachedStatements removeAllObjects]; + [self.useCount removeAllObjects]; + } + +} + +@end diff --git a/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.h b/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.h index 5957415..7dba16b 100644 --- a/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.h +++ b/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.h @@ -31,16 +31,4 @@ */ - (NSArray *)fetchWithError:(NSError **)error; -/** - close statement - */ -- (void)close; - -/** - reset statement - */ -- (void)reset; - -@property (nonatomic, assign, readonly) long useCount; -@property (nonatomic, unsafe_unretained, readonly) sqlite3_stmt *statement; @end diff --git a/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m b/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m index 9e2e037..45a5d7b 100644 --- a/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m +++ b/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m @@ -10,13 +10,17 @@ #import #import "CTPersistanceConfiguration.h" +#import "CTPersistanceStatementCacheManager.h" + @interface CTPersistanceSqlStatement () -@property (nonatomic, assign, readwrite) long useCount; @property (nonatomic, unsafe_unretained, readwrite) sqlite3_stmt *statement; @property (nonatomic, weak) CTPersistanceDataBase *database; +@property (nonatomic, copy) NSString *sqlString; +@property (nonatomic, strong) CTPersistanceStatementCacheManager *statementCacheManager; + @end @implementation CTPersistanceSqlStatement @@ -26,28 +30,40 @@ - (instancetype)initWithSqlString:(NSString *)sqlString bindValueList:(NSMutable self = [super init]; if (self) { self.database = database; -#warning todo statement cache - sqlite3_stmt *statement = nil; - int result = sqlite3_prepare_v2(database.database, [sqlString UTF8String], (int)sqlString.length, &statement, NULL); - - if (result != SQLITE_OK) { - self.statement = nil; - NSString *errorMessage = [NSString stringWithUTF8String:sqlite3_errmsg(database.database)]; - NSError *generatedError = [NSError errorWithDomain:kCTPersistanceErrorDomain code:CTPersistanceErrorCodeQueryStringError userInfo:@{NSLocalizedDescriptionKey:[NSString stringWithFormat:@"\n======================\nQuery Error: \n Origin Query is : %@\n Error Message is: %@\n======================\n", sqlString, errorMessage]}]; - if (error != NULL) { - *error = generatedError; + self.sqlString = sqlString; + sqlite3_stmt *statement = [self.statementCacheManager getCachedStatementWithSQLString:sqlString]; + + if (!statement) { + + int result = sqlite3_prepare_v2(database.database, [sqlString UTF8String], (int)sqlString.length, &statement, NULL); + + if (result != SQLITE_OK) { + self.statement = nil; + NSString *errorMessage = [NSString stringWithUTF8String:sqlite3_errmsg(database.database)]; + NSError *generatedError = [NSError errorWithDomain:kCTPersistanceErrorDomain code:CTPersistanceErrorCodeQueryStringError userInfo:@{NSLocalizedDescriptionKey:[NSString stringWithFormat:@"\n======================\nQuery Error: \n Origin Query is : %@\n Error Message is: %@\n======================\n", sqlString, errorMessage]}]; + if (error != NULL) { + *error = generatedError; + } + NSLog(@"\n\n\n======================\n\n%@\n\n%s\n%s(%d):\n\n\terror is:\n\t\t %@ \n\n\t sqlString is %@\n\n======================\n\n\n", [NSThread currentThread], __FILE__, __FUNCTION__, __LINE__, errorMessage, sqlString); + sqlite3_finalize(statement); + return nil; } - NSLog(@"\n\n\n======================\n\n%@\n\n%s\n%s(%d):\n\n\terror is:\n\t\t %@ \n\n\t sqlString is %@\n\n======================\n\n\n", [NSThread currentThread], __FILE__, __FUNCTION__, __LINE__, errorMessage, sqlString); - sqlite3_finalize(statement); - return nil; + + self.statement = statement; + + } else { + + self.statement = statement; + } - self.statement = statement; + [bindValueList enumerateObjectsUsingBlock:^(NSInvocation * _Nonnull bindInvocation, NSUInteger idx, BOOL * _Nonnull stop) { [bindInvocation setArgument:(void *)&statement atIndex:2]; [bindInvocation invoke]; }]; [bindValueList removeAllObjects]; + } return self; } @@ -58,7 +74,6 @@ - (void)close { } - (void)reset { - self.useCount += 1; sqlite3_reset(self.statement); } @@ -80,9 +95,15 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error sqlite3_finalize(statement); return NO; } + + [self.statementCacheManager setCachedStatement:self.statement forSQLString:self.sqlString]; - [self reset]; - + if (![self.statementCacheManager getCachedStatementWithSQLString:self.sqlString]) { + [self close]; + } else { + [self reset]; + } + return YES; } @@ -153,10 +174,25 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error } [resultsArray addObject:result]; } - - [self reset]; + + [self.statementCacheManager setCachedStatement:self.statement forSQLString:self.sqlString]; + + if (![self.statementCacheManager getCachedStatementWithSQLString:self.sqlString]) { + [self close]; + } else { + [self reset]; + } return resultsArray; } +#pragma mark - getter and setter +- (CTPersistanceStatementCacheManager *)statementCacheManager { + if (_statementCacheManager == nil) { + _statementCacheManager = [CTPersistanceStatementCacheManager sharedInstance]; + } + return _statementCacheManager; +} + + @end diff --git a/CTPersistanceTests/CTPersistenceTestStatementCache.m b/CTPersistanceTests/CTPersistenceTestStatementCache.m deleted file mode 100644 index 349f40e..0000000 --- a/CTPersistanceTests/CTPersistenceTestStatementCache.m +++ /dev/null @@ -1,51 +0,0 @@ -// -// CTPersistenceTestStatementCache.m -// CTPersistanceTests -// -// Created by zl on 2018/6/28. -// Copyright © 2018年 casa. All rights reserved. -// - -#import -#import "CTPersistance.h" - -@interface CTPersistenceTestStatementCache : XCTestCase - -@end - -@implementation CTPersistenceTestStatementCache - -- (void)setUp { - // Put setup code here. This method is called before the invocation of each test method in the class. -} - -- (void)tearDown { - - CTPersistanceQueryCommand *queryCommand = [[CTPersistanceQueryCommand alloc] initWithDatabaseName:@"testStatementCache.sqlite"]; - NSString *tableName = @"test_statement_cache_t"; - - NSString *deleteSqlString = [NSString stringWithFormat:@"delete from `%@`;",tableName]; - [[queryCommand compileSqlString:deleteSqlString bindValueList:nil error:NULL] executeWithError:NULL]; -} - -- (void)testStatementCache { - CTPersistanceQueryCommand *queryCommand = [[CTPersistanceQueryCommand alloc] initWithDatabaseName:@"testStatementCache.sqlite"]; - NSDictionary *columnInfo = @{@"book_id": @"INTEGER"}; - NSString *tableName = @"test_statement_cache_t"; - - [[queryCommand createTable:tableName columnInfo:columnInfo error:NULL] executeWithError:NULL]; - - NSDictionary *item0 = @{@"book_id" : @0}; - NSDictionary *item1 = @{@"book_id" : @1}; - NSDictionary *item2 = @{@"book_id" : @2}; - - CTPersistanceSqlStatement *statement = [queryCommand insertTable:tableName columnInfo:columnInfo dataList:@[item0] error:NULL]; - [statement executeWithError:NULL]; - - [[queryCommand insertTable:tableName columnInfo:columnInfo dataList:@[item1] error:NULL] executeWithError:NULL]; - [[queryCommand insertTable:tableName columnInfo:columnInfo dataList:@[item2] error:NULL] executeWithError:NULL]; - - XCTAssertEqual([statement useCount], 3); -} - -@end From c5b241c2f6d584ac2e4f4211f1022af08886105f Mon Sep 17 00:00:00 2001 From: jianglongjian <2511067577@qq.com> Date: Wed, 11 Jul 2018 16:17:05 +0800 Subject: [PATCH 07/17] when sql execute fail , remove that sql corresponding statement; --- .../CTPersistanceStatementCacheManager.h | 1 + .../CTPersistanceStatementCacheManager.m | 17 +++++++--- .../Statement/CTPersistanceSqlStatement.m | 33 +++++++++++-------- .../CTPersistanceTestChangeKey.m | 2 ++ .../CTPersistanceTestMigration.m | 3 ++ 5 files changed, 39 insertions(+), 17 deletions(-) diff --git a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.h b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.h index be04d26..f755727 100644 --- a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.h +++ b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.h @@ -15,5 +15,6 @@ - (sqlite3_stmt *)getCachedStatementWithSQLString:(NSString *)sqlString; - (void)setCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString; +- (void)removeCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString; @end diff --git a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m index 9f1fa37..99d19b5 100644 --- a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m +++ b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m @@ -44,9 +44,6 @@ - (sqlite3_stmt *)getCachedStatementWithSQLString:(NSString *)sqlString #ifdef DEBUG NSLog(@" sqlstring %@ , use count is %@", sqlString, [self.useCount objectForKey:sqlString]); #endif - if (count >= 55) { - return NULL; - } return pStmt; } @@ -58,7 +55,7 @@ - (void)setCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlStr return; } - NSArray *notCachedSQL = @[@"PRAGMA"]; + NSArray *notCachedSQL = @[@"PRAGMA", @"CREATE"]; BOOL shouldCache = YES; for (NSString *sql in notCachedSQL) { if ([[sqlString uppercaseString] rangeOfString:sql].length) { @@ -76,6 +73,18 @@ - (void)setCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlStr } +- (void)removeCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString { + if (![self.cachedStatements objectForKey:sqlString]) { + return; + } + + @synchronized(self) { + [self.cachedStatements removeObjectForKey:sqlString]; + [self.useCount removeObjectForKey:sqlString]; + } +} + + #pragma mark - life cycle - (instancetype)init { diff --git a/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m b/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m index 45a5d7b..cb77efe 100644 --- a/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m +++ b/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m @@ -45,6 +45,7 @@ - (instancetype)initWithSqlString:(NSString *)sqlString bindValueList:(NSMutable *error = generatedError; } NSLog(@"\n\n\n======================\n\n%@\n\n%s\n%s(%d):\n\n\terror is:\n\t\t %@ \n\n\t sqlString is %@\n\n======================\n\n\n", [NSThread currentThread], __FILE__, __FUNCTION__, __LINE__, errorMessage, sqlString); + sqlite3_finalize(statement); return nil; } @@ -69,6 +70,8 @@ - (instancetype)initWithSqlString:(NSString *)sqlString bindValueList:(NSMutable } - (void)close { + [self.statementCacheManager removeCachedStatement:self.statement forSQLString:self.sqlString]; + sqlite3_finalize(self.statement); self.statement = nil; } @@ -89,20 +92,19 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error int result = sqlite3_step(statement); if (result != SQLITE_DONE && error) { + + [self.statementCacheManager removeCachedStatement:self.statement forSQLString:self.sqlString]; + const char *errorMsg = sqlite3_errmsg(self.database.database); NSError *generatedError = [NSError errorWithDomain:kCTPersistanceErrorDomain code:CTPersistanceErrorCodeQueryStringError userInfo:@{NSLocalizedDescriptionKey:[NSString stringWithFormat:@"\n======================\nQuery Error: \n Origin Query is : %@\n Error Message is: %@\n======================\n", [NSString stringWithUTF8String:sqlite3_sql(statement)], [NSString stringWithCString:errorMsg encoding:NSUTF8StringEncoding]]}]; *error = generatedError; sqlite3_finalize(statement); + return NO; } [self.statementCacheManager setCachedStatement:self.statement forSQLString:self.sqlString]; - - if (![self.statementCacheManager getCachedStatementWithSQLString:self.sqlString]) { - [self close]; - } else { - [self reset]; - } + [self reset]; return YES; } @@ -114,10 +116,16 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error return nil; } - NSMutableArray *resultsArray = [[NSMutableArray alloc] init]; + NSMutableArray *resultsArray = nil; sqlite3_stmt *statement = self.statement; + while (sqlite3_step(statement) == SQLITE_ROW) { + + if (!resultsArray) { + resultsArray = [NSMutableArray new]; + } + int columns = sqlite3_column_count(statement); NSMutableDictionary *result = [[NSMutableDictionary alloc] initWithCapacity:columns]; @@ -175,14 +183,13 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error [resultsArray addObject:result]; } - [self.statementCacheManager setCachedStatement:self.statement forSQLString:self.sqlString]; - - if (![self.statementCacheManager getCachedStatementWithSQLString:self.sqlString]) { - [self close]; - } else { + if (resultsArray) { + [self.statementCacheManager setCachedStatement:self.statement forSQLString:self.sqlString]; [self reset]; + } else { + [self close]; } - + return resultsArray; } diff --git a/CTPersistanceTests/CTPersistanceTestChangeKey.m b/CTPersistanceTests/CTPersistanceTestChangeKey.m index 7dbd70c..b85f5b4 100644 --- a/CTPersistanceTests/CTPersistanceTestChangeKey.m +++ b/CTPersistanceTests/CTPersistanceTestChangeKey.m @@ -22,6 +22,7 @@ @interface CTPersistanceTestChangeKey : XCTestCase @implementation CTPersistanceTestChangeKey + - (void)setUp { [super setUp]; // Put setup code here. This method is called before the invocation of each test method in the class. @@ -144,4 +145,5 @@ - (NSString *)databaseFilePath return _databaseFilePath; } + @end diff --git a/CTPersistanceTests/CTPersistanceTestMigration.m b/CTPersistanceTests/CTPersistanceTestMigration.m index fc64aa5..fedfe88 100644 --- a/CTPersistanceTests/CTPersistanceTestMigration.m +++ b/CTPersistanceTests/CTPersistanceTestMigration.m @@ -24,6 +24,7 @@ @interface CTPersistanceTestMigration : XCTestCase @implementation CTPersistanceTestMigration + - (void)setUp { [super setUp]; @@ -261,4 +262,6 @@ - (void)testMiagration_brandNew_v4 XCTAssertEqual(columnInfo.count, 5); } + + @end From 1574cea3707d0d3985897eb75c35c20b89332c69 Mon Sep 17 00:00:00 2001 From: jianglongjian <2511067577@qq.com> Date: Wed, 11 Jul 2018 18:24:39 +0800 Subject: [PATCH 08/17] statement cache based on different database; --- CTPersistance.xcodeproj/project.pbxproj | 12 +- .../Database/CTPersistanceDataBase.m | 4 + .../CTPersistanceStatementCacheManager.h | 10 +- .../CTPersistanceStatementCacheManager.m | 120 ++++++++++-------- .../Statement/CTPersistanceSqlStatement.m | 28 ++-- 5 files changed, 94 insertions(+), 80 deletions(-) diff --git a/CTPersistance.xcodeproj/project.pbxproj b/CTPersistance.xcodeproj/project.pbxproj index 9d5dce3..2003cc5 100644 --- a/CTPersistance.xcodeproj/project.pbxproj +++ b/CTPersistance.xcodeproj/project.pbxproj @@ -1102,12 +1102,12 @@ TargetAttributes = { 4A0E00241F2C384E00E6A4DA = { CreatedOnToolsVersion = 8.3.2; - DevelopmentTeam = THS9SRQVES; + DevelopmentTeam = 74MF5V7Z8J; ProvisioningStyle = Automatic; }; 4A0E003D1F2C384E00E6A4DA = { CreatedOnToolsVersion = 8.3.2; - DevelopmentTeam = THS9SRQVES; + DevelopmentTeam = 74MF5V7Z8J; ProvisioningStyle = Automatic; TestTargetID = 4A0E00241F2C384E00E6A4DA; }; @@ -1415,7 +1415,7 @@ baseConfigurationReference = 89D41C6489863CB58D4966E5 /* Pods-CTPersistance.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = THS9SRQVES; + DEVELOPMENT_TEAM = 74MF5V7Z8J; INFOPLIST_FILE = CTPersistance/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; @@ -1429,7 +1429,7 @@ baseConfigurationReference = 0E3659C0BE44F61448420F54 /* Pods-CTPersistance.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = THS9SRQVES; + DEVELOPMENT_TEAM = 74MF5V7Z8J; INFOPLIST_FILE = CTPersistance/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; @@ -1443,7 +1443,7 @@ baseConfigurationReference = AD102503C09A04454242FAC0 /* Pods-CTPersistanceTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; - DEVELOPMENT_TEAM = THS9SRQVES; + DEVELOPMENT_TEAM = 74MF5V7Z8J; INFOPLIST_FILE = CTPersistanceTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = casa.CTPersistanceTests; @@ -1457,7 +1457,7 @@ baseConfigurationReference = D9D5304E203B48D737D87E6F /* Pods-CTPersistanceTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; - DEVELOPMENT_TEAM = THS9SRQVES; + DEVELOPMENT_TEAM = 74MF5V7Z8J; INFOPLIST_FILE = CTPersistanceTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = casa.CTPersistanceTests; diff --git a/CTPersistance/CTPersistance/Database/CTPersistanceDataBase.m b/CTPersistance/CTPersistance/Database/CTPersistanceDataBase.m index f1d968b..a20ceb3 100644 --- a/CTPersistance/CTPersistance/Database/CTPersistanceDataBase.m +++ b/CTPersistance/CTPersistance/Database/CTPersistanceDataBase.m @@ -13,6 +13,8 @@ #import "CTPersistanceVersionTable.h" #import +#import "CTPersistanceStatementCacheManager.h" + extern SQLITE_API int sqlite3_key(sqlite3 *db, const void *pKey, int nKey); extern SQLITE_API int sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey); @@ -115,6 +117,8 @@ - (void)closeDatabase _database = NULL; _databaseFilePath = nil; + + [[CTPersistanceStatementCacheManager sharedInstance] clearDatabaseStatementCache:self.databaseName]; } #pragma mark - private methods diff --git a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.h b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.h index f755727..162649e 100644 --- a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.h +++ b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.h @@ -13,8 +13,12 @@ + (instancetype)sharedInstance; -- (sqlite3_stmt *)getCachedStatementWithSQLString:(NSString *)sqlString; -- (void)setCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString; -- (void)removeCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString; + +- (void)setCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName; +- (sqlite3_stmt *)getCachedStatementWithSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName; +- (void)removeCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName; + +- (void)clearDatabaseStatementCache:(NSString *)databaseName; +- (void)clearAllDatabaseStatementCache; @end diff --git a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m index 99d19b5..6eb8865 100644 --- a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m +++ b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m @@ -2,7 +2,7 @@ // CTPersistanceStatementCacheManager.m // CTPersistance // -// Created by zl on 2018/7/7. +// Created by longjianjiang on 2018/7/7. // Copyright © 2018年 casa. All rights reserved. // @@ -10,103 +10,115 @@ @interface CTPersistanceStatementCacheManager() -@property (nonatomic, strong) NSMutableDictionary *cachedStatements; - -@property (nonatomic, strong) NSMutableDictionary *useCount; +@property (nonatomic, strong) NSMutableDictionary *cachedTree; @end @implementation CTPersistanceStatementCacheManager -#pragma mark - public methods -+ (instancetype)sharedInstance +- (sqlite3_stmt *)getCachedStatementWithSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName { - static CTPersistanceStatementCacheManager *sharedInstance = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - sharedInstance = [[CTPersistanceStatementCacheManager alloc] init]; - }); - return sharedInstance; -} - -- (sqlite3_stmt *)getCachedStatementWithSQLString:(NSString *)sqlString -{ - if (![self.cachedStatements objectForKey:sqlString]) { + if (![self.cachedTree objectForKey:databaseName]) { + return NULL; + } + + NSMutableDictionary *cachedStatements = [self.cachedTree objectForKey:databaseName]; + + if (![cachedStatements objectForKey:sqlString]) { return NULL; } @synchronized(self) { - sqlite3_stmt *pStmt = [[self.cachedStatements objectForKey:sqlString] pointerValue]; - - NSInteger count = [[self.useCount objectForKey:sqlString] integerValue] + 1; - [self.useCount setObject:@(count) forKey:sqlString]; -#ifdef DEBUG - NSLog(@" sqlstring %@ , use count is %@", sqlString, [self.useCount objectForKey:sqlString]); -#endif + sqlite3_stmt *pStmt = [[cachedStatements objectForKey:sqlString] pointerValue]; return pStmt; } +} + +- (void)setCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName +{ + if (![self.cachedTree objectForKey:databaseName]) { + [self.cachedTree setObject:[NSMutableDictionary dictionary] forKey:databaseName]; + } + NSMutableDictionary *cachedStatements = [self.cachedTree objectForKey:databaseName]; + + if ([cachedStatements objectForKey:sqlString]) { + return; + } + + @synchronized(self) { + [cachedStatements setObject:[NSValue valueWithPointer:pStmt] forKey:sqlString]; + } } -- (void)setCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString +- (void)removeCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName { - if ([self.cachedStatements objectForKey:sqlString]) { + if (![self.cachedTree objectForKey:databaseName]) { return; } - NSArray *notCachedSQL = @[@"PRAGMA", @"CREATE"]; - BOOL shouldCache = YES; - for (NSString *sql in notCachedSQL) { - if ([[sqlString uppercaseString] rangeOfString:sql].length) { - shouldCache = NO; - break; - } + NSMutableDictionary *cachedStatements = [self.cachedTree objectForKey:databaseName]; + + if (![cachedStatements objectForKey:sqlString]) { + return; } - if (1) { - @synchronized(self) { - [self.cachedStatements setObject:[NSValue valueWithPointer:pStmt] forKey:sqlString]; - [self.useCount setObject:@(0) forKey:sqlString]; - } + @synchronized(self) { + [cachedStatements removeObjectForKey:sqlString]; } - } -- (void)removeCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString { - if (![self.cachedStatements objectForKey:sqlString]) { +- (void)clearDatabaseStatementCache:(NSString *)databaseName +{ + if (![self.cachedTree objectForKey:databaseName]) { return; } + NSMutableDictionary *cachedStatements = [self.cachedTree objectForKey:databaseName]; + @synchronized(self) { - [self.cachedStatements removeObjectForKey:sqlString]; - [self.useCount removeObjectForKey:sqlString]; + for (id statement in [cachedStatements objectEnumerator]) { + sqlite3_finalize((sqlite3_stmt *)[statement pointerValue]); + } + + [cachedStatements removeAllObjects]; + [self.cachedTree removeObjectForKey:databaseName]; + } +} + +- (void)clearAllDatabaseStatementCache +{ + for (NSString *databaseName in [self.cachedTree objectEnumerator]) { + [self clearDatabaseStatementCache:databaseName]; } } +#pragma mark - public methods ++ (instancetype)sharedInstance +{ + static CTPersistanceStatementCacheManager *sharedInstance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedInstance = [[CTPersistanceStatementCacheManager alloc] init]; + }); + return sharedInstance; +} + #pragma mark - life cycle - (instancetype)init { self = [super init]; if (self) { - self.cachedStatements = [NSMutableDictionary dictionary]; - self.useCount = [NSMutableDictionary dictionary]; + self.cachedTree = [NSMutableDictionary dictionary]; } return self; } - (void)dealloc { - @synchronized(self) { - for (id statement in [self.cachedStatements objectEnumerator]) { - sqlite3_finalize((sqlite3_stmt *)[statement pointerValue]); - } - - [self.cachedStatements removeAllObjects]; - [self.useCount removeAllObjects]; - } - + [self clearAllDatabaseStatementCache]; } @end diff --git a/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m b/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m index cb77efe..d786567 100644 --- a/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m +++ b/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m @@ -31,14 +31,14 @@ - (instancetype)initWithSqlString:(NSString *)sqlString bindValueList:(NSMutable if (self) { self.database = database; self.sqlString = sqlString; - sqlite3_stmt *statement = [self.statementCacheManager getCachedStatementWithSQLString:sqlString]; + sqlite3_stmt *statement = [self.statementCacheManager getCachedStatementWithSQLString:sqlString atDatabase:self.database.databaseName]; if (!statement) { int result = sqlite3_prepare_v2(database.database, [sqlString UTF8String], (int)sqlString.length, &statement, NULL); if (result != SQLITE_OK) { - self.statement = nil; + NSString *errorMessage = [NSString stringWithUTF8String:sqlite3_errmsg(database.database)]; NSError *generatedError = [NSError errorWithDomain:kCTPersistanceErrorDomain code:CTPersistanceErrorCodeQueryStringError userInfo:@{NSLocalizedDescriptionKey:[NSString stringWithFormat:@"\n======================\nQuery Error: \n Origin Query is : %@\n Error Message is: %@\n======================\n", sqlString, errorMessage]}]; if (error != NULL) { @@ -46,7 +46,8 @@ - (instancetype)initWithSqlString:(NSString *)sqlString bindValueList:(NSMutable } NSLog(@"\n\n\n======================\n\n%@\n\n%s\n%s(%d):\n\n\terror is:\n\t\t %@ \n\n\t sqlString is %@\n\n======================\n\n\n", [NSThread currentThread], __FILE__, __FUNCTION__, __LINE__, errorMessage, sqlString); - sqlite3_finalize(statement); + [self close]; + return nil; } @@ -70,7 +71,7 @@ - (instancetype)initWithSqlString:(NSString *)sqlString bindValueList:(NSMutable } - (void)close { - [self.statementCacheManager removeCachedStatement:self.statement forSQLString:self.sqlString]; + [self.statementCacheManager removeCachedStatement:self.statement forSQLString:self.sqlString atDatabase:self.database.databaseName]; sqlite3_finalize(self.statement); self.statement = nil; @@ -93,7 +94,7 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error if (result != SQLITE_DONE && error) { - [self.statementCacheManager removeCachedStatement:self.statement forSQLString:self.sqlString]; + [self.statementCacheManager removeCachedStatement:self.statement forSQLString:self.sqlString atDatabase:self.database.databaseName]; const char *errorMsg = sqlite3_errmsg(self.database.database); NSError *generatedError = [NSError errorWithDomain:kCTPersistanceErrorDomain code:CTPersistanceErrorCodeQueryStringError userInfo:@{NSLocalizedDescriptionKey:[NSString stringWithFormat:@"\n======================\nQuery Error: \n Origin Query is : %@\n Error Message is: %@\n======================\n", [NSString stringWithUTF8String:sqlite3_sql(statement)], [NSString stringWithCString:errorMsg encoding:NSUTF8StringEncoding]]}]; @@ -103,12 +104,13 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error return NO; } - [self.statementCacheManager setCachedStatement:self.statement forSQLString:self.sqlString]; + [self.statementCacheManager setCachedStatement:self.statement forSQLString:self.sqlString atDatabase:self.database.databaseName]; [self reset]; return YES; } + - (NSArray *)fetchWithError:(NSError *__autoreleasing *)error { if (error != NULL && *error != nil) { @@ -116,16 +118,12 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error return nil; } - NSMutableArray *resultsArray = nil; + NSMutableArray *resultsArray = [NSMutableArray array]; sqlite3_stmt *statement = self.statement; while (sqlite3_step(statement) == SQLITE_ROW) { - if (!resultsArray) { - resultsArray = [NSMutableArray new]; - } - int columns = sqlite3_column_count(statement); NSMutableDictionary *result = [[NSMutableDictionary alloc] initWithCapacity:columns]; @@ -183,12 +181,8 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error [resultsArray addObject:result]; } - if (resultsArray) { - [self.statementCacheManager setCachedStatement:self.statement forSQLString:self.sqlString]; - [self reset]; - } else { - [self close]; - } + [self.statementCacheManager setCachedStatement:self.statement forSQLString:self.sqlString atDatabase:self.database.databaseName]; + [self reset]; return resultsArray; } From d556fff7d3affb0fca08b5bc8d48e0abde951a1d Mon Sep 17 00:00:00 2001 From: zhouzhongguang Date: Thu, 12 Jul 2018 16:34:47 +0800 Subject: [PATCH 09/17] =?UTF-8?q?sql=E8=AF=AD=E5=8F=A5=E4=B8=AD=E6=9C=89?= =?UTF-8?q?=E6=B1=89=E5=AD=97=E6=97=B6result=3DSQLITE=5FERROR?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 当sqlString中有汉字时,sqlString.length != [sqlString UTF8String]的字节长度(汉字UTF8编码中占两个字节)。 所以这里应该是(int)[sqlString lengthOfBytesUsingEncoding:NSUTF8StringEncoding],或像前一个版本一样写成-1 --- .../xcshareddata/IDEWorkspaceChecks.plist | 8 ++++++++ .../QueryCommand/Statement/CTPersistanceSqlStatement.m | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 CTPersistance.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/CTPersistance.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/CTPersistance.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/CTPersistance.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m b/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m index 12b5c48..4055e8d 100644 --- a/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m +++ b/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m @@ -26,7 +26,7 @@ - (instancetype)initWithSqlString:(NSString *)sqlString bindValueList:(NSMutable self.database = database; #warning todo statement cache sqlite3_stmt *statement = nil; - int result = sqlite3_prepare_v2(database.database, [sqlString UTF8String], (int)sqlString.length, &statement, NULL); + int result = sqlite3_prepare_v2(database.database, [sqlString UTF8String], (int)[sqlString lengthOfBytesUsingEncoding:NSUTF8StringEncoding], &statement, NULL); if (result != SQLITE_OK) { self.statement = nil; From c3453d3d0164f4dfc3441e270fe89facdcfce53a Mon Sep 17 00:00:00 2001 From: Casa Taloyum Date: Thu, 12 Jul 2018 16:43:00 +0800 Subject: [PATCH 10/17] 185 --- CTPersistance.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CTPersistance.podspec b/CTPersistance.podspec index 6f11a6a..78b31f6 100644 --- a/CTPersistance.podspec +++ b/CTPersistance.podspec @@ -16,7 +16,7 @@ Pod::Spec.new do |s| # s.name = "CTPersistance" - s.version = "184" + s.version = "185" s.summary = "Objective-C Model Layer with SQLite." s.description = <<-DESC From 9ac25f2b51761655492c962b46f0d86856217927 Mon Sep 17 00:00:00 2001 From: jianglongjian <2511067577@qq.com> Date: Sun, 15 Jul 2018 11:08:04 +0800 Subject: [PATCH 11/17] use set to save multi statement; --- CTPersistance.xcodeproj/project.pbxproj | 14 +++ .../CTPersistanceStatementCacheManager.h | 10 +- .../CTPersistanceStatementCacheManager.m | 115 +++++++++--------- .../Statement/CTPersistanceSqlStatement.m | 59 +++++---- .../CTPersistanceStatementCacheItem.h | 21 ++++ .../CTPersistanceStatementCacheItem.m | 53 ++++++++ .../AsyncTestViewController.m | 14 +-- 7 files changed, 193 insertions(+), 93 deletions(-) create mode 100644 CTPersistance/CTPersistance/QueryCommand/StatementCache/CTPersistanceStatementCacheItem.h create mode 100644 CTPersistance/CTPersistance/QueryCommand/StatementCache/CTPersistanceStatementCacheItem.m diff --git a/CTPersistance.xcodeproj/project.pbxproj b/CTPersistance.xcodeproj/project.pbxproj index 2003cc5..26d5acb 100644 --- a/CTPersistance.xcodeproj/project.pbxproj +++ b/CTPersistance.xcodeproj/project.pbxproj @@ -82,6 +82,7 @@ 4AF38F2F1F69068700801322 /* CTPersistanceTestTransaction.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AF38F2E1F69068700801322 /* CTPersistanceTestTransaction.m */; }; 6284DE2109EF43B7A3D56F18 /* libPods-CTPersistance.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 24975DE09A62B809BFED181C /* libPods-CTPersistance.a */; }; 7866EEC620F0ABB300819E75 /* CTPersistanceStatementCacheManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 7866EEC520F0ABB300819E75 /* CTPersistanceStatementCacheManager.m */; }; + 786AD97020F88812005B33FB /* CTPersistanceStatementCacheItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 786AD96F20F88812005B33FB /* CTPersistanceStatementCacheItem.m */; }; DAEEF1BE411F617114764D0F /* libPods-CTPersistanceTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D31FADFF56BDD50C7FD9C94 /* libPods-CTPersistanceTests.a */; }; /* End PBXBuildFile section */ @@ -236,6 +237,8 @@ 4D31FADFF56BDD50C7FD9C94 /* libPods-CTPersistanceTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-CTPersistanceTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 7866EEC420F0ABB300819E75 /* CTPersistanceStatementCacheManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CTPersistanceStatementCacheManager.h; sourceTree = ""; }; 7866EEC520F0ABB300819E75 /* CTPersistanceStatementCacheManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTPersistanceStatementCacheManager.m; sourceTree = ""; }; + 786AD96E20F88812005B33FB /* CTPersistanceStatementCacheItem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CTPersistanceStatementCacheItem.h; sourceTree = ""; }; + 786AD96F20F88812005B33FB /* CTPersistanceStatementCacheItem.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTPersistanceStatementCacheItem.m; sourceTree = ""; }; 89D41C6489863CB58D4966E5 /* Pods-CTPersistance.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CTPersistance.debug.xcconfig"; path = "Pods/Target Support Files/Pods-CTPersistance/Pods-CTPersistance.debug.xcconfig"; sourceTree = ""; }; AD102503C09A04454242FAC0 /* Pods-CTPersistanceTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CTPersistanceTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-CTPersistanceTests/Pods-CTPersistanceTests.debug.xcconfig"; sourceTree = ""; }; D9D5304E203B48D737D87E6F /* Pods-CTPersistanceTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CTPersistanceTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-CTPersistanceTests/Pods-CTPersistanceTests.release.xcconfig"; sourceTree = ""; }; @@ -476,6 +479,7 @@ children = ( 7866EEC420F0ABB300819E75 /* CTPersistanceStatementCacheManager.h */, 7866EEC520F0ABB300819E75 /* CTPersistanceStatementCacheManager.m */, + 786AD96D20F88755005B33FB /* StatementCache */, 4A5E3BEE1F403A3200946749 /* Statement */, 4A0E00751F2CBD6300E6A4DA /* Categories */, 4A0E00821F2CBD6300E6A4DA /* CTPersistanceQueryCommand.h */, @@ -1051,6 +1055,15 @@ path = Upsert; sourceTree = ""; }; + 786AD96D20F88755005B33FB /* StatementCache */ = { + isa = PBXGroup; + children = ( + 786AD96E20F88812005B33FB /* CTPersistanceStatementCacheItem.h */, + 786AD96F20F88812005B33FB /* CTPersistanceStatementCacheItem.m */, + ); + path = StatementCache; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -1217,6 +1230,7 @@ 4A0E002D1F2C384E00E6A4DA /* AppDelegate.m in Sources */, 4A0E00A41F2CBD6300E6A4DA /* CTPersistanceVersionRecord.m in Sources */, 4A0238741F3FFBA800EE91C7 /* TestTable.m in Sources */, + 786AD97020F88812005B33FB /* CTPersistanceStatementCacheItem.m in Sources */, 4AC74A631FBE7DC60092B636 /* ViewRecordAndMergeDemoViewController.m in Sources */, 4AC74A731FBE7EC90092B636 /* ItemDetailTable.m in Sources */, 7866EEC620F0ABB300819E75 /* CTPersistanceStatementCacheManager.m in Sources */, diff --git a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.h b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.h index 162649e..06e255d 100644 --- a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.h +++ b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.h @@ -7,18 +7,16 @@ // #import -#import +#import "CTPersistanceStatementCacheItem.h" @interface CTPersistanceStatementCacheManager : NSObject + (instancetype)sharedInstance; - -- (void)setCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName; -- (sqlite3_stmt *)getCachedStatementWithSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName; -- (void)removeCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName; +- (void)setCachedStatement:(CTPersistanceStatementCacheItem *)pStmt forSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName; +- (CTPersistanceStatementCacheItem *)getCachedStatementWithSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName; +- (void)removeCachedStatement:(CTPersistanceStatementCacheItem *)pStmt forSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName; - (void)clearDatabaseStatementCache:(NSString *)databaseName; -- (void)clearAllDatabaseStatementCache; @end diff --git a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m index 6eb8865..19f6c38 100644 --- a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m +++ b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m @@ -8,6 +8,7 @@ #import "CTPersistanceStatementCacheManager.h" + @interface CTPersistanceStatementCacheManager() @property (nonatomic, strong) NSMutableDictionary *cachedTree; @@ -17,55 +18,70 @@ @interface CTPersistanceStatementCacheManager() @implementation CTPersistanceStatementCacheManager -- (sqlite3_stmt *)getCachedStatementWithSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName +- (CTPersistanceStatementCacheItem *)getCachedStatementWithSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName { - if (![self.cachedTree objectForKey:databaseName]) { - return NULL; - } - - NSMutableDictionary *cachedStatements = [self.cachedTree objectForKey:databaseName]; - - if (![cachedStatements objectForKey:sqlString]) { - return NULL; - } + @synchronized (self) { + if (![self.cachedTree objectForKey:databaseName]) { + return NULL; + } + + NSMutableDictionary *cachedStatements = [self.cachedTree objectForKey:databaseName]; + + if (![cachedStatements objectForKey:sqlString]) { + return NULL; + } + + NSMutableSet *statements = [cachedStatements objectForKey:sqlString]; - @synchronized(self) { - sqlite3_stmt *pStmt = [[cachedStatements objectForKey:sqlString] pointerValue]; - return pStmt; + for (CTPersistanceStatementCacheItem *item in statements) { + if (item.inUse == NO) { + return item; + } + } + + return nil; } } -- (void)setCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName +- (void)setCachedStatement:(CTPersistanceStatementCacheItem *)pStmt forSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName { - if (![self.cachedTree objectForKey:databaseName]) { - [self.cachedTree setObject:[NSMutableDictionary dictionary] forKey:databaseName]; - } - - NSMutableDictionary *cachedStatements = [self.cachedTree objectForKey:databaseName]; - - if ([cachedStatements objectForKey:sqlString]) { - return; - } + @synchronized (self) { + if (![self.cachedTree objectForKey:databaseName]) { + [self.cachedTree setObject:[NSMutableDictionary dictionary] forKey:databaseName]; + } + + NSMutableDictionary *cachedStatements = [self.cachedTree objectForKey:databaseName]; + + NSMutableSet *statements = [cachedStatements objectForKey:sqlString]; + if (!statements) { + statements = [NSMutableSet set]; + } + + if (pStmt.inUse == NO) { + [statements addObject:pStmt]; + [cachedStatements setObject:statements forKey:sqlString]; + } - @synchronized(self) { - [cachedStatements setObject:[NSValue valueWithPointer:pStmt] forKey:sqlString]; } + } -- (void)removeCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName +- (void)removeCachedStatement:(CTPersistanceStatementCacheItem *)pStmt forSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName { - if (![self.cachedTree objectForKey:databaseName]) { - return; - } - - NSMutableDictionary *cachedStatements = [self.cachedTree objectForKey:databaseName]; - if (![cachedStatements objectForKey:sqlString]) { - return; - } - - @synchronized(self) { - [cachedStatements removeObjectForKey:sqlString]; + @synchronized (self) { + if (![self.cachedTree objectForKey:databaseName]) { + return; + } + + NSMutableDictionary *cachedStatements = [self.cachedTree objectForKey:databaseName]; + + if (![cachedStatements objectForKey:sqlString]) { + return; + } + + NSMutableSet *statements = [cachedStatements objectForKey:sqlString]; + [statements removeObject:pStmt]; } } @@ -77,24 +93,16 @@ - (void)clearDatabaseStatementCache:(NSString *)databaseName NSMutableDictionary *cachedStatements = [self.cachedTree objectForKey:databaseName]; - @synchronized(self) { - for (id statement in [cachedStatements objectEnumerator]) { - sqlite3_finalize((sqlite3_stmt *)[statement pointerValue]); + for (NSMutableSet *statements in [cachedStatements objectEnumerator]) { + for (CTPersistanceStatementCacheItem *item in statements) { + [item close]; } - - [cachedStatements removeAllObjects]; - [self.cachedTree removeObjectForKey:databaseName]; - } -} - -- (void)clearAllDatabaseStatementCache -{ - for (NSString *databaseName in [self.cachedTree objectEnumerator]) { - [self clearDatabaseStatementCache:databaseName]; } + + [cachedStatements removeAllObjects]; + [self.cachedTree removeObjectForKey:databaseName]; } - #pragma mark - public methods + (instancetype)sharedInstance { @@ -116,9 +124,4 @@ - (instancetype)init return self; } -- (void)dealloc -{ - [self clearAllDatabaseStatementCache]; -} - @end diff --git a/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m b/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m index d786567..5d31bbb 100644 --- a/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m +++ b/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m @@ -14,12 +14,12 @@ @interface CTPersistanceSqlStatement () -@property (nonatomic, unsafe_unretained, readwrite) sqlite3_stmt *statement; - @property (nonatomic, weak) CTPersistanceDataBase *database; @property (nonatomic, copy) NSString *sqlString; + @property (nonatomic, strong) CTPersistanceStatementCacheManager *statementCacheManager; +@property (nonatomic, strong) CTPersistanceStatementCacheItem *statementItem; @end @@ -28,12 +28,19 @@ @implementation CTPersistanceSqlStatement - (instancetype)initWithSqlString:(NSString *)sqlString bindValueList:(NSMutableArray *)bindValueList database:(CTPersistanceDataBase *)database error:(NSError *__autoreleasing *)error { self = [super init]; + if (self) { + self.database = database; self.sqlString = sqlString; - sqlite3_stmt *statement = [self.statementCacheManager getCachedStatementWithSQLString:sqlString atDatabase:self.database.databaseName]; - if (!statement) { + self.statementItem = [self.statementCacheManager getCachedStatementWithSQLString:sqlString atDatabase:self.database.databaseName]; + + sqlite3_stmt *statement = self.statementItem.statement; + + if (!self.statementItem) { + + self.statementItem = [CTPersistanceStatementCacheItem new]; int result = sqlite3_prepare_v2(database.database, [sqlString UTF8String], (int)sqlString.length, &statement, NULL); @@ -51,15 +58,10 @@ - (instancetype)initWithSqlString:(NSString *)sqlString bindValueList:(NSMutable return nil; } - self.statement = statement; - - } else { - - self.statement = statement; - + self.statementItem.statement = statement; + } - [bindValueList enumerateObjectsUsingBlock:^(NSInvocation * _Nonnull bindInvocation, NSUInteger idx, BOOL * _Nonnull stop) { [bindInvocation setArgument:(void *)&statement atIndex:2]; [bindInvocation invoke]; @@ -70,15 +72,17 @@ - (instancetype)initWithSqlString:(NSString *)sqlString bindValueList:(NSMutable return self; } -- (void)close { - [self.statementCacheManager removeCachedStatement:self.statement forSQLString:self.sqlString atDatabase:self.database.databaseName]; +- (void)close +{ + [self.statementCacheManager removeCachedStatement:self.statementItem forSQLString:self.sqlString atDatabase:self.database.databaseName]; - sqlite3_finalize(self.statement); - self.statement = nil; + [self.statementItem close]; + self.statementItem = nil; } -- (void)reset { - sqlite3_reset(self.statement); +- (void)reset +{ + [self.statementItem reset]; } - (BOOL)executeWithError:(NSError *__autoreleasing *)error @@ -88,13 +92,15 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error return NO; } - sqlite3_stmt *statement = self.statement; + self.statementItem.inUse = YES; + + sqlite3_stmt *statement = self.statementItem.statement; int result = sqlite3_step(statement); if (result != SQLITE_DONE && error) { - [self.statementCacheManager removeCachedStatement:self.statement forSQLString:self.sqlString atDatabase:self.database.databaseName]; + [self.statementCacheManager removeCachedStatement:self.statementItem forSQLString:self.sqlString atDatabase:self.database.databaseName]; const char *errorMsg = sqlite3_errmsg(self.database.database); NSError *generatedError = [NSError errorWithDomain:kCTPersistanceErrorDomain code:CTPersistanceErrorCodeQueryStringError userInfo:@{NSLocalizedDescriptionKey:[NSString stringWithFormat:@"\n======================\nQuery Error: \n Origin Query is : %@\n Error Message is: %@\n======================\n", [NSString stringWithUTF8String:sqlite3_sql(statement)], [NSString stringWithCString:errorMsg encoding:NSUTF8StringEncoding]]}]; @@ -104,9 +110,10 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error return NO; } - [self.statementCacheManager setCachedStatement:self.statement forSQLString:self.sqlString atDatabase:self.database.databaseName]; [self reset]; + [self.statementCacheManager setCachedStatement:self.statementItem forSQLString:self.sqlString atDatabase:self.database.databaseName]; + return YES; } @@ -118,9 +125,12 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error return nil; } + self.statementItem.inUse = YES; + + NSMutableArray *resultsArray = [NSMutableArray array]; - sqlite3_stmt *statement = self.statement; + sqlite3_stmt *statement = self.statementItem.statement; while (sqlite3_step(statement) == SQLITE_ROW) { @@ -181,14 +191,17 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error [resultsArray addObject:result]; } - [self.statementCacheManager setCachedStatement:self.statement forSQLString:self.sqlString atDatabase:self.database.databaseName]; [self reset]; + + [self.statementCacheManager setCachedStatement:self.statementItem forSQLString:self.sqlString atDatabase:self.database.databaseName]; + return resultsArray; } #pragma mark - getter and setter -- (CTPersistanceStatementCacheManager *)statementCacheManager { +- (CTPersistanceStatementCacheManager *)statementCacheManager +{ if (_statementCacheManager == nil) { _statementCacheManager = [CTPersistanceStatementCacheManager sharedInstance]; } diff --git a/CTPersistance/CTPersistance/QueryCommand/StatementCache/CTPersistanceStatementCacheItem.h b/CTPersistance/CTPersistance/QueryCommand/StatementCache/CTPersistanceStatementCacheItem.h new file mode 100644 index 0000000..88dfcac --- /dev/null +++ b/CTPersistance/CTPersistance/QueryCommand/StatementCache/CTPersistanceStatementCacheItem.h @@ -0,0 +1,21 @@ +// +// CTPersistanceStatementCacheItem.h +// CTPersistance +// +// Created by longjianjiang on 2018/7/13. +// Copyright © 2018年 casa. All rights reserved. +// + +#import +#import + +@interface CTPersistanceStatementCacheItem : NSObject + +@property (atomic, assign) BOOL inUse; +@property (nonatomic, unsafe_unretained) sqlite3_stmt *statement; + +- (void)reset; +- (void)close; + +@end + diff --git a/CTPersistance/CTPersistance/QueryCommand/StatementCache/CTPersistanceStatementCacheItem.m b/CTPersistance/CTPersistance/QueryCommand/StatementCache/CTPersistanceStatementCacheItem.m new file mode 100644 index 0000000..13ec268 --- /dev/null +++ b/CTPersistance/CTPersistance/QueryCommand/StatementCache/CTPersistanceStatementCacheItem.m @@ -0,0 +1,53 @@ +// +// CTPersistanceStatementCacheItem.m +// CTPersistance +// +// Created by longjianjiang on 2018/7/13. +// Copyright © 2018年 casa. All rights reserved. +// + +#import "CTPersistanceStatementCacheItem.h" +#import + +@interface CTPersistanceStatementCacheItem () { + volatile uint32_t _inUse; +} + +@end + +@implementation CTPersistanceStatementCacheItem +- (void)close +{ + if (_statement) { + sqlite3_finalize(_statement); + _statement = 0x00; + } + + self.inUse = NO; +} + +- (void)reset +{ + if (_statement) { + sqlite3_reset(_statement); + } + + self.inUse = NO; +} + +#pragma mark getter and setter +- (BOOL)inUse +{ + return _inUse != 0; +} + +- (void)setInUse:(BOOL)allowed +{ + if (allowed) { + OSAtomicOr32Barrier(1, &_inUse); + } else { + OSAtomicAnd32Barrier(0, &_inUse); + } +} + +@end diff --git a/CTPersistance/Demo/AsyncTestViewController/AsyncTestViewController.m b/CTPersistance/Demo/AsyncTestViewController/AsyncTestViewController.m index 07b574f..74bdde2 100644 --- a/CTPersistance/Demo/AsyncTestViewController/AsyncTestViewController.m +++ b/CTPersistance/Demo/AsyncTestViewController/AsyncTestViewController.m @@ -34,8 +34,6 @@ - (void)viewDidLoad record.name = @"casa"; [self.testTable insertRecord:record error:NULL]; } - - NSLog(@"%@", self.testTable.queryCommand.database.databaseFilePath); } - (void)viewWillAppear:(BOOL)animated @@ -46,7 +44,7 @@ - (void)viewWillAppear:(BOOL)animated NSInteger count = COUNT; while (count --> 0) { TestRecord *record = (TestRecord *)[self.testTable findWithPrimaryKey:@(count) error:NULL]; - NSLog(@"%@", record.primaryKey); + NSLog(@"read one %@", record.primaryKey); } }]; @@ -54,7 +52,7 @@ - (void)viewWillAppear:(BOOL)animated while (count --> 0) { [[CTPersistanceAsyncExecutor sharedInstance] read:^{ TestRecord *record = (TestRecord *)[self.testTable findWithPrimaryKey:@(count) error:NULL]; - NSLog(@"%@", record.primaryKey); + NSLog(@"read two %@", record.primaryKey); }]; } @@ -62,7 +60,7 @@ - (void)viewWillAppear:(BOOL)animated NSInteger count = COUNT; while (count --> 0) { NSNumber *primaryKey = [self.testTable insertValue:@"casa" forKey:@"name" error:NULL]; - NSLog(@"%@", primaryKey); + NSLog(@"write one %@", primaryKey); } }]; @@ -70,7 +68,7 @@ - (void)viewWillAppear:(BOOL)animated while (count --> 0) { [[CTPersistanceAsyncExecutor sharedInstance] read:^{ TestRecord *record = (TestRecord *)[self.testTable findWithPrimaryKey:@(count) error:NULL]; - NSLog(@"%@", record.primaryKey); + NSLog(@"read three %@", record.primaryKey); }]; } @@ -78,7 +76,7 @@ - (void)viewWillAppear:(BOOL)animated while (count --> 0) { [[CTPersistanceAsyncExecutor sharedInstance] write:^{ NSNumber *primaryKey = [self.testTable insertValue:@"casa" forKey:@"name" error:NULL]; - NSLog(@"%@", primaryKey); + NSLog(@"write two %@", primaryKey); }]; } @@ -86,7 +84,7 @@ - (void)viewWillAppear:(BOOL)animated while (count --> 0) { [[CTPersistanceAsyncExecutor sharedInstance] read:^{ TestRecord *record = (TestRecord *)[self.testTable findWithPrimaryKey:@(count) error:NULL]; - NSLog(@"%@", record.primaryKey); + NSLog(@"read four %@", record.primaryKey); }]; } } From ead9723f2ea95fdd92b5df4b17c13a021867c83a Mon Sep 17 00:00:00 2001 From: jianglongjian <2511067577@qq.com> Date: Thu, 28 Jun 2018 18:57:28 +0800 Subject: [PATCH 12/17] add statement cache and test case --- CTPersistance.xcodeproj/project.pbxproj | 4 ++ .../QueryCommand/CTPersistanceQueryCommand.m | 47 ++++++++++++++++- .../Statement/CTPersistanceSqlStatement.h | 12 +++++ .../Statement/CTPersistanceSqlStatement.m | 26 ++++++---- .../CTPersistenceTestStatementCache.m | 51 +++++++++++++++++++ 5 files changed, 130 insertions(+), 10 deletions(-) create mode 100644 CTPersistanceTests/CTPersistenceTestStatementCache.m diff --git a/CTPersistance.xcodeproj/project.pbxproj b/CTPersistance.xcodeproj/project.pbxproj index b37d288..f345632 100644 --- a/CTPersistance.xcodeproj/project.pbxproj +++ b/CTPersistance.xcodeproj/project.pbxproj @@ -81,6 +81,7 @@ 4AF38F2D1F6905DF00801322 /* TestRecord.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E00BA1F2CC25A00E6A4DA /* TestRecord.m */; }; 4AF38F2F1F69068700801322 /* CTPersistanceTestTransaction.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AF38F2E1F69068700801322 /* CTPersistanceTestTransaction.m */; }; 6284DE2109EF43B7A3D56F18 /* libPods-CTPersistance.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 24975DE09A62B809BFED181C /* libPods-CTPersistance.a */; }; + 78B4B74020E4F16C001836CF /* CTPersistenceTestStatementCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 78B4B73F20E4F16C001836CF /* CTPersistenceTestStatementCache.m */; }; DAEEF1BE411F617114764D0F /* libPods-CTPersistanceTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D31FADFF56BDD50C7FD9C94 /* libPods-CTPersistanceTests.a */; }; /* End PBXBuildFile section */ @@ -233,6 +234,7 @@ 4AE5541320AD1F190020BAC7 /* CTPersistanceTestUpsert.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTPersistanceTestUpsert.m; sourceTree = ""; }; 4AF38F2E1F69068700801322 /* CTPersistanceTestTransaction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CTPersistanceTestTransaction.m; sourceTree = ""; }; 4D31FADFF56BDD50C7FD9C94 /* libPods-CTPersistanceTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-CTPersistanceTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 78B4B73F20E4F16C001836CF /* CTPersistenceTestStatementCache.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTPersistenceTestStatementCache.m; sourceTree = ""; }; 89D41C6489863CB58D4966E5 /* Pods-CTPersistance.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CTPersistance.debug.xcconfig"; path = "Pods/Target Support Files/Pods-CTPersistance/Pods-CTPersistance.debug.xcconfig"; sourceTree = ""; }; AD102503C09A04454242FAC0 /* Pods-CTPersistanceTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CTPersistanceTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-CTPersistanceTests/Pods-CTPersistanceTests.debug.xcconfig"; sourceTree = ""; }; D9D5304E203B48D737D87E6F /* Pods-CTPersistanceTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CTPersistanceTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-CTPersistanceTests/Pods-CTPersistanceTests.release.xcconfig"; sourceTree = ""; }; @@ -349,6 +351,7 @@ 4AC74A891FBEAEBD0092B636 /* CTPersistanceTestTableIndex.m */, 4A76096620A16C82009908A6 /* CTPersistanceTestChangeKey.m */, 4AE5541320AD1F190020BAC7 /* CTPersistanceTestUpsert.m */, + 78B4B73F20E4F16C001836CF /* CTPersistenceTestStatementCache.m */, 4A7844C41F32C1F500CF4809 /* MigrationTestModel */, 4A0E00441F2C384E00E6A4DA /* Info.plist */, ); @@ -1239,6 +1242,7 @@ files = ( 4AC74A8A1FBEAEBD0092B636 /* CTPersistanceTestTableIndex.m in Sources */, 4A17A96C1F32D0C0009356D7 /* TestMiagratorVersion_3_to_4.m in Sources */, + 78B4B74020E4F16C001836CF /* CTPersistenceTestStatementCache.m in Sources */, 4A17A9941F32F392009356D7 /* TestTableVersion1.m in Sources */, 47C1D71920A145DE00020AB5 /* Target_TestDatabase.m in Sources */, 4A17A9951F32F392009356D7 /* TestRecordVersion2.m in Sources */, diff --git a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceQueryCommand.m b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceQueryCommand.m index d656672..5297d74 100644 --- a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceQueryCommand.m +++ b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceQueryCommand.m @@ -17,6 +17,8 @@ @interface CTPersistanceQueryCommand () @property (nonatomic, strong) NSString *databaseName; @property (nonatomic, assign) BOOL shouldKeepDatabase; +@property (nonatomic, strong) NSMutableDictionary *cachedStatements; + @end @implementation CTPersistanceQueryCommand @@ -26,6 +28,7 @@ - (instancetype)initWithDatabase:(CTPersistanceDataBase *)database { self = [super init]; if (self) { + self.cachedStatements = [NSMutableDictionary dictionary]; self.shouldKeepDatabase = YES; self.database = database; } @@ -36,6 +39,7 @@ - (instancetype)initWithDatabaseName:(NSString *)databaseName { self = [super init]; if (self) { + self.cachedStatements = [NSMutableDictionary dictionary]; self.shouldKeepDatabase = NO; self.databaseName = databaseName; } @@ -44,10 +48,51 @@ - (instancetype)initWithDatabaseName:(NSString *)databaseName - (CTPersistanceSqlStatement *)compileSqlString:(NSString *)sqlString bindValueList:(NSMutableArray *)bindValueList error:(NSError *__autoreleasing *)error { - CTPersistanceSqlStatement *statement = [[CTPersistanceSqlStatement alloc] initWithSqlString:sqlString bindValueList:bindValueList database:self.database error:error]; + CTPersistanceSqlStatement *statement = nil; + + statement = [self cachedStatementForSqlString:sqlString]; + + if (statement == nil) { + + statement = [[CTPersistanceSqlStatement alloc] initWithSqlString:sqlString bindValueList:bindValueList database:self.database error:error]; + [self setCachedStatement:statement forSqlString:sqlString]; + + } else { + + sqlite3_stmt *stmt = statement.statement; + + [bindValueList enumerateObjectsUsingBlock:^(NSInvocation * _Nonnull bindInvocation, NSUInteger idx, BOOL * _Nonnull stop) { + [bindInvocation setArgument:(void *)&stmt atIndex:2]; + [bindInvocation invoke]; + }]; + [bindValueList removeAllObjects]; + + + } + return statement; } +#pragma mark - statement cache +- (CTPersistanceSqlStatement *)cachedStatementForSqlString:(NSString *)sqlString +{ + return [self.cachedStatements objectForKey:sqlString]; +} + +- (void)setCachedStatement:(CTPersistanceSqlStatement *)statement forSqlString:(NSString *)sqlString +{ + [self.cachedStatements setObject:statement forKey:sqlString]; +} + +- (void)clearCachedStatements +{ + for (CTPersistanceSqlStatement *statement in [self.cachedStatements objectEnumerator]) { + [statement close]; + } + + [self.cachedStatements removeAllObjects]; +} + #pragma mark - getters and setters - (CTPersistanceDataBase *)database { diff --git a/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.h b/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.h index 7dba16b..5957415 100644 --- a/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.h +++ b/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.h @@ -31,4 +31,16 @@ */ - (NSArray *)fetchWithError:(NSError **)error; +/** + close statement + */ +- (void)close; + +/** + reset statement + */ +- (void)reset; + +@property (nonatomic, assign, readonly) long useCount; +@property (nonatomic, unsafe_unretained, readonly) sqlite3_stmt *statement; @end diff --git a/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m b/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m index 4055e8d..fbb3b27 100644 --- a/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m +++ b/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m @@ -12,7 +12,9 @@ @interface CTPersistanceSqlStatement () -@property (nonatomic, unsafe_unretained) sqlite3_stmt *statement; +@property (nonatomic, assign, readwrite) long useCount; +@property (nonatomic, unsafe_unretained, readwrite) sqlite3_stmt *statement; + @property (nonatomic, weak) CTPersistanceDataBase *database; @end @@ -50,11 +52,20 @@ - (instancetype)initWithSqlString:(NSString *)sqlString bindValueList:(NSMutable return self; } +- (void)close { + sqlite3_finalize(self.statement); + self.statement = nil; +} + +- (void)reset { + self.useCount += 1; + sqlite3_reset(self.statement); +} + - (BOOL)executeWithError:(NSError *__autoreleasing *)error { if (error != NULL && *error != nil) { - sqlite3_finalize(self.statement); - self.statement = nil; + [self close]; return NO; } @@ -70,8 +81,7 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error return NO; } - sqlite3_finalize(statement); - self.statement = nil; + [self reset]; return YES; } @@ -79,8 +89,7 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error - (NSArray *)fetchWithError:(NSError *__autoreleasing *)error { if (error != NULL && *error != nil) { - sqlite3_finalize(self.statement); - self.statement = nil; + [self close]; return nil; } @@ -145,8 +154,7 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error [resultsArray addObject:result]; } - sqlite3_finalize(statement); - self.statement = nil; + [self reset]; return resultsArray; } diff --git a/CTPersistanceTests/CTPersistenceTestStatementCache.m b/CTPersistanceTests/CTPersistenceTestStatementCache.m new file mode 100644 index 0000000..349f40e --- /dev/null +++ b/CTPersistanceTests/CTPersistenceTestStatementCache.m @@ -0,0 +1,51 @@ +// +// CTPersistenceTestStatementCache.m +// CTPersistanceTests +// +// Created by zl on 2018/6/28. +// Copyright © 2018年 casa. All rights reserved. +// + +#import +#import "CTPersistance.h" + +@interface CTPersistenceTestStatementCache : XCTestCase + +@end + +@implementation CTPersistenceTestStatementCache + +- (void)setUp { + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + + CTPersistanceQueryCommand *queryCommand = [[CTPersistanceQueryCommand alloc] initWithDatabaseName:@"testStatementCache.sqlite"]; + NSString *tableName = @"test_statement_cache_t"; + + NSString *deleteSqlString = [NSString stringWithFormat:@"delete from `%@`;",tableName]; + [[queryCommand compileSqlString:deleteSqlString bindValueList:nil error:NULL] executeWithError:NULL]; +} + +- (void)testStatementCache { + CTPersistanceQueryCommand *queryCommand = [[CTPersistanceQueryCommand alloc] initWithDatabaseName:@"testStatementCache.sqlite"]; + NSDictionary *columnInfo = @{@"book_id": @"INTEGER"}; + NSString *tableName = @"test_statement_cache_t"; + + [[queryCommand createTable:tableName columnInfo:columnInfo error:NULL] executeWithError:NULL]; + + NSDictionary *item0 = @{@"book_id" : @0}; + NSDictionary *item1 = @{@"book_id" : @1}; + NSDictionary *item2 = @{@"book_id" : @2}; + + CTPersistanceSqlStatement *statement = [queryCommand insertTable:tableName columnInfo:columnInfo dataList:@[item0] error:NULL]; + [statement executeWithError:NULL]; + + [[queryCommand insertTable:tableName columnInfo:columnInfo dataList:@[item1] error:NULL] executeWithError:NULL]; + [[queryCommand insertTable:tableName columnInfo:columnInfo dataList:@[item2] error:NULL] executeWithError:NULL]; + + XCTAssertEqual([statement useCount], 3); +} + +@end From c78d074c9f4d50bf95ac59db29b13ec6a1757870 Mon Sep 17 00:00:00 2001 From: jianglongjian <2511067577@qq.com> Date: Tue, 10 Jul 2018 17:18:59 +0800 Subject: [PATCH 13/17] create statementCacheManager to manager statement cache; --- CTPersistance.xcodeproj/project.pbxproj | 74 ++++++++++++- .../QueryCommand/CTPersistanceQueryCommand.m | 48 +------- .../CTPersistanceStatementCacheManager.h | 19 ++++ .../CTPersistanceStatementCacheManager.m | 103 ++++++++++++++++++ .../Statement/CTPersistanceSqlStatement.h | 12 -- .../Statement/CTPersistanceSqlStatement.m | 76 +++++++++---- .../CTPersistenceTestStatementCache.m | 51 --------- 7 files changed, 249 insertions(+), 134 deletions(-) create mode 100644 CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.h create mode 100644 CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m delete mode 100644 CTPersistanceTests/CTPersistenceTestStatementCache.m diff --git a/CTPersistance.xcodeproj/project.pbxproj b/CTPersistance.xcodeproj/project.pbxproj index f345632..28f0576 100644 --- a/CTPersistance.xcodeproj/project.pbxproj +++ b/CTPersistance.xcodeproj/project.pbxproj @@ -81,7 +81,7 @@ 4AF38F2D1F6905DF00801322 /* TestRecord.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A0E00BA1F2CC25A00E6A4DA /* TestRecord.m */; }; 4AF38F2F1F69068700801322 /* CTPersistanceTestTransaction.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AF38F2E1F69068700801322 /* CTPersistanceTestTransaction.m */; }; 6284DE2109EF43B7A3D56F18 /* libPods-CTPersistance.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 24975DE09A62B809BFED181C /* libPods-CTPersistance.a */; }; - 78B4B74020E4F16C001836CF /* CTPersistenceTestStatementCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 78B4B73F20E4F16C001836CF /* CTPersistenceTestStatementCache.m */; }; + 7866EEC620F0ABB300819E75 /* CTPersistanceStatementCacheManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 7866EEC520F0ABB300819E75 /* CTPersistanceStatementCacheManager.m */; }; DAEEF1BE411F617114764D0F /* libPods-CTPersistanceTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D31FADFF56BDD50C7FD9C94 /* libPods-CTPersistanceTests.a */; }; /* End PBXBuildFile section */ @@ -234,7 +234,8 @@ 4AE5541320AD1F190020BAC7 /* CTPersistanceTestUpsert.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTPersistanceTestUpsert.m; sourceTree = ""; }; 4AF38F2E1F69068700801322 /* CTPersistanceTestTransaction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CTPersistanceTestTransaction.m; sourceTree = ""; }; 4D31FADFF56BDD50C7FD9C94 /* libPods-CTPersistanceTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-CTPersistanceTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 78B4B73F20E4F16C001836CF /* CTPersistenceTestStatementCache.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTPersistenceTestStatementCache.m; sourceTree = ""; }; + 7866EEC420F0ABB300819E75 /* CTPersistanceStatementCacheManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CTPersistanceStatementCacheManager.h; sourceTree = ""; }; + 7866EEC520F0ABB300819E75 /* CTPersistanceStatementCacheManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTPersistanceStatementCacheManager.m; sourceTree = ""; }; 89D41C6489863CB58D4966E5 /* Pods-CTPersistance.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CTPersistance.debug.xcconfig"; path = "Pods/Target Support Files/Pods-CTPersistance/Pods-CTPersistance.debug.xcconfig"; sourceTree = ""; }; AD102503C09A04454242FAC0 /* Pods-CTPersistanceTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CTPersistanceTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-CTPersistanceTests/Pods-CTPersistanceTests.debug.xcconfig"; sourceTree = ""; }; D9D5304E203B48D737D87E6F /* Pods-CTPersistanceTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CTPersistanceTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-CTPersistanceTests/Pods-CTPersistanceTests.release.xcconfig"; sourceTree = ""; }; @@ -351,7 +352,6 @@ 4AC74A891FBEAEBD0092B636 /* CTPersistanceTestTableIndex.m */, 4A76096620A16C82009908A6 /* CTPersistanceTestChangeKey.m */, 4AE5541320AD1F190020BAC7 /* CTPersistanceTestUpsert.m */, - 78B4B73F20E4F16C001836CF /* CTPersistenceTestStatementCache.m */, 4A7844C41F32C1F500CF4809 /* MigrationTestModel */, 4A0E00441F2C384E00E6A4DA /* Info.plist */, ); @@ -474,6 +474,8 @@ 4A0E00741F2CBD6300E6A4DA /* QueryCommand */ = { isa = PBXGroup; children = ( + 7866EEC420F0ABB300819E75 /* CTPersistanceStatementCacheManager.h */, + 7866EEC520F0ABB300819E75 /* CTPersistanceStatementCacheManager.m */, 4A5E3BEE1F403A3200946749 /* Statement */, 4A0E00751F2CBD6300E6A4DA /* Categories */, 4A0E00821F2CBD6300E6A4DA /* CTPersistanceQueryCommand.h */, @@ -1060,6 +1062,8 @@ 4A0E00211F2C384E00E6A4DA /* Sources */, 4A0E00221F2C384E00E6A4DA /* Frameworks */, 4A0E00231F2C384E00E6A4DA /* Resources */, + 10751F76ED1BC0072083D1EB /* [CP] Embed Pods Frameworks */, + 8C4AC309B366006C6CA8BB6A /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -1078,6 +1082,8 @@ 4A0E003A1F2C384E00E6A4DA /* Sources */, 4A0E003B1F2C384E00E6A4DA /* Frameworks */, 4A0E003C1F2C384E00E6A4DA /* Resources */, + BCC3FE446475752A1DF7D1B8 /* [CP] Embed Pods Frameworks */, + 47D5EC83DAF4AF6795D548BD /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -1152,6 +1158,36 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 10751F76ED1BC0072083D1EB /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-CTPersistance/Pods-CTPersistance-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 47D5EC83DAF4AF6795D548BD /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-CTPersistanceTests/Pods-CTPersistanceTests-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; 810DA35DC562A3E4A0F26373 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -1170,6 +1206,36 @@ 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"; showEnvVarsInLog = 0; }; + 8C4AC309B366006C6CA8BB6A /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-CTPersistance/Pods-CTPersistance-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + BCC3FE446475752A1DF7D1B8 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-CTPersistanceTests/Pods-CTPersistanceTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; DFE4FB2A9CE5F55E85A7BA6A /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -1217,6 +1283,7 @@ 4A0238741F3FFBA800EE91C7 /* TestTable.m in Sources */, 4AC74A631FBE7DC60092B636 /* ViewRecordAndMergeDemoViewController.m in Sources */, 4AC74A731FBE7EC90092B636 /* ItemDetailTable.m in Sources */, + 7866EEC620F0ABB300819E75 /* CTPersistanceStatementCacheManager.m in Sources */, 4A0E002A1F2C384E00E6A4DA /* main.m in Sources */, 4A5E3BF11F403A4A00946749 /* CTPersistanceSqlStatement.m in Sources */, 4ACE635B1F340E54007F3E6D /* NSDictionary+KeyValueBind.m in Sources */, @@ -1242,7 +1309,6 @@ files = ( 4AC74A8A1FBEAEBD0092B636 /* CTPersistanceTestTableIndex.m in Sources */, 4A17A96C1F32D0C0009356D7 /* TestMiagratorVersion_3_to_4.m in Sources */, - 78B4B74020E4F16C001836CF /* CTPersistenceTestStatementCache.m in Sources */, 4A17A9941F32F392009356D7 /* TestTableVersion1.m in Sources */, 47C1D71920A145DE00020AB5 /* Target_TestDatabase.m in Sources */, 4A17A9951F32F392009356D7 /* TestRecordVersion2.m in Sources */, diff --git a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceQueryCommand.m b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceQueryCommand.m index 5297d74..214c332 100644 --- a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceQueryCommand.m +++ b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceQueryCommand.m @@ -17,8 +17,6 @@ @interface CTPersistanceQueryCommand () @property (nonatomic, strong) NSString *databaseName; @property (nonatomic, assign) BOOL shouldKeepDatabase; -@property (nonatomic, strong) NSMutableDictionary *cachedStatements; - @end @implementation CTPersistanceQueryCommand @@ -28,7 +26,6 @@ - (instancetype)initWithDatabase:(CTPersistanceDataBase *)database { self = [super init]; if (self) { - self.cachedStatements = [NSMutableDictionary dictionary]; self.shouldKeepDatabase = YES; self.database = database; } @@ -39,7 +36,6 @@ - (instancetype)initWithDatabaseName:(NSString *)databaseName { self = [super init]; if (self) { - self.cachedStatements = [NSMutableDictionary dictionary]; self.shouldKeepDatabase = NO; self.databaseName = databaseName; } @@ -48,49 +44,7 @@ - (instancetype)initWithDatabaseName:(NSString *)databaseName - (CTPersistanceSqlStatement *)compileSqlString:(NSString *)sqlString bindValueList:(NSMutableArray *)bindValueList error:(NSError *__autoreleasing *)error { - CTPersistanceSqlStatement *statement = nil; - - statement = [self cachedStatementForSqlString:sqlString]; - - if (statement == nil) { - - statement = [[CTPersistanceSqlStatement alloc] initWithSqlString:sqlString bindValueList:bindValueList database:self.database error:error]; - [self setCachedStatement:statement forSqlString:sqlString]; - - } else { - - sqlite3_stmt *stmt = statement.statement; - - [bindValueList enumerateObjectsUsingBlock:^(NSInvocation * _Nonnull bindInvocation, NSUInteger idx, BOOL * _Nonnull stop) { - [bindInvocation setArgument:(void *)&stmt atIndex:2]; - [bindInvocation invoke]; - }]; - [bindValueList removeAllObjects]; - - - } - - return statement; -} - -#pragma mark - statement cache -- (CTPersistanceSqlStatement *)cachedStatementForSqlString:(NSString *)sqlString -{ - return [self.cachedStatements objectForKey:sqlString]; -} - -- (void)setCachedStatement:(CTPersistanceSqlStatement *)statement forSqlString:(NSString *)sqlString -{ - [self.cachedStatements setObject:statement forKey:sqlString]; -} - -- (void)clearCachedStatements -{ - for (CTPersistanceSqlStatement *statement in [self.cachedStatements objectEnumerator]) { - [statement close]; - } - - [self.cachedStatements removeAllObjects]; + return [[CTPersistanceSqlStatement alloc] initWithSqlString:sqlString bindValueList:bindValueList database:self.database error:error]; } #pragma mark - getters and setters diff --git a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.h b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.h new file mode 100644 index 0000000..be04d26 --- /dev/null +++ b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.h @@ -0,0 +1,19 @@ +// +// CTPersistanceStatementCacheManager.h +// CTPersistance +// +// Created by longjianjiang on 2018/7/7. +// Copyright © 2018年 casa. All rights reserved. +// + +#import +#import + +@interface CTPersistanceStatementCacheManager : NSObject + ++ (instancetype)sharedInstance; + +- (sqlite3_stmt *)getCachedStatementWithSQLString:(NSString *)sqlString; +- (void)setCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString; + +@end diff --git a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m new file mode 100644 index 0000000..9f1fa37 --- /dev/null +++ b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m @@ -0,0 +1,103 @@ +// +// CTPersistanceStatementCacheManager.m +// CTPersistance +// +// Created by zl on 2018/7/7. +// Copyright © 2018年 casa. All rights reserved. +// + +#import "CTPersistanceStatementCacheManager.h" + +@interface CTPersistanceStatementCacheManager() + +@property (nonatomic, strong) NSMutableDictionary *cachedStatements; + +@property (nonatomic, strong) NSMutableDictionary *useCount; + +@end + + +@implementation CTPersistanceStatementCacheManager + +#pragma mark - public methods ++ (instancetype)sharedInstance +{ + static CTPersistanceStatementCacheManager *sharedInstance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedInstance = [[CTPersistanceStatementCacheManager alloc] init]; + }); + return sharedInstance; +} + +- (sqlite3_stmt *)getCachedStatementWithSQLString:(NSString *)sqlString +{ + if (![self.cachedStatements objectForKey:sqlString]) { + return NULL; + } + + @synchronized(self) { + sqlite3_stmt *pStmt = [[self.cachedStatements objectForKey:sqlString] pointerValue]; + + NSInteger count = [[self.useCount objectForKey:sqlString] integerValue] + 1; + [self.useCount setObject:@(count) forKey:sqlString]; +#ifdef DEBUG + NSLog(@" sqlstring %@ , use count is %@", sqlString, [self.useCount objectForKey:sqlString]); +#endif + if (count >= 55) { + return NULL; + } + return pStmt; + } + +} + +- (void)setCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString +{ + if ([self.cachedStatements objectForKey:sqlString]) { + return; + } + + NSArray *notCachedSQL = @[@"PRAGMA"]; + BOOL shouldCache = YES; + for (NSString *sql in notCachedSQL) { + if ([[sqlString uppercaseString] rangeOfString:sql].length) { + shouldCache = NO; + break; + } + } + + if (1) { + @synchronized(self) { + [self.cachedStatements setObject:[NSValue valueWithPointer:pStmt] forKey:sqlString]; + [self.useCount setObject:@(0) forKey:sqlString]; + } + } + +} + +#pragma mark - life cycle +- (instancetype)init +{ + self = [super init]; + if (self) { + self.cachedStatements = [NSMutableDictionary dictionary]; + self.useCount = [NSMutableDictionary dictionary]; + } + return self; +} + +- (void)dealloc +{ + @synchronized(self) { + for (id statement in [self.cachedStatements objectEnumerator]) { + sqlite3_finalize((sqlite3_stmt *)[statement pointerValue]); + } + + [self.cachedStatements removeAllObjects]; + [self.useCount removeAllObjects]; + } + +} + +@end diff --git a/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.h b/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.h index 5957415..7dba16b 100644 --- a/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.h +++ b/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.h @@ -31,16 +31,4 @@ */ - (NSArray *)fetchWithError:(NSError **)error; -/** - close statement - */ -- (void)close; - -/** - reset statement - */ -- (void)reset; - -@property (nonatomic, assign, readonly) long useCount; -@property (nonatomic, unsafe_unretained, readonly) sqlite3_stmt *statement; @end diff --git a/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m b/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m index fbb3b27..45a5d7b 100644 --- a/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m +++ b/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m @@ -10,13 +10,17 @@ #import #import "CTPersistanceConfiguration.h" +#import "CTPersistanceStatementCacheManager.h" + @interface CTPersistanceSqlStatement () -@property (nonatomic, assign, readwrite) long useCount; @property (nonatomic, unsafe_unretained, readwrite) sqlite3_stmt *statement; @property (nonatomic, weak) CTPersistanceDataBase *database; +@property (nonatomic, copy) NSString *sqlString; +@property (nonatomic, strong) CTPersistanceStatementCacheManager *statementCacheManager; + @end @implementation CTPersistanceSqlStatement @@ -26,28 +30,40 @@ - (instancetype)initWithSqlString:(NSString *)sqlString bindValueList:(NSMutable self = [super init]; if (self) { self.database = database; -#warning todo statement cache - sqlite3_stmt *statement = nil; - int result = sqlite3_prepare_v2(database.database, [sqlString UTF8String], (int)[sqlString lengthOfBytesUsingEncoding:NSUTF8StringEncoding], &statement, NULL); - - if (result != SQLITE_OK) { - self.statement = nil; - NSString *errorMessage = [NSString stringWithUTF8String:sqlite3_errmsg(database.database)]; - NSError *generatedError = [NSError errorWithDomain:kCTPersistanceErrorDomain code:CTPersistanceErrorCodeQueryStringError userInfo:@{NSLocalizedDescriptionKey:[NSString stringWithFormat:@"\n======================\nQuery Error: \n Origin Query is : %@\n Error Message is: %@\n======================\n", sqlString, errorMessage]}]; - if (error != NULL) { - *error = generatedError; + self.sqlString = sqlString; + sqlite3_stmt *statement = [self.statementCacheManager getCachedStatementWithSQLString:sqlString]; + + if (!statement) { + + int result = sqlite3_prepare_v2(database.database, [sqlString UTF8String], (int)sqlString.length, &statement, NULL); + + if (result != SQLITE_OK) { + self.statement = nil; + NSString *errorMessage = [NSString stringWithUTF8String:sqlite3_errmsg(database.database)]; + NSError *generatedError = [NSError errorWithDomain:kCTPersistanceErrorDomain code:CTPersistanceErrorCodeQueryStringError userInfo:@{NSLocalizedDescriptionKey:[NSString stringWithFormat:@"\n======================\nQuery Error: \n Origin Query is : %@\n Error Message is: %@\n======================\n", sqlString, errorMessage]}]; + if (error != NULL) { + *error = generatedError; + } + NSLog(@"\n\n\n======================\n\n%@\n\n%s\n%s(%d):\n\n\terror is:\n\t\t %@ \n\n\t sqlString is %@\n\n======================\n\n\n", [NSThread currentThread], __FILE__, __FUNCTION__, __LINE__, errorMessage, sqlString); + sqlite3_finalize(statement); + return nil; } - NSLog(@"\n\n\n======================\n\n%@\n\n%s\n%s(%d):\n\n\terror is:\n\t\t %@ \n\n\t sqlString is %@\n\n======================\n\n\n", [NSThread currentThread], __FILE__, __FUNCTION__, __LINE__, errorMessage, sqlString); - sqlite3_finalize(statement); - return nil; + + self.statement = statement; + + } else { + + self.statement = statement; + } - self.statement = statement; + [bindValueList enumerateObjectsUsingBlock:^(NSInvocation * _Nonnull bindInvocation, NSUInteger idx, BOOL * _Nonnull stop) { [bindInvocation setArgument:(void *)&statement atIndex:2]; [bindInvocation invoke]; }]; [bindValueList removeAllObjects]; + } return self; } @@ -58,7 +74,6 @@ - (void)close { } - (void)reset { - self.useCount += 1; sqlite3_reset(self.statement); } @@ -80,9 +95,15 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error sqlite3_finalize(statement); return NO; } + + [self.statementCacheManager setCachedStatement:self.statement forSQLString:self.sqlString]; - [self reset]; - + if (![self.statementCacheManager getCachedStatementWithSQLString:self.sqlString]) { + [self close]; + } else { + [self reset]; + } + return YES; } @@ -153,10 +174,25 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error } [resultsArray addObject:result]; } - - [self reset]; + + [self.statementCacheManager setCachedStatement:self.statement forSQLString:self.sqlString]; + + if (![self.statementCacheManager getCachedStatementWithSQLString:self.sqlString]) { + [self close]; + } else { + [self reset]; + } return resultsArray; } +#pragma mark - getter and setter +- (CTPersistanceStatementCacheManager *)statementCacheManager { + if (_statementCacheManager == nil) { + _statementCacheManager = [CTPersistanceStatementCacheManager sharedInstance]; + } + return _statementCacheManager; +} + + @end diff --git a/CTPersistanceTests/CTPersistenceTestStatementCache.m b/CTPersistanceTests/CTPersistenceTestStatementCache.m deleted file mode 100644 index 349f40e..0000000 --- a/CTPersistanceTests/CTPersistenceTestStatementCache.m +++ /dev/null @@ -1,51 +0,0 @@ -// -// CTPersistenceTestStatementCache.m -// CTPersistanceTests -// -// Created by zl on 2018/6/28. -// Copyright © 2018年 casa. All rights reserved. -// - -#import -#import "CTPersistance.h" - -@interface CTPersistenceTestStatementCache : XCTestCase - -@end - -@implementation CTPersistenceTestStatementCache - -- (void)setUp { - // Put setup code here. This method is called before the invocation of each test method in the class. -} - -- (void)tearDown { - - CTPersistanceQueryCommand *queryCommand = [[CTPersistanceQueryCommand alloc] initWithDatabaseName:@"testStatementCache.sqlite"]; - NSString *tableName = @"test_statement_cache_t"; - - NSString *deleteSqlString = [NSString stringWithFormat:@"delete from `%@`;",tableName]; - [[queryCommand compileSqlString:deleteSqlString bindValueList:nil error:NULL] executeWithError:NULL]; -} - -- (void)testStatementCache { - CTPersistanceQueryCommand *queryCommand = [[CTPersistanceQueryCommand alloc] initWithDatabaseName:@"testStatementCache.sqlite"]; - NSDictionary *columnInfo = @{@"book_id": @"INTEGER"}; - NSString *tableName = @"test_statement_cache_t"; - - [[queryCommand createTable:tableName columnInfo:columnInfo error:NULL] executeWithError:NULL]; - - NSDictionary *item0 = @{@"book_id" : @0}; - NSDictionary *item1 = @{@"book_id" : @1}; - NSDictionary *item2 = @{@"book_id" : @2}; - - CTPersistanceSqlStatement *statement = [queryCommand insertTable:tableName columnInfo:columnInfo dataList:@[item0] error:NULL]; - [statement executeWithError:NULL]; - - [[queryCommand insertTable:tableName columnInfo:columnInfo dataList:@[item1] error:NULL] executeWithError:NULL]; - [[queryCommand insertTable:tableName columnInfo:columnInfo dataList:@[item2] error:NULL] executeWithError:NULL]; - - XCTAssertEqual([statement useCount], 3); -} - -@end From 9e09519dbbc32e5d652143282cf4637ac1004b7e Mon Sep 17 00:00:00 2001 From: jianglongjian <2511067577@qq.com> Date: Wed, 11 Jul 2018 16:17:05 +0800 Subject: [PATCH 14/17] when sql execute fail , remove that sql corresponding statement; --- .../CTPersistanceStatementCacheManager.h | 1 + .../CTPersistanceStatementCacheManager.m | 17 +++++++--- .../Statement/CTPersistanceSqlStatement.m | 33 +++++++++++-------- .../CTPersistanceTestChangeKey.m | 2 ++ .../CTPersistanceTestMigration.m | 3 ++ 5 files changed, 39 insertions(+), 17 deletions(-) diff --git a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.h b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.h index be04d26..f755727 100644 --- a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.h +++ b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.h @@ -15,5 +15,6 @@ - (sqlite3_stmt *)getCachedStatementWithSQLString:(NSString *)sqlString; - (void)setCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString; +- (void)removeCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString; @end diff --git a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m index 9f1fa37..99d19b5 100644 --- a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m +++ b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m @@ -44,9 +44,6 @@ - (sqlite3_stmt *)getCachedStatementWithSQLString:(NSString *)sqlString #ifdef DEBUG NSLog(@" sqlstring %@ , use count is %@", sqlString, [self.useCount objectForKey:sqlString]); #endif - if (count >= 55) { - return NULL; - } return pStmt; } @@ -58,7 +55,7 @@ - (void)setCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlStr return; } - NSArray *notCachedSQL = @[@"PRAGMA"]; + NSArray *notCachedSQL = @[@"PRAGMA", @"CREATE"]; BOOL shouldCache = YES; for (NSString *sql in notCachedSQL) { if ([[sqlString uppercaseString] rangeOfString:sql].length) { @@ -76,6 +73,18 @@ - (void)setCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlStr } +- (void)removeCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString { + if (![self.cachedStatements objectForKey:sqlString]) { + return; + } + + @synchronized(self) { + [self.cachedStatements removeObjectForKey:sqlString]; + [self.useCount removeObjectForKey:sqlString]; + } +} + + #pragma mark - life cycle - (instancetype)init { diff --git a/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m b/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m index 45a5d7b..cb77efe 100644 --- a/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m +++ b/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m @@ -45,6 +45,7 @@ - (instancetype)initWithSqlString:(NSString *)sqlString bindValueList:(NSMutable *error = generatedError; } NSLog(@"\n\n\n======================\n\n%@\n\n%s\n%s(%d):\n\n\terror is:\n\t\t %@ \n\n\t sqlString is %@\n\n======================\n\n\n", [NSThread currentThread], __FILE__, __FUNCTION__, __LINE__, errorMessage, sqlString); + sqlite3_finalize(statement); return nil; } @@ -69,6 +70,8 @@ - (instancetype)initWithSqlString:(NSString *)sqlString bindValueList:(NSMutable } - (void)close { + [self.statementCacheManager removeCachedStatement:self.statement forSQLString:self.sqlString]; + sqlite3_finalize(self.statement); self.statement = nil; } @@ -89,20 +92,19 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error int result = sqlite3_step(statement); if (result != SQLITE_DONE && error) { + + [self.statementCacheManager removeCachedStatement:self.statement forSQLString:self.sqlString]; + const char *errorMsg = sqlite3_errmsg(self.database.database); NSError *generatedError = [NSError errorWithDomain:kCTPersistanceErrorDomain code:CTPersistanceErrorCodeQueryStringError userInfo:@{NSLocalizedDescriptionKey:[NSString stringWithFormat:@"\n======================\nQuery Error: \n Origin Query is : %@\n Error Message is: %@\n======================\n", [NSString stringWithUTF8String:sqlite3_sql(statement)], [NSString stringWithCString:errorMsg encoding:NSUTF8StringEncoding]]}]; *error = generatedError; sqlite3_finalize(statement); + return NO; } [self.statementCacheManager setCachedStatement:self.statement forSQLString:self.sqlString]; - - if (![self.statementCacheManager getCachedStatementWithSQLString:self.sqlString]) { - [self close]; - } else { - [self reset]; - } + [self reset]; return YES; } @@ -114,10 +116,16 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error return nil; } - NSMutableArray *resultsArray = [[NSMutableArray alloc] init]; + NSMutableArray *resultsArray = nil; sqlite3_stmt *statement = self.statement; + while (sqlite3_step(statement) == SQLITE_ROW) { + + if (!resultsArray) { + resultsArray = [NSMutableArray new]; + } + int columns = sqlite3_column_count(statement); NSMutableDictionary *result = [[NSMutableDictionary alloc] initWithCapacity:columns]; @@ -175,14 +183,13 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error [resultsArray addObject:result]; } - [self.statementCacheManager setCachedStatement:self.statement forSQLString:self.sqlString]; - - if (![self.statementCacheManager getCachedStatementWithSQLString:self.sqlString]) { - [self close]; - } else { + if (resultsArray) { + [self.statementCacheManager setCachedStatement:self.statement forSQLString:self.sqlString]; [self reset]; + } else { + [self close]; } - + return resultsArray; } diff --git a/CTPersistanceTests/CTPersistanceTestChangeKey.m b/CTPersistanceTests/CTPersistanceTestChangeKey.m index 7dbd70c..b85f5b4 100644 --- a/CTPersistanceTests/CTPersistanceTestChangeKey.m +++ b/CTPersistanceTests/CTPersistanceTestChangeKey.m @@ -22,6 +22,7 @@ @interface CTPersistanceTestChangeKey : XCTestCase @implementation CTPersistanceTestChangeKey + - (void)setUp { [super setUp]; // Put setup code here. This method is called before the invocation of each test method in the class. @@ -144,4 +145,5 @@ - (NSString *)databaseFilePath return _databaseFilePath; } + @end diff --git a/CTPersistanceTests/CTPersistanceTestMigration.m b/CTPersistanceTests/CTPersistanceTestMigration.m index fc64aa5..fedfe88 100644 --- a/CTPersistanceTests/CTPersistanceTestMigration.m +++ b/CTPersistanceTests/CTPersistanceTestMigration.m @@ -24,6 +24,7 @@ @interface CTPersistanceTestMigration : XCTestCase @implementation CTPersistanceTestMigration + - (void)setUp { [super setUp]; @@ -261,4 +262,6 @@ - (void)testMiagration_brandNew_v4 XCTAssertEqual(columnInfo.count, 5); } + + @end From 02f00be9bbc16b220cbde6cdda03459c52a2d211 Mon Sep 17 00:00:00 2001 From: jianglongjian <2511067577@qq.com> Date: Wed, 11 Jul 2018 18:24:39 +0800 Subject: [PATCH 15/17] statement cache based on different database; --- CTPersistance.xcodeproj/project.pbxproj | 12 +- .../Database/CTPersistanceDataBase.m | 4 + .../CTPersistanceStatementCacheManager.h | 10 +- .../CTPersistanceStatementCacheManager.m | 120 ++++++++++-------- .../Statement/CTPersistanceSqlStatement.m | 28 ++-- 5 files changed, 94 insertions(+), 80 deletions(-) diff --git a/CTPersistance.xcodeproj/project.pbxproj b/CTPersistance.xcodeproj/project.pbxproj index 28f0576..3a32984 100644 --- a/CTPersistance.xcodeproj/project.pbxproj +++ b/CTPersistance.xcodeproj/project.pbxproj @@ -1106,12 +1106,12 @@ TargetAttributes = { 4A0E00241F2C384E00E6A4DA = { CreatedOnToolsVersion = 8.3.2; - DevelopmentTeam = THS9SRQVES; + DevelopmentTeam = 74MF5V7Z8J; ProvisioningStyle = Automatic; }; 4A0E003D1F2C384E00E6A4DA = { CreatedOnToolsVersion = 8.3.2; - DevelopmentTeam = THS9SRQVES; + DevelopmentTeam = 74MF5V7Z8J; ProvisioningStyle = Automatic; TestTargetID = 4A0E00241F2C384E00E6A4DA; }; @@ -1479,7 +1479,7 @@ baseConfigurationReference = 89D41C6489863CB58D4966E5 /* Pods-CTPersistance.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = THS9SRQVES; + DEVELOPMENT_TEAM = 74MF5V7Z8J; INFOPLIST_FILE = CTPersistance/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; @@ -1493,7 +1493,7 @@ baseConfigurationReference = 0E3659C0BE44F61448420F54 /* Pods-CTPersistance.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = THS9SRQVES; + DEVELOPMENT_TEAM = 74MF5V7Z8J; INFOPLIST_FILE = CTPersistance/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; @@ -1507,7 +1507,7 @@ baseConfigurationReference = AD102503C09A04454242FAC0 /* Pods-CTPersistanceTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; - DEVELOPMENT_TEAM = THS9SRQVES; + DEVELOPMENT_TEAM = 74MF5V7Z8J; INFOPLIST_FILE = CTPersistanceTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = casa.CTPersistanceTests; @@ -1521,7 +1521,7 @@ baseConfigurationReference = D9D5304E203B48D737D87E6F /* Pods-CTPersistanceTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; - DEVELOPMENT_TEAM = THS9SRQVES; + DEVELOPMENT_TEAM = 74MF5V7Z8J; INFOPLIST_FILE = CTPersistanceTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = casa.CTPersistanceTests; diff --git a/CTPersistance/CTPersistance/Database/CTPersistanceDataBase.m b/CTPersistance/CTPersistance/Database/CTPersistanceDataBase.m index f1d968b..a20ceb3 100644 --- a/CTPersistance/CTPersistance/Database/CTPersistanceDataBase.m +++ b/CTPersistance/CTPersistance/Database/CTPersistanceDataBase.m @@ -13,6 +13,8 @@ #import "CTPersistanceVersionTable.h" #import +#import "CTPersistanceStatementCacheManager.h" + extern SQLITE_API int sqlite3_key(sqlite3 *db, const void *pKey, int nKey); extern SQLITE_API int sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey); @@ -115,6 +117,8 @@ - (void)closeDatabase _database = NULL; _databaseFilePath = nil; + + [[CTPersistanceStatementCacheManager sharedInstance] clearDatabaseStatementCache:self.databaseName]; } #pragma mark - private methods diff --git a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.h b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.h index f755727..162649e 100644 --- a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.h +++ b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.h @@ -13,8 +13,12 @@ + (instancetype)sharedInstance; -- (sqlite3_stmt *)getCachedStatementWithSQLString:(NSString *)sqlString; -- (void)setCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString; -- (void)removeCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString; + +- (void)setCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName; +- (sqlite3_stmt *)getCachedStatementWithSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName; +- (void)removeCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName; + +- (void)clearDatabaseStatementCache:(NSString *)databaseName; +- (void)clearAllDatabaseStatementCache; @end diff --git a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m index 99d19b5..6eb8865 100644 --- a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m +++ b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m @@ -2,7 +2,7 @@ // CTPersistanceStatementCacheManager.m // CTPersistance // -// Created by zl on 2018/7/7. +// Created by longjianjiang on 2018/7/7. // Copyright © 2018年 casa. All rights reserved. // @@ -10,103 +10,115 @@ @interface CTPersistanceStatementCacheManager() -@property (nonatomic, strong) NSMutableDictionary *cachedStatements; - -@property (nonatomic, strong) NSMutableDictionary *useCount; +@property (nonatomic, strong) NSMutableDictionary *cachedTree; @end @implementation CTPersistanceStatementCacheManager -#pragma mark - public methods -+ (instancetype)sharedInstance +- (sqlite3_stmt *)getCachedStatementWithSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName { - static CTPersistanceStatementCacheManager *sharedInstance = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - sharedInstance = [[CTPersistanceStatementCacheManager alloc] init]; - }); - return sharedInstance; -} - -- (sqlite3_stmt *)getCachedStatementWithSQLString:(NSString *)sqlString -{ - if (![self.cachedStatements objectForKey:sqlString]) { + if (![self.cachedTree objectForKey:databaseName]) { + return NULL; + } + + NSMutableDictionary *cachedStatements = [self.cachedTree objectForKey:databaseName]; + + if (![cachedStatements objectForKey:sqlString]) { return NULL; } @synchronized(self) { - sqlite3_stmt *pStmt = [[self.cachedStatements objectForKey:sqlString] pointerValue]; - - NSInteger count = [[self.useCount objectForKey:sqlString] integerValue] + 1; - [self.useCount setObject:@(count) forKey:sqlString]; -#ifdef DEBUG - NSLog(@" sqlstring %@ , use count is %@", sqlString, [self.useCount objectForKey:sqlString]); -#endif + sqlite3_stmt *pStmt = [[cachedStatements objectForKey:sqlString] pointerValue]; return pStmt; } +} + +- (void)setCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName +{ + if (![self.cachedTree objectForKey:databaseName]) { + [self.cachedTree setObject:[NSMutableDictionary dictionary] forKey:databaseName]; + } + NSMutableDictionary *cachedStatements = [self.cachedTree objectForKey:databaseName]; + + if ([cachedStatements objectForKey:sqlString]) { + return; + } + + @synchronized(self) { + [cachedStatements setObject:[NSValue valueWithPointer:pStmt] forKey:sqlString]; + } } -- (void)setCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString +- (void)removeCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName { - if ([self.cachedStatements objectForKey:sqlString]) { + if (![self.cachedTree objectForKey:databaseName]) { return; } - NSArray *notCachedSQL = @[@"PRAGMA", @"CREATE"]; - BOOL shouldCache = YES; - for (NSString *sql in notCachedSQL) { - if ([[sqlString uppercaseString] rangeOfString:sql].length) { - shouldCache = NO; - break; - } + NSMutableDictionary *cachedStatements = [self.cachedTree objectForKey:databaseName]; + + if (![cachedStatements objectForKey:sqlString]) { + return; } - if (1) { - @synchronized(self) { - [self.cachedStatements setObject:[NSValue valueWithPointer:pStmt] forKey:sqlString]; - [self.useCount setObject:@(0) forKey:sqlString]; - } + @synchronized(self) { + [cachedStatements removeObjectForKey:sqlString]; } - } -- (void)removeCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString { - if (![self.cachedStatements objectForKey:sqlString]) { +- (void)clearDatabaseStatementCache:(NSString *)databaseName +{ + if (![self.cachedTree objectForKey:databaseName]) { return; } + NSMutableDictionary *cachedStatements = [self.cachedTree objectForKey:databaseName]; + @synchronized(self) { - [self.cachedStatements removeObjectForKey:sqlString]; - [self.useCount removeObjectForKey:sqlString]; + for (id statement in [cachedStatements objectEnumerator]) { + sqlite3_finalize((sqlite3_stmt *)[statement pointerValue]); + } + + [cachedStatements removeAllObjects]; + [self.cachedTree removeObjectForKey:databaseName]; + } +} + +- (void)clearAllDatabaseStatementCache +{ + for (NSString *databaseName in [self.cachedTree objectEnumerator]) { + [self clearDatabaseStatementCache:databaseName]; } } +#pragma mark - public methods ++ (instancetype)sharedInstance +{ + static CTPersistanceStatementCacheManager *sharedInstance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedInstance = [[CTPersistanceStatementCacheManager alloc] init]; + }); + return sharedInstance; +} + #pragma mark - life cycle - (instancetype)init { self = [super init]; if (self) { - self.cachedStatements = [NSMutableDictionary dictionary]; - self.useCount = [NSMutableDictionary dictionary]; + self.cachedTree = [NSMutableDictionary dictionary]; } return self; } - (void)dealloc { - @synchronized(self) { - for (id statement in [self.cachedStatements objectEnumerator]) { - sqlite3_finalize((sqlite3_stmt *)[statement pointerValue]); - } - - [self.cachedStatements removeAllObjects]; - [self.useCount removeAllObjects]; - } - + [self clearAllDatabaseStatementCache]; } @end diff --git a/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m b/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m index cb77efe..d786567 100644 --- a/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m +++ b/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m @@ -31,14 +31,14 @@ - (instancetype)initWithSqlString:(NSString *)sqlString bindValueList:(NSMutable if (self) { self.database = database; self.sqlString = sqlString; - sqlite3_stmt *statement = [self.statementCacheManager getCachedStatementWithSQLString:sqlString]; + sqlite3_stmt *statement = [self.statementCacheManager getCachedStatementWithSQLString:sqlString atDatabase:self.database.databaseName]; if (!statement) { int result = sqlite3_prepare_v2(database.database, [sqlString UTF8String], (int)sqlString.length, &statement, NULL); if (result != SQLITE_OK) { - self.statement = nil; + NSString *errorMessage = [NSString stringWithUTF8String:sqlite3_errmsg(database.database)]; NSError *generatedError = [NSError errorWithDomain:kCTPersistanceErrorDomain code:CTPersistanceErrorCodeQueryStringError userInfo:@{NSLocalizedDescriptionKey:[NSString stringWithFormat:@"\n======================\nQuery Error: \n Origin Query is : %@\n Error Message is: %@\n======================\n", sqlString, errorMessage]}]; if (error != NULL) { @@ -46,7 +46,8 @@ - (instancetype)initWithSqlString:(NSString *)sqlString bindValueList:(NSMutable } NSLog(@"\n\n\n======================\n\n%@\n\n%s\n%s(%d):\n\n\terror is:\n\t\t %@ \n\n\t sqlString is %@\n\n======================\n\n\n", [NSThread currentThread], __FILE__, __FUNCTION__, __LINE__, errorMessage, sqlString); - sqlite3_finalize(statement); + [self close]; + return nil; } @@ -70,7 +71,7 @@ - (instancetype)initWithSqlString:(NSString *)sqlString bindValueList:(NSMutable } - (void)close { - [self.statementCacheManager removeCachedStatement:self.statement forSQLString:self.sqlString]; + [self.statementCacheManager removeCachedStatement:self.statement forSQLString:self.sqlString atDatabase:self.database.databaseName]; sqlite3_finalize(self.statement); self.statement = nil; @@ -93,7 +94,7 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error if (result != SQLITE_DONE && error) { - [self.statementCacheManager removeCachedStatement:self.statement forSQLString:self.sqlString]; + [self.statementCacheManager removeCachedStatement:self.statement forSQLString:self.sqlString atDatabase:self.database.databaseName]; const char *errorMsg = sqlite3_errmsg(self.database.database); NSError *generatedError = [NSError errorWithDomain:kCTPersistanceErrorDomain code:CTPersistanceErrorCodeQueryStringError userInfo:@{NSLocalizedDescriptionKey:[NSString stringWithFormat:@"\n======================\nQuery Error: \n Origin Query is : %@\n Error Message is: %@\n======================\n", [NSString stringWithUTF8String:sqlite3_sql(statement)], [NSString stringWithCString:errorMsg encoding:NSUTF8StringEncoding]]}]; @@ -103,12 +104,13 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error return NO; } - [self.statementCacheManager setCachedStatement:self.statement forSQLString:self.sqlString]; + [self.statementCacheManager setCachedStatement:self.statement forSQLString:self.sqlString atDatabase:self.database.databaseName]; [self reset]; return YES; } + - (NSArray *)fetchWithError:(NSError *__autoreleasing *)error { if (error != NULL && *error != nil) { @@ -116,16 +118,12 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error return nil; } - NSMutableArray *resultsArray = nil; + NSMutableArray *resultsArray = [NSMutableArray array]; sqlite3_stmt *statement = self.statement; while (sqlite3_step(statement) == SQLITE_ROW) { - if (!resultsArray) { - resultsArray = [NSMutableArray new]; - } - int columns = sqlite3_column_count(statement); NSMutableDictionary *result = [[NSMutableDictionary alloc] initWithCapacity:columns]; @@ -183,12 +181,8 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error [resultsArray addObject:result]; } - if (resultsArray) { - [self.statementCacheManager setCachedStatement:self.statement forSQLString:self.sqlString]; - [self reset]; - } else { - [self close]; - } + [self.statementCacheManager setCachedStatement:self.statement forSQLString:self.sqlString atDatabase:self.database.databaseName]; + [self reset]; return resultsArray; } From ed559694cad649375c40c2b082831c46e8969dc2 Mon Sep 17 00:00:00 2001 From: jianglongjian <2511067577@qq.com> Date: Sun, 15 Jul 2018 11:08:04 +0800 Subject: [PATCH 16/17] use set to save multi statement; --- CTPersistance.xcodeproj/project.pbxproj | 14 +++ .../CTPersistanceStatementCacheManager.h | 10 +- .../CTPersistanceStatementCacheManager.m | 115 +++++++++--------- .../Statement/CTPersistanceSqlStatement.m | 59 +++++---- .../CTPersistanceStatementCacheItem.h | 21 ++++ .../CTPersistanceStatementCacheItem.m | 53 ++++++++ .../AsyncTestViewController.m | 14 +-- 7 files changed, 193 insertions(+), 93 deletions(-) create mode 100644 CTPersistance/CTPersistance/QueryCommand/StatementCache/CTPersistanceStatementCacheItem.h create mode 100644 CTPersistance/CTPersistance/QueryCommand/StatementCache/CTPersistanceStatementCacheItem.m diff --git a/CTPersistance.xcodeproj/project.pbxproj b/CTPersistance.xcodeproj/project.pbxproj index 3a32984..5bb3143 100644 --- a/CTPersistance.xcodeproj/project.pbxproj +++ b/CTPersistance.xcodeproj/project.pbxproj @@ -82,6 +82,7 @@ 4AF38F2F1F69068700801322 /* CTPersistanceTestTransaction.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AF38F2E1F69068700801322 /* CTPersistanceTestTransaction.m */; }; 6284DE2109EF43B7A3D56F18 /* libPods-CTPersistance.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 24975DE09A62B809BFED181C /* libPods-CTPersistance.a */; }; 7866EEC620F0ABB300819E75 /* CTPersistanceStatementCacheManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 7866EEC520F0ABB300819E75 /* CTPersistanceStatementCacheManager.m */; }; + 786AD97020F88812005B33FB /* CTPersistanceStatementCacheItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 786AD96F20F88812005B33FB /* CTPersistanceStatementCacheItem.m */; }; DAEEF1BE411F617114764D0F /* libPods-CTPersistanceTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D31FADFF56BDD50C7FD9C94 /* libPods-CTPersistanceTests.a */; }; /* End PBXBuildFile section */ @@ -236,6 +237,8 @@ 4D31FADFF56BDD50C7FD9C94 /* libPods-CTPersistanceTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-CTPersistanceTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 7866EEC420F0ABB300819E75 /* CTPersistanceStatementCacheManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CTPersistanceStatementCacheManager.h; sourceTree = ""; }; 7866EEC520F0ABB300819E75 /* CTPersistanceStatementCacheManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTPersistanceStatementCacheManager.m; sourceTree = ""; }; + 786AD96E20F88812005B33FB /* CTPersistanceStatementCacheItem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CTPersistanceStatementCacheItem.h; sourceTree = ""; }; + 786AD96F20F88812005B33FB /* CTPersistanceStatementCacheItem.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTPersistanceStatementCacheItem.m; sourceTree = ""; }; 89D41C6489863CB58D4966E5 /* Pods-CTPersistance.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CTPersistance.debug.xcconfig"; path = "Pods/Target Support Files/Pods-CTPersistance/Pods-CTPersistance.debug.xcconfig"; sourceTree = ""; }; AD102503C09A04454242FAC0 /* Pods-CTPersistanceTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CTPersistanceTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-CTPersistanceTests/Pods-CTPersistanceTests.debug.xcconfig"; sourceTree = ""; }; D9D5304E203B48D737D87E6F /* Pods-CTPersistanceTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CTPersistanceTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-CTPersistanceTests/Pods-CTPersistanceTests.release.xcconfig"; sourceTree = ""; }; @@ -476,6 +479,7 @@ children = ( 7866EEC420F0ABB300819E75 /* CTPersistanceStatementCacheManager.h */, 7866EEC520F0ABB300819E75 /* CTPersistanceStatementCacheManager.m */, + 786AD96D20F88755005B33FB /* StatementCache */, 4A5E3BEE1F403A3200946749 /* Statement */, 4A0E00751F2CBD6300E6A4DA /* Categories */, 4A0E00821F2CBD6300E6A4DA /* CTPersistanceQueryCommand.h */, @@ -1051,6 +1055,15 @@ path = Upsert; sourceTree = ""; }; + 786AD96D20F88755005B33FB /* StatementCache */ = { + isa = PBXGroup; + children = ( + 786AD96E20F88812005B33FB /* CTPersistanceStatementCacheItem.h */, + 786AD96F20F88812005B33FB /* CTPersistanceStatementCacheItem.m */, + ); + path = StatementCache; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -1281,6 +1294,7 @@ 4A0E002D1F2C384E00E6A4DA /* AppDelegate.m in Sources */, 4A0E00A41F2CBD6300E6A4DA /* CTPersistanceVersionRecord.m in Sources */, 4A0238741F3FFBA800EE91C7 /* TestTable.m in Sources */, + 786AD97020F88812005B33FB /* CTPersistanceStatementCacheItem.m in Sources */, 4AC74A631FBE7DC60092B636 /* ViewRecordAndMergeDemoViewController.m in Sources */, 4AC74A731FBE7EC90092B636 /* ItemDetailTable.m in Sources */, 7866EEC620F0ABB300819E75 /* CTPersistanceStatementCacheManager.m in Sources */, diff --git a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.h b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.h index 162649e..06e255d 100644 --- a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.h +++ b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.h @@ -7,18 +7,16 @@ // #import -#import +#import "CTPersistanceStatementCacheItem.h" @interface CTPersistanceStatementCacheManager : NSObject + (instancetype)sharedInstance; - -- (void)setCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName; -- (sqlite3_stmt *)getCachedStatementWithSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName; -- (void)removeCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName; +- (void)setCachedStatement:(CTPersistanceStatementCacheItem *)pStmt forSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName; +- (CTPersistanceStatementCacheItem *)getCachedStatementWithSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName; +- (void)removeCachedStatement:(CTPersistanceStatementCacheItem *)pStmt forSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName; - (void)clearDatabaseStatementCache:(NSString *)databaseName; -- (void)clearAllDatabaseStatementCache; @end diff --git a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m index 6eb8865..19f6c38 100644 --- a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m +++ b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m @@ -8,6 +8,7 @@ #import "CTPersistanceStatementCacheManager.h" + @interface CTPersistanceStatementCacheManager() @property (nonatomic, strong) NSMutableDictionary *cachedTree; @@ -17,55 +18,70 @@ @interface CTPersistanceStatementCacheManager() @implementation CTPersistanceStatementCacheManager -- (sqlite3_stmt *)getCachedStatementWithSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName +- (CTPersistanceStatementCacheItem *)getCachedStatementWithSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName { - if (![self.cachedTree objectForKey:databaseName]) { - return NULL; - } - - NSMutableDictionary *cachedStatements = [self.cachedTree objectForKey:databaseName]; - - if (![cachedStatements objectForKey:sqlString]) { - return NULL; - } + @synchronized (self) { + if (![self.cachedTree objectForKey:databaseName]) { + return NULL; + } + + NSMutableDictionary *cachedStatements = [self.cachedTree objectForKey:databaseName]; + + if (![cachedStatements objectForKey:sqlString]) { + return NULL; + } + + NSMutableSet *statements = [cachedStatements objectForKey:sqlString]; - @synchronized(self) { - sqlite3_stmt *pStmt = [[cachedStatements objectForKey:sqlString] pointerValue]; - return pStmt; + for (CTPersistanceStatementCacheItem *item in statements) { + if (item.inUse == NO) { + return item; + } + } + + return nil; } } -- (void)setCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName +- (void)setCachedStatement:(CTPersistanceStatementCacheItem *)pStmt forSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName { - if (![self.cachedTree objectForKey:databaseName]) { - [self.cachedTree setObject:[NSMutableDictionary dictionary] forKey:databaseName]; - } - - NSMutableDictionary *cachedStatements = [self.cachedTree objectForKey:databaseName]; - - if ([cachedStatements objectForKey:sqlString]) { - return; - } + @synchronized (self) { + if (![self.cachedTree objectForKey:databaseName]) { + [self.cachedTree setObject:[NSMutableDictionary dictionary] forKey:databaseName]; + } + + NSMutableDictionary *cachedStatements = [self.cachedTree objectForKey:databaseName]; + + NSMutableSet *statements = [cachedStatements objectForKey:sqlString]; + if (!statements) { + statements = [NSMutableSet set]; + } + + if (pStmt.inUse == NO) { + [statements addObject:pStmt]; + [cachedStatements setObject:statements forKey:sqlString]; + } - @synchronized(self) { - [cachedStatements setObject:[NSValue valueWithPointer:pStmt] forKey:sqlString]; } + } -- (void)removeCachedStatement:(sqlite3_stmt *)pStmt forSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName +- (void)removeCachedStatement:(CTPersistanceStatementCacheItem *)pStmt forSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName { - if (![self.cachedTree objectForKey:databaseName]) { - return; - } - - NSMutableDictionary *cachedStatements = [self.cachedTree objectForKey:databaseName]; - if (![cachedStatements objectForKey:sqlString]) { - return; - } - - @synchronized(self) { - [cachedStatements removeObjectForKey:sqlString]; + @synchronized (self) { + if (![self.cachedTree objectForKey:databaseName]) { + return; + } + + NSMutableDictionary *cachedStatements = [self.cachedTree objectForKey:databaseName]; + + if (![cachedStatements objectForKey:sqlString]) { + return; + } + + NSMutableSet *statements = [cachedStatements objectForKey:sqlString]; + [statements removeObject:pStmt]; } } @@ -77,24 +93,16 @@ - (void)clearDatabaseStatementCache:(NSString *)databaseName NSMutableDictionary *cachedStatements = [self.cachedTree objectForKey:databaseName]; - @synchronized(self) { - for (id statement in [cachedStatements objectEnumerator]) { - sqlite3_finalize((sqlite3_stmt *)[statement pointerValue]); + for (NSMutableSet *statements in [cachedStatements objectEnumerator]) { + for (CTPersistanceStatementCacheItem *item in statements) { + [item close]; } - - [cachedStatements removeAllObjects]; - [self.cachedTree removeObjectForKey:databaseName]; - } -} - -- (void)clearAllDatabaseStatementCache -{ - for (NSString *databaseName in [self.cachedTree objectEnumerator]) { - [self clearDatabaseStatementCache:databaseName]; } + + [cachedStatements removeAllObjects]; + [self.cachedTree removeObjectForKey:databaseName]; } - #pragma mark - public methods + (instancetype)sharedInstance { @@ -116,9 +124,4 @@ - (instancetype)init return self; } -- (void)dealloc -{ - [self clearAllDatabaseStatementCache]; -} - @end diff --git a/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m b/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m index d786567..5d31bbb 100644 --- a/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m +++ b/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m @@ -14,12 +14,12 @@ @interface CTPersistanceSqlStatement () -@property (nonatomic, unsafe_unretained, readwrite) sqlite3_stmt *statement; - @property (nonatomic, weak) CTPersistanceDataBase *database; @property (nonatomic, copy) NSString *sqlString; + @property (nonatomic, strong) CTPersistanceStatementCacheManager *statementCacheManager; +@property (nonatomic, strong) CTPersistanceStatementCacheItem *statementItem; @end @@ -28,12 +28,19 @@ @implementation CTPersistanceSqlStatement - (instancetype)initWithSqlString:(NSString *)sqlString bindValueList:(NSMutableArray *)bindValueList database:(CTPersistanceDataBase *)database error:(NSError *__autoreleasing *)error { self = [super init]; + if (self) { + self.database = database; self.sqlString = sqlString; - sqlite3_stmt *statement = [self.statementCacheManager getCachedStatementWithSQLString:sqlString atDatabase:self.database.databaseName]; - if (!statement) { + self.statementItem = [self.statementCacheManager getCachedStatementWithSQLString:sqlString atDatabase:self.database.databaseName]; + + sqlite3_stmt *statement = self.statementItem.statement; + + if (!self.statementItem) { + + self.statementItem = [CTPersistanceStatementCacheItem new]; int result = sqlite3_prepare_v2(database.database, [sqlString UTF8String], (int)sqlString.length, &statement, NULL); @@ -51,15 +58,10 @@ - (instancetype)initWithSqlString:(NSString *)sqlString bindValueList:(NSMutable return nil; } - self.statement = statement; - - } else { - - self.statement = statement; - + self.statementItem.statement = statement; + } - [bindValueList enumerateObjectsUsingBlock:^(NSInvocation * _Nonnull bindInvocation, NSUInteger idx, BOOL * _Nonnull stop) { [bindInvocation setArgument:(void *)&statement atIndex:2]; [bindInvocation invoke]; @@ -70,15 +72,17 @@ - (instancetype)initWithSqlString:(NSString *)sqlString bindValueList:(NSMutable return self; } -- (void)close { - [self.statementCacheManager removeCachedStatement:self.statement forSQLString:self.sqlString atDatabase:self.database.databaseName]; +- (void)close +{ + [self.statementCacheManager removeCachedStatement:self.statementItem forSQLString:self.sqlString atDatabase:self.database.databaseName]; - sqlite3_finalize(self.statement); - self.statement = nil; + [self.statementItem close]; + self.statementItem = nil; } -- (void)reset { - sqlite3_reset(self.statement); +- (void)reset +{ + [self.statementItem reset]; } - (BOOL)executeWithError:(NSError *__autoreleasing *)error @@ -88,13 +92,15 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error return NO; } - sqlite3_stmt *statement = self.statement; + self.statementItem.inUse = YES; + + sqlite3_stmt *statement = self.statementItem.statement; int result = sqlite3_step(statement); if (result != SQLITE_DONE && error) { - [self.statementCacheManager removeCachedStatement:self.statement forSQLString:self.sqlString atDatabase:self.database.databaseName]; + [self.statementCacheManager removeCachedStatement:self.statementItem forSQLString:self.sqlString atDatabase:self.database.databaseName]; const char *errorMsg = sqlite3_errmsg(self.database.database); NSError *generatedError = [NSError errorWithDomain:kCTPersistanceErrorDomain code:CTPersistanceErrorCodeQueryStringError userInfo:@{NSLocalizedDescriptionKey:[NSString stringWithFormat:@"\n======================\nQuery Error: \n Origin Query is : %@\n Error Message is: %@\n======================\n", [NSString stringWithUTF8String:sqlite3_sql(statement)], [NSString stringWithCString:errorMsg encoding:NSUTF8StringEncoding]]}]; @@ -104,9 +110,10 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error return NO; } - [self.statementCacheManager setCachedStatement:self.statement forSQLString:self.sqlString atDatabase:self.database.databaseName]; [self reset]; + [self.statementCacheManager setCachedStatement:self.statementItem forSQLString:self.sqlString atDatabase:self.database.databaseName]; + return YES; } @@ -118,9 +125,12 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error return nil; } + self.statementItem.inUse = YES; + + NSMutableArray *resultsArray = [NSMutableArray array]; - sqlite3_stmt *statement = self.statement; + sqlite3_stmt *statement = self.statementItem.statement; while (sqlite3_step(statement) == SQLITE_ROW) { @@ -181,14 +191,17 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error [resultsArray addObject:result]; } - [self.statementCacheManager setCachedStatement:self.statement forSQLString:self.sqlString atDatabase:self.database.databaseName]; [self reset]; + + [self.statementCacheManager setCachedStatement:self.statementItem forSQLString:self.sqlString atDatabase:self.database.databaseName]; + return resultsArray; } #pragma mark - getter and setter -- (CTPersistanceStatementCacheManager *)statementCacheManager { +- (CTPersistanceStatementCacheManager *)statementCacheManager +{ if (_statementCacheManager == nil) { _statementCacheManager = [CTPersistanceStatementCacheManager sharedInstance]; } diff --git a/CTPersistance/CTPersistance/QueryCommand/StatementCache/CTPersistanceStatementCacheItem.h b/CTPersistance/CTPersistance/QueryCommand/StatementCache/CTPersistanceStatementCacheItem.h new file mode 100644 index 0000000..88dfcac --- /dev/null +++ b/CTPersistance/CTPersistance/QueryCommand/StatementCache/CTPersistanceStatementCacheItem.h @@ -0,0 +1,21 @@ +// +// CTPersistanceStatementCacheItem.h +// CTPersistance +// +// Created by longjianjiang on 2018/7/13. +// Copyright © 2018年 casa. All rights reserved. +// + +#import +#import + +@interface CTPersistanceStatementCacheItem : NSObject + +@property (atomic, assign) BOOL inUse; +@property (nonatomic, unsafe_unretained) sqlite3_stmt *statement; + +- (void)reset; +- (void)close; + +@end + diff --git a/CTPersistance/CTPersistance/QueryCommand/StatementCache/CTPersistanceStatementCacheItem.m b/CTPersistance/CTPersistance/QueryCommand/StatementCache/CTPersistanceStatementCacheItem.m new file mode 100644 index 0000000..13ec268 --- /dev/null +++ b/CTPersistance/CTPersistance/QueryCommand/StatementCache/CTPersistanceStatementCacheItem.m @@ -0,0 +1,53 @@ +// +// CTPersistanceStatementCacheItem.m +// CTPersistance +// +// Created by longjianjiang on 2018/7/13. +// Copyright © 2018年 casa. All rights reserved. +// + +#import "CTPersistanceStatementCacheItem.h" +#import + +@interface CTPersistanceStatementCacheItem () { + volatile uint32_t _inUse; +} + +@end + +@implementation CTPersistanceStatementCacheItem +- (void)close +{ + if (_statement) { + sqlite3_finalize(_statement); + _statement = 0x00; + } + + self.inUse = NO; +} + +- (void)reset +{ + if (_statement) { + sqlite3_reset(_statement); + } + + self.inUse = NO; +} + +#pragma mark getter and setter +- (BOOL)inUse +{ + return _inUse != 0; +} + +- (void)setInUse:(BOOL)allowed +{ + if (allowed) { + OSAtomicOr32Barrier(1, &_inUse); + } else { + OSAtomicAnd32Barrier(0, &_inUse); + } +} + +@end diff --git a/CTPersistance/Demo/AsyncTestViewController/AsyncTestViewController.m b/CTPersistance/Demo/AsyncTestViewController/AsyncTestViewController.m index 07b574f..74bdde2 100644 --- a/CTPersistance/Demo/AsyncTestViewController/AsyncTestViewController.m +++ b/CTPersistance/Demo/AsyncTestViewController/AsyncTestViewController.m @@ -34,8 +34,6 @@ - (void)viewDidLoad record.name = @"casa"; [self.testTable insertRecord:record error:NULL]; } - - NSLog(@"%@", self.testTable.queryCommand.database.databaseFilePath); } - (void)viewWillAppear:(BOOL)animated @@ -46,7 +44,7 @@ - (void)viewWillAppear:(BOOL)animated NSInteger count = COUNT; while (count --> 0) { TestRecord *record = (TestRecord *)[self.testTable findWithPrimaryKey:@(count) error:NULL]; - NSLog(@"%@", record.primaryKey); + NSLog(@"read one %@", record.primaryKey); } }]; @@ -54,7 +52,7 @@ - (void)viewWillAppear:(BOOL)animated while (count --> 0) { [[CTPersistanceAsyncExecutor sharedInstance] read:^{ TestRecord *record = (TestRecord *)[self.testTable findWithPrimaryKey:@(count) error:NULL]; - NSLog(@"%@", record.primaryKey); + NSLog(@"read two %@", record.primaryKey); }]; } @@ -62,7 +60,7 @@ - (void)viewWillAppear:(BOOL)animated NSInteger count = COUNT; while (count --> 0) { NSNumber *primaryKey = [self.testTable insertValue:@"casa" forKey:@"name" error:NULL]; - NSLog(@"%@", primaryKey); + NSLog(@"write one %@", primaryKey); } }]; @@ -70,7 +68,7 @@ - (void)viewWillAppear:(BOOL)animated while (count --> 0) { [[CTPersistanceAsyncExecutor sharedInstance] read:^{ TestRecord *record = (TestRecord *)[self.testTable findWithPrimaryKey:@(count) error:NULL]; - NSLog(@"%@", record.primaryKey); + NSLog(@"read three %@", record.primaryKey); }]; } @@ -78,7 +76,7 @@ - (void)viewWillAppear:(BOOL)animated while (count --> 0) { [[CTPersistanceAsyncExecutor sharedInstance] write:^{ NSNumber *primaryKey = [self.testTable insertValue:@"casa" forKey:@"name" error:NULL]; - NSLog(@"%@", primaryKey); + NSLog(@"write two %@", primaryKey); }]; } @@ -86,7 +84,7 @@ - (void)viewWillAppear:(BOOL)animated while (count --> 0) { [[CTPersistanceAsyncExecutor sharedInstance] read:^{ TestRecord *record = (TestRecord *)[self.testTable findWithPrimaryKey:@(count) error:NULL]; - NSLog(@"%@", record.primaryKey); + NSLog(@"read four %@", record.primaryKey); }]; } } From 1dbc5ae94db4e6668f7bd58e41fb5a99eec19e2b Mon Sep 17 00:00:00 2001 From: longjianjiang Date: Mon, 12 Nov 2018 14:15:09 +0800 Subject: [PATCH 17/17] add lock on statement cache operation; --- .../CTPersistanceStatementCacheManager.m | 108 +++++++++--------- .../Statement/CTPersistanceSqlStatement.m | 21 +--- .../CTPersistanceStatementCacheItem.m | 15 +++ 3 files changed, 76 insertions(+), 68 deletions(-) diff --git a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m index 19f6c38..d03d310 100644 --- a/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m +++ b/CTPersistance/CTPersistance/QueryCommand/CTPersistanceStatementCacheManager.m @@ -8,6 +8,8 @@ #import "CTPersistanceStatementCacheManager.h" +#define Lock() dispatch_semaphore_wait(self->_lock, DISPATCH_TIME_FOREVER) +#define Unlock() dispatch_semaphore_signal(self->_lock) @interface CTPersistanceStatementCacheManager() @@ -16,83 +18,83 @@ @interface CTPersistanceStatementCacheManager() @end -@implementation CTPersistanceStatementCacheManager +@implementation CTPersistanceStatementCacheManager { + dispatch_semaphore_t _lock; +} + +#pragma mark - getter +- (NSMutableDictionary *)_getCachedStatements:(NSString *)databaseName { + Lock(); + NSMutableDictionary *cachedStatements = [self.cachedTree objectForKey:databaseName]; + if (!cachedStatements) { + [self.cachedTree setObject:[NSMutableDictionary dictionary] forKey:databaseName]; + cachedStatements = [self.cachedTree objectForKey:databaseName]; + } + Unlock(); + return cachedStatements; +} + +- (NSMutableSet *)_getStatementSet:(NSString *)databaseName sqlString:(NSString *)sqlString { + NSMutableDictionary *cachedStatements = [self _getCachedStatements:databaseName]; + Lock(); + NSMutableSet *statements = [cachedStatements objectForKey:sqlString]; + if (!statements) { + [cachedStatements setObject:[NSMutableSet set] forKey:sqlString]; + statements = [cachedStatements objectForKey:sqlString]; + } + Unlock(); + return statements; +} - (CTPersistanceStatementCacheItem *)getCachedStatementWithSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName { - @synchronized (self) { - if (![self.cachedTree objectForKey:databaseName]) { - return NULL; - } - - NSMutableDictionary *cachedStatements = [self.cachedTree objectForKey:databaseName]; - - if (![cachedStatements objectForKey:sqlString]) { - return NULL; - } - - NSMutableSet *statements = [cachedStatements objectForKey:sqlString]; + NSMutableDictionary *cachedStatements = [self _getCachedStatements:databaseName]; + if (!cachedStatements) { + return nil; + } else { + NSMutableSet *statements = [self _getStatementSet:databaseName sqlString:sqlString]; + CTPersistanceStatementCacheItem *scItem = nil; + + Lock(); for (CTPersistanceStatementCacheItem *item in statements) { if (item.inUse == NO) { - return item; + scItem = item; + [statements removeObject:item]; + break; } } + Unlock(); - return nil; + return scItem; } } - (void)setCachedStatement:(CTPersistanceStatementCacheItem *)pStmt forSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName { - @synchronized (self) { - if (![self.cachedTree objectForKey:databaseName]) { - [self.cachedTree setObject:[NSMutableDictionary dictionary] forKey:databaseName]; - } - - NSMutableDictionary *cachedStatements = [self.cachedTree objectForKey:databaseName]; - - NSMutableSet *statements = [cachedStatements objectForKey:sqlString]; - if (!statements) { - statements = [NSMutableSet set]; - } - - if (pStmt.inUse == NO) { - [statements addObject:pStmt]; - [cachedStatements setObject:statements forKey:sqlString]; - } + NSMutableSet *statements = [self _getStatementSet:databaseName sqlString:sqlString]; - } - + Lock(); + [pStmt reset]; + [statements addObject:pStmt]; + Unlock(); } - (void)removeCachedStatement:(CTPersistanceStatementCacheItem *)pStmt forSQLString:(NSString *)sqlString atDatabase:(NSString *)databaseName { + NSMutableSet *statements = [self _getStatementSet:databaseName sqlString:sqlString]; - @synchronized (self) { - if (![self.cachedTree objectForKey:databaseName]) { - return; - } - - NSMutableDictionary *cachedStatements = [self.cachedTree objectForKey:databaseName]; - - if (![cachedStatements objectForKey:sqlString]) { - return; - } - - NSMutableSet *statements = [cachedStatements objectForKey:sqlString]; - [statements removeObject:pStmt]; - } + Lock(); + [pStmt close]; + [statements removeObject:pStmt]; + Unlock(); } - (void)clearDatabaseStatementCache:(NSString *)databaseName { - if (![self.cachedTree objectForKey:databaseName]) { - return; - } - - NSMutableDictionary *cachedStatements = [self.cachedTree objectForKey:databaseName]; + NSMutableDictionary *cachedStatements = [self _getCachedStatements:databaseName]; + Lock(); for (NSMutableSet *statements in [cachedStatements objectEnumerator]) { for (CTPersistanceStatementCacheItem *item in statements) { [item close]; @@ -101,6 +103,7 @@ - (void)clearDatabaseStatementCache:(NSString *)databaseName [cachedStatements removeAllObjects]; [self.cachedTree removeObjectForKey:databaseName]; + Unlock(); } #pragma mark - public methods @@ -119,6 +122,7 @@ - (instancetype)init { self = [super init]; if (self) { + _lock = dispatch_semaphore_create(1); self.cachedTree = [NSMutableDictionary dictionary]; } return self; diff --git a/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m b/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m index 5d31bbb..5b1c8ae 100644 --- a/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m +++ b/CTPersistance/CTPersistance/QueryCommand/Statement/CTPersistanceSqlStatement.m @@ -75,16 +75,9 @@ - (instancetype)initWithSqlString:(NSString *)sqlString bindValueList:(NSMutable - (void)close { [self.statementCacheManager removeCachedStatement:self.statementItem forSQLString:self.sqlString atDatabase:self.database.databaseName]; - - [self.statementItem close]; self.statementItem = nil; } -- (void)reset -{ - [self.statementItem reset]; -} - - (BOOL)executeWithError:(NSError *__autoreleasing *)error { if (error != NULL && *error != nil) { @@ -100,21 +93,19 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error if (result != SQLITE_DONE && error) { - [self.statementCacheManager removeCachedStatement:self.statementItem forSQLString:self.sqlString atDatabase:self.database.databaseName]; + [self close]; const char *errorMsg = sqlite3_errmsg(self.database.database); NSError *generatedError = [NSError errorWithDomain:kCTPersistanceErrorDomain code:CTPersistanceErrorCodeQueryStringError userInfo:@{NSLocalizedDescriptionKey:[NSString stringWithFormat:@"\n======================\nQuery Error: \n Origin Query is : %@\n Error Message is: %@\n======================\n", [NSString stringWithUTF8String:sqlite3_sql(statement)], [NSString stringWithCString:errorMsg encoding:NSUTF8StringEncoding]]}]; *error = generatedError; - sqlite3_finalize(statement); + return NO; } - [self reset]; + [self.statementCacheManager setCachedStatement:self.statementItem forSQLString:self.sqlString atDatabase:self.database.databaseName]; - [self.statementCacheManager setCachedStatement:self.statementItem forSQLString:self.sqlString atDatabase:self.database.databaseName]; - - return YES; + return YES; } @@ -191,9 +182,7 @@ - (BOOL)executeWithError:(NSError *__autoreleasing *)error [resultsArray addObject:result]; } - [self reset]; - - [self.statementCacheManager setCachedStatement:self.statementItem forSQLString:self.sqlString atDatabase:self.database.databaseName]; + [self.statementCacheManager setCachedStatement:self.statementItem forSQLString:self.sqlString atDatabase:self.database.databaseName]; return resultsArray; diff --git a/CTPersistance/CTPersistance/QueryCommand/StatementCache/CTPersistanceStatementCacheItem.m b/CTPersistance/CTPersistance/QueryCommand/StatementCache/CTPersistanceStatementCacheItem.m index 13ec268..349738e 100644 --- a/CTPersistance/CTPersistance/QueryCommand/StatementCache/CTPersistanceStatementCacheItem.m +++ b/CTPersistance/CTPersistance/QueryCommand/StatementCache/CTPersistanceStatementCacheItem.m @@ -8,9 +8,11 @@ #import "CTPersistanceStatementCacheItem.h" #import +#import @interface CTPersistanceStatementCacheItem () { volatile uint32_t _inUse; + pthread_mutex_t _lock; } @end @@ -19,8 +21,10 @@ @implementation CTPersistanceStatementCacheItem - (void)close { if (_statement) { + pthread_mutex_lock(&_lock); sqlite3_finalize(_statement); _statement = 0x00; + pthread_mutex_unlock(&_lock); } self.inUse = NO; @@ -29,12 +33,23 @@ - (void)close - (void)reset { if (_statement) { + pthread_mutex_lock(&_lock); sqlite3_reset(_statement); + pthread_mutex_unlock(&_lock); } self.inUse = NO; } +#pragma mark - life cycle +- (instancetype)init { + self = [super init]; + if (self) { + pthread_mutex_init(&_lock, NULL); + } + return self; +} + #pragma mark getter and setter - (BOOL)inUse {