Skip to content

Commit 100a4b0

Browse files
committed
Squashed commit of the following:
commit e51e69f Author: Marco Arment <[email protected]> Date: Sat Apr 2 15:15:49 2016 -0400 Custom exception handling commit 62f80d5 Author: Marco Arment <[email protected]> Date: Thu Feb 25 14:51:05 2016 -0500 Fix queries in initial schema-builder callback commit aeac007 Author: Marco Arment <[email protected]> Date: Mon Feb 22 12:02:42 2016 -0500 Add VACUUM support commit 5fc3cf9 Merge: 2b0b7a3 f02684e Author: marcoarment <[email protected]> Date: Wed Dec 16 11:28:20 2015 -0500 Merge pull request #134 from kirb/fix/swift-support Fix issues that prevent FCModel from working with Swift commit f02684e Author: Adam Demasi <[email protected]> Date: Wed Dec 16 13:28:15 2015 +1030 Revert "I missed self.class in -primaryKey when rearranging for prefixes." Whoops, the keys are actually class names, not strings. Keep them as-is. This reverts commit 3593fbf. commit b2a4345 Author: Adam Demasi <[email protected]> Date: Wed Dec 16 13:17:30 2015 +1030 Explain the module name argument in the header. commit 3593fbf Author: Adam Demasi <[email protected]> Date: Wed Dec 16 13:16:23 2015 +1030 I missed self.class in -primaryKey when rearranging for prefixes. commit f3b319b Author: Adam Demasi <[email protected]> Date: Wed Dec 16 12:52:14 2015 +1030 Allow a module prefix to be passed so Swift classes can be used. In Objective-C land, Swift classes are called Module.Class. SQLite doesn’t like periods in the table name, so take that out so the table is just called the same as the class name. Class name on its own looks cleaner, anyway. commit 469e8c4 Author: Adam Demasi <[email protected]> Date: Wed Dec 16 12:01:50 2015 +1030 Add instance initWithPrimaryKey:… methods. commit 63b5528 Author: Adam Demasi <[email protected]> Date: Wed Dec 16 11:59:45 2015 +1030 Add missing sqlite3.h import. commit 2b0b7a3 Author: Marco Arment <[email protected]> Date: Mon Oct 12 21:12:31 2015 -0400 Fixed unique-map omission for new instances commit 8dcd4a5 Author: Marco Arment <[email protected]> Date: Fri Jul 24 22:57:05 2015 -0400 Re-added allLoadedInstances now that unique's back commit 4b655c1 Author: Marco Arment <[email protected]> Date: Fri Jul 10 23:11:10 2015 -0400 Flattening queue hierarchy down to the main thread commit 4c8130c Author: Marco Arment <[email protected]> Date: Thu Jul 9 18:37:20 2015 -0400 Testing out unique instances within the new stuff commit fcbd0a2 Author: Marco Arment <[email protected]> Date: Fri Jan 30 17:05:00 2015 -0500 Fixed analyzer warning commit 215224f Author: Marco Arment <[email protected]> Date: Fri Jan 30 16:47:04 2015 -0500 Added new atomic-write method commit 28101f8 Author: Marco Arment <[email protected]> Date: Thu Jan 22 23:01:49 2015 -0500 Safer mutable Foundation structures in concurrent-accessible contexts commit af42dc8 Author: Marco Arment <[email protected]> Date: Mon Jan 19 15:41:39 2015 -0500 Fixed compiler warnings when used outside of Cocoapods commit 67c43d9 Author: Marco Arment <[email protected]> Date: Mon Jan 19 14:52:08 2015 -0500 Changed finicky NSCache to a better model for iOS memory pressure commit 7ef6f0e Author: Marco Arment <[email protected]> Date: Fri Jan 16 21:34:48 2015 -0500 New notification conveniences commit 9c5c410 Author: Marco Arment <[email protected]> Date: Thu Dec 11 15:01:43 2014 -0500 Fixing reads before file descriptor is open commit 9173939 Author: Marco Arment <[email protected]> Date: Mon Dec 8 23:46:50 2014 -0500 Removed custom serialization commit 9cc5e78 Author: Marco Arment <[email protected]> Date: Mon Dec 8 22:55:46 2014 -0500 Removed externally accessible va_list variants commit 604ab54 Merge: 98655b9 9a1b05c Author: Marco Arment <[email protected]> Date: Mon Dec 8 22:52:55 2014 -0500 Merge branch 'non-unique-instances' of github.com:marcoarment/FCModel into non-unique-instances commit 98655b9 Author: Marco Arment <[email protected]> Date: Mon Dec 8 22:52:49 2014 -0500 NSCopying, fixed hash/isEqual, basic cache-by-PK commit 9a1b05c Merge: 7e9b40c a84727b Author: marcoarment <[email protected]> Date: Mon Dec 8 20:44:36 2014 -0500 Merge pull request #108 from jlnr/fix-ternary-typo Fix a small ternary operator typo commit a84727b Author: Julian Raschke <[email protected]> Date: Tue Dec 9 02:40:19 2014 +0100 Fix a small ternary operator typo commit 7e9b40c Author: Marco Arment <[email protected]> Date: Mon Dec 8 09:47:33 2014 -0500 Parameter name cleanup commit 7b37ea9 Author: Marco Arment <[email protected]> Date: Mon Dec 8 09:42:55 2014 -0500 Added va_list-accepting variants for Swift binding commit fc5b468 Author: Marco Arment <[email protected]> Date: Sun Dec 7 22:10:17 2014 -0500 Restored va_args, removed keyed/resultSet selects commit a729857 Author: Marco Arment <[email protected]> Date: Sun Dec 7 22:07:36 2014 -0500 Updated FMDB copy in test app commit dbc9fe1 Author: Marco Arment <[email protected]> Date: Sun Dec 7 00:27:54 2014 -0500 Cherry-picked best of master commits And removed all variadic functions for Swift friendliness and code reduction commit cc11577 Author: Marco Arment <[email protected]> Date: Sat Dec 6 23:18:08 2014 -0500 Cherry-picked ignored fields from master commit 237a0d4 Author: Marco Arment <[email protected]> Date: Sat Sep 13 14:25:04 2014 -0400 Removed unnecessary, bad-idea method that you probably weren't using anyway commit e0f9b3d Author: Marco Arment <[email protected]> Date: Mon Sep 8 00:24:01 2014 -0400 Crazy non-uniquing branch (experimental)
1 parent 9ca7e6c commit 100a4b0

27 files changed

+2105
-1846
lines changed

FCModel/FCModel.h

Lines changed: 74 additions & 140 deletions
Large diffs are not rendered by default.

FCModel/FCModel.m

Lines changed: 505 additions & 901 deletions
Large diffs are not rendered by default.

FCModel/FCModelCachedObject.m

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,13 @@
88
#import "FCModelCachedObject.h"
99
#import "FCModel.h"
1010

11+
#if TARGET_OS_IPHONE
12+
#import <UIKit/UIKit.h>
13+
#endif
14+
1115
// FCModelCachedObject has its own notification that runs BEFORE the other FCModel change notifications
1216
// so it can remove stale data before any application actions fetch new data in response to the change.
13-
extern NSString * const FCModelWillSendAnyChangeNotification;
17+
extern NSString * const FCModelWillSendChangeNotification;
1418

1519
#pragma mark - Global cache
1620

@@ -123,8 +127,7 @@ + (instancetype)objectWithModelClass:(Class)fcModelClass cacheIdentifier:(id)ide
123127
obj.generator = generatorBlock;
124128
obj.ignoredFieldsForInvalidation = ignoredFields;
125129

126-
[NSNotificationCenter.defaultCenter addObserver:obj selector:@selector(dataSourceChanged:) name:FCModelWillReloadNotification object:fcModelClass];
127-
[NSNotificationCenter.defaultCenter addObserver:obj selector:@selector(dataSourceChanged:) name:FCModelWillSendAnyChangeNotification object:fcModelClass];
130+
[NSNotificationCenter.defaultCenter addObserver:obj selector:@selector(dataSourceChanged:) name:FCModelWillSendChangeNotification object:fcModelClass];
128131

129132
#if TARGET_OS_IPHONE
130133
[NSNotificationCenter.defaultCenter addObserver:obj selector:@selector(flush:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];

FCModel/FCModelDatabase.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//
2+
// FCModelDatabase.h
3+
//
4+
// Created by Marco Arment on 3/12/14.
5+
// Copyright (c) 2014 Marco Arment. See included LICENSE file.
6+
//
7+
8+
#import <Foundation/Foundation.h>
9+
10+
#ifdef COCOAPODS
11+
#import <FMDB/FMDatabase.h>
12+
#else
13+
#import "FMDatabase.h"
14+
#endif
15+
16+
@interface FCModelDatabase : NSObject
17+
18+
- (instancetype)initWithDatabasePath:(NSString *)filename;
19+
- (void)startMonitoringForExternalChanges;
20+
- (void)close;
21+
- (void)inDatabase:(void (^)(FMDatabase *db))block;
22+
23+
@property (nonatomic, readonly) FMDatabase *database;
24+
@property (nonatomic, readonly) NSMutableDictionary *enqueuedChangedFieldsByClass;
25+
@property (nonatomic) BOOL isQueuingNotifications;
26+
@property (nonatomic) BOOL isInInternalWrite;
27+
28+
@end

FCModel/FCModelDatabase.m

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
//
2+
// FCModelDatabase.m
3+
//
4+
// Created by Marco Arment on 3/12/14.
5+
// Copyright (c) 2014 Marco Arment. See included LICENSE file.
6+
//
7+
8+
#import "FCModelDatabase.h"
9+
#import "FCModel.h"
10+
#import <sqlite3.h>
11+
12+
@interface FCModel ()
13+
+ (void)postChangeNotificationWithChangedFields:(NSSet *)changedFields;
14+
+ (void)dataChangedExternally;
15+
@end
16+
17+
@interface FCModelDatabase ()
18+
- (uint32_t)sqliteChangeCount;
19+
- (BOOL)sqliteChangeTrackingIsActive;
20+
@property (nonatomic) int32_t expectedChangeCount;
21+
@end
22+
23+
#define kSQLiteFileChangeCounterOffset 24
24+
25+
static void _sqlite3_update_hook(void *context, int sqlite_operation, char const *db_name, char const *table_name, sqlite3_int64 rowid)
26+
{
27+
Class class = NSClassFromString([NSString stringWithCString:table_name encoding:NSUTF8StringEncoding]);
28+
if (! class || ! [class isSubclassOfClass:FCModel.class]) return;
29+
30+
FCModelDatabase *queue = (__bridge FCModelDatabase *) context;
31+
if (! queue.sqliteChangeTrackingIsActive) return;
32+
queue.expectedChangeCount = [queue sqliteChangeCount] + 1;
33+
if (queue.isInInternalWrite) return;
34+
35+
// Can't run synchronously since SQLite requires that no other database queries are executed before this function returns,
36+
// and queries are likely to be executed by any notification listeners.
37+
if (queue.isQueuingNotifications) [class postChangeNotificationWithChangedFields:nil];
38+
else dispatch_async(dispatch_get_main_queue(), ^{ [class postChangeNotificationWithChangedFields:nil]; });
39+
}
40+
41+
@interface FCModelDatabase () {
42+
int changeCounterReadFileDescriptor;
43+
int dispatchEventFileDescriptor;
44+
dispatch_source_t dispatchFileWriteSource;
45+
}
46+
@property (nonatomic) FMDatabase *openDatabase;
47+
@property (nonatomic) NSString *path;
48+
@property (nonatomic) NSMutableDictionary *enqueuedChangedFieldsByClass;
49+
@property (nonatomic) BOOL inExpectedWrite;
50+
@end
51+
52+
@implementation FCModelDatabase
53+
54+
- (instancetype)initWithDatabasePath:(NSString *)path
55+
{
56+
if ( (self = [super init]) ) {
57+
self.path = path;
58+
self.enqueuedChangedFieldsByClass = [NSMutableDictionary dictionary];
59+
}
60+
return self;
61+
}
62+
63+
- (FMDatabase *)database
64+
{
65+
if (! _openDatabase) fcm_onMainThread(^{
66+
self.openDatabase = [[FMDatabase alloc] initWithPath:_path];
67+
if (! [_openDatabase open]) {
68+
[[NSException exceptionWithName:NSGenericException reason:[NSString stringWithFormat:@"Cannot open or create database at path: %@", self.path] userInfo:nil] raise];
69+
}
70+
71+
sqlite3_update_hook(_openDatabase.sqliteHandle, &_sqlite3_update_hook, (__bridge void *) self);
72+
});
73+
return _openDatabase;
74+
}
75+
76+
- (BOOL)sqliteChangeTrackingIsActive { return changeCounterReadFileDescriptor > 0; }
77+
78+
- (uint32_t)sqliteChangeCount
79+
{
80+
if (! changeCounterReadFileDescriptor) return 0;
81+
82+
uint32_t changeCounter = 0;
83+
lseek(changeCounterReadFileDescriptor, kSQLiteFileChangeCounterOffset, SEEK_SET);
84+
read(changeCounterReadFileDescriptor, &changeCounter, sizeof(uint32_t));
85+
return CFSwapInt32BigToHost(changeCounter);
86+
}
87+
88+
- (void)startMonitoringForExternalChanges
89+
{
90+
if (! self.openDatabase) [[NSException exceptionWithName:NSGenericException reason:@"Database must be open" userInfo:nil] raise];
91+
92+
const char *fsp = _path.fileSystemRepresentation;
93+
changeCounterReadFileDescriptor = open(fsp, O_RDONLY);
94+
dispatchEventFileDescriptor = open(fsp, O_EVTONLY);
95+
dispatchFileWriteSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, dispatchEventFileDescriptor, DISPATCH_VNODE_WRITE, dispatch_get_main_queue());
96+
97+
int rfdCopy = changeCounterReadFileDescriptor;
98+
int efdCopy = dispatchEventFileDescriptor;
99+
dispatch_source_set_cancel_handler(dispatchFileWriteSource, ^{
100+
close(rfdCopy);
101+
close(efdCopy);
102+
});
103+
104+
__weak typeof(self) weakSelf = self;
105+
dispatch_source_set_event_handler(dispatchFileWriteSource, ^{
106+
__strong typeof(self) strongSelf = weakSelf;
107+
if (strongSelf && strongSelf.expectedChangeCount != strongSelf.sqliteChangeCount) [FCModel dataChangedExternally];
108+
});
109+
110+
dispatch_resume(dispatchFileWriteSource);
111+
}
112+
113+
- (void)close
114+
{
115+
dispatchEventFileDescriptor = 0;
116+
changeCounterReadFileDescriptor = 0;
117+
dispatch_source_cancel(dispatchFileWriteSource);
118+
dispatchFileWriteSource = NULL;
119+
120+
[self.openDatabase close];
121+
self.openDatabase = nil;
122+
}
123+
124+
- (void)dealloc
125+
{
126+
[_openDatabase close];
127+
self.openDatabase = nil;
128+
}
129+
130+
- (void)inDatabase:(void (^)(FMDatabase *db))block
131+
{
132+
NSParameterAssert(NSThread.isMainThread);
133+
134+
FMDatabase *db = self.database;
135+
BOOL hadOpenResultSetsBefore = db.hasOpenResultSets;
136+
uint32_t changeCounterBeforeBlock = [self sqliteChangeCount];
137+
138+
block(db);
139+
140+
if (changeCounterReadFileDescriptor) {
141+
// if more than 1 change during this expected write, either there's 2 queries in it (unexpected) or another process changed it
142+
uint32_t changeCounterAfterBlock = [self sqliteChangeCount];
143+
if (changeCounterAfterBlock - changeCounterBeforeBlock > 1) [FCModel dataChangedExternally];
144+
}
145+
146+
if (db.hasOpenResultSets != hadOpenResultSetsBefore) {
147+
[[NSException exceptionWithName:NSGenericException reason:@"FCModelDatabase has an open FMResultSet after inDatabase:" userInfo:nil] raise];
148+
}
149+
}
150+
151+
@end

FCModel/FCModelDatabaseQueue.h

Lines changed: 0 additions & 32 deletions
This file was deleted.

FCModel/FCModelDatabaseQueue.m

Lines changed: 0 additions & 144 deletions
This file was deleted.

0 commit comments

Comments
 (0)