0%

Core-Data

包含组件

最底层File System -> SQLite -> NSPersistent Store(可有多个) -> NSPersistent StoreCoordinator -> NSManagedObjectContext(可有多个,每个可包含多个NSManagedObject)

设置堆栈

范例:https://github.com/objcio/issue-4-full-core-data-application

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
- (void)setupManagedObjectContext

{

//使用initWithConcurrencyType:来明确使用的是基于队列的并发模型

self.managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];

self.managedObjectContext.persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel];

NSError* error;

[self.managedObjectContext.persistentStoreCoordinator

addPersistentStoreWithType:NSSQLiteStoreType

configuration:nil

URL:self.storeURL

options:nil

error:&error];

if (error) {

NSLog(@"error: %@", error);

}

self.managedObjectContext.undoManager = [[NSUndoManager alloc] init];

}

创建模型

在xcode新建的Core Data选项中选择Data Model template,模型文件会被编译成.momd文件。模型创建完毕就可以创建与之对应的NSManagedObject子类。从菜单选择Editor > NSManagedObject subclass。

模型的属性

  • 默认/可选:建议不使用带默认值的可选属性
  • Transient:方便撤销操作和故障处理,建议使用transient属性
  • 索引:提高读取速度
  • 标量类型:默认NSNumber,也可以使用int64_t,float_t或BOOL。

创建Store类

存储类除了managed object context还有rootItem方法,程序启动时会查找这root item然后传给root view controller。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
- (Item*)rootItem

{

NSFetchRequest* request = [NSFetchRequest fetchRequestWithEntityName:@"Item"];

request.predicate = [NSPredicate predicateWithFormat:@"parent = %@", nil];

NSArray* objects = [self.managedObjectContext executeFetchRequest:request error:NULL];

Item* rootItem = [objects lastObject];

if (rootItem == nil) {

rootItem = [Item insertItemWithTitle:nil

parent:nil

inManagedObjectContext:self.managedObjectContext];

}

return rootItem;

}

//增加一个item

+ (instancetype)insertItemWithTitle:(NSString*)title

parent:(Item*)parent

inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext

{

NSUInteger order = parent.numberOfChildren;

Item* item = [NSEntityDescription insertNewObjectForEntityForName:self.entityName

inManagedObjectContext:managedObjectContext];

item.title = title;

item.parent = parent;

item.order = @(order);

return item;

}

//获得子节点数量

- (NSUInteger)numberOfChildren

{

return self.children.count;

}

//创建一个fetched results controller的方法方便自动更新table view

- (NSFetchedResultsController*)childrenFetchedResultsController

{

NSFetchRequest* request = [NSFetchRequest fetchRequestWithEntityName:[self.class entityName]];

request.predicate = [NSPredicate predicateWithFormat:@"parent = %@", self];

request.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"order" ascending:YES]];

return [[NSFetchedResultsController alloc] initWithFetchRequest:request

managedObjectContext:self.managedObjectContext

sectionNameKeyPath:nil

cacheName:nil];

}

和Table View无缝结合

创建一个NSFetchedResultsController作为table view的data source

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

- (id)initWithTableView:(UITableView*)tableView

{

self = [super init];

if (self) {

self.tableView = tableView;

self.tableView.dataSource = self;

}

return self;

}

- (void)setFetchedResultsController:(NSFetchedResultsController*)fetchedResultsController

{

_fetchedResultsController = fetchedResultsController;

fetchedResultsController.delegate = self;

[fetchedResultsController performFetch:NULL];

}

- (NSInteger)numberOfSectionsInTableView:(UITableView*)tableView

{

return self.fetchedResultsController.sections.count;

}

- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)sectionIndex

{

id<NSFetchedResultsSectionInfo> section = self.fetchedResultsController.sections[sectionIndex];

return section.numberOfObjects;

}

- (UITableViewCell*)tableView:(UITableView*)tableView

cellForRowAtIndexPath:(NSIndexPath*)indexPath

{

id object = [self.fetchedResultsController objectAtIndexPath:indexPath];

id cell = [tableView dequeueReusableCellWithIdentifier:self.reuseIdentifier

forIndexPath:indexPath];

[self.delegate configureCell:cell withObject:object];

return cell;

}

创建Table View Controller

在新建的Table view的viewDidLoad里写:

1
2
3
4
5
6
7
8
fetchedResultsControllerDataSource = [[FetchedResultsControllerDataSource alloc] initWithTableView:self.tableView];

self.fetchedResultsControllerDataSource.fetchedResultsController = self.parent.childrenFetchedResultsController;

fetchedResultsControllerDataSource.delegate = self;

fetchedResultsControllerDataSource.reuseIdentifier = @"Cell";

实现delegate

1
2
3
4
5
6
7
8
9
10
11
12
- (void)configureCell:(id)theCell withObject:(id)object

{

UITableViewCell* cell = theCell;

Item* item = object;

cell.textLabel.text = item.title;

}

添加在textFieldShouldReturn:里

1
2
3
4
5
6
7
8
9
10
[Item insertItemWithTitle:title

parent:self.parent

inManagedObjectContext:self.parent.managedObjectContext];

textField.text = @"";

[textField resignFirstResponder];

增删改后table view也会更改显示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

- (void)controller:(NSFetchedResultsController*)controller

didChangeObject:(id)anObject

atIndexPath:(NSIndexPath*)indexPath

forChangeType:(NSFetchedResultsChangeType)type

newIndexPath:(NSIndexPath*)newIndexPath

{

if (type == NSFetchedResultsChangeInsert) {

[self.tableView insertRowsAtIndexPaths:@[newIndexPath]

withRowAnimation:UITableViewRowAnimationAutomatic];

}

}

- (void)controllerWillChangeContent:(NSFetchedResultsController*)controller

{

[self.tableView beginUpdates];

}

- (void)controllerDidChangeContent:(NSFetchedResultsController*)controller

{

[self.tableView endUpdates];

}

和Collection View的结合

范例:https://github.com/AshFurrow/UICollectionView-NSFetchedResultsController collection view没有beginUpdates和endUpdates方法,所以只能用performBatchUpdate方法收集所有更新,然后在controllerDidChangeContent中用block执行所有更新。

如何传递Table view里的Model对象到新的view controller中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

- (void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender

{

[super prepareForSegue:segue sender:sender];

if ([segue.identifier isEqualToString:selectItemSegue]) {

[self presentSubItemViewController:segue.destinationViewController];

}

}

- (void)presentSubItemViewController:(ItemViewController*)subItemViewController

{

Item* item = [self.fetchedResultsControllerDataSource selectedItem];

subItemViewController.parent = item;

}

- (void)viewWillAppear:(BOOL)animated

{

[super viewWillAppear:animated];

self.fetchedResultsControllerDataSource.paused = NO;

}

- (void)viewWillDisappear:(BOOL)animated

{

[super viewWillDisappear:animated];

self.fetchedResultsControllerDataSource.paused = YES;

}

- (void)setPaused:(BOOL)paused

{

_paused = paused;

if (paused) {

self.fetchedResultsController.delegate = nil;

} else {

self.fetchedResultsController.delegate = self;

[self.fetchedResultsController performFetch:NULL];

[self.tableView reloadData];

}

}

删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

//让table view支持滑动删除

- (BOOL)tableView:(UITableView*)tableView

canEditRowAtIndexPath:(NSIndexPath*)indexPath

{

return YES;

}

- (void)tableView:(UITableView *)tableView

commitEditingStyle:(UITableViewCellEditingStyle)editingStyle

forRowAtIndexPath:(NSIndexPath *)indexPath {

if (editingStyle == UITableViewCellEditingStyleDelete) {

id object = [self.fetchedResultsController objectAtIndexPath:indexPath];

[self.delegate deleteObject:object];

}

}

//强制order变化,可以重写prepareForDeletion方法

- (void)prepareForDeletion

{

NSSet* siblings = self.parent.children;

NSPredicate* predicate = [NSPredicate predicateWithFormat:@"order > %@", self.order];

NSSet* siblingsAfterSelf = [siblings filteredSetUsingPredicate:predicate];

[siblingsAfterSelf enumerateObjectsUsingBlock:^(Item* sibling, BOOL* stop)

{

sibling.order = @(sibling.order.integerValue - 1);

}];

}

增加删除的动画效果

1
2
3
4
5
6
7
8
9

else if (type == NSFetchedResultsChangeDelete) {

[self.tableView deleteRowsAtIndexPaths:@[indexPath]

withRowAnimation:UITableViewRowAnimationAutomatic];

}

增加晃动撤销功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//第一步告诉application支持这个

application.applicationSupportsShakeToEdit = YES;

//重写UIResponder类中的两个方法

- (BOOL)canBecomeFirstResponder {

return YES;

}

- (NSUndoManager*)undoManager

{

return self.managedObjectContext.undoManager;

}

//在持续化stack中设置一个undo manager

self.managedObjectContext.undoManager = [[NSUndoManager alloc] init];

//实现上面几步后晃动时会得到两个按钮的提醒框,可以给让用户体验更加友好些

NSString* title = textField.text;

NSString* actionName = [NSString stringWithFormat:NSLocalizedString(@"add item \"%@\"", @"Undo action name of add item"), title];

[self.undoManager setActionName:actionName];

[self.store addItem:title parent:nil];

排序

可以参考官方文档:https://developer.apple.com/library/ios/documentation/CoreData/Reference/NSFetchedResultsControllerDelegate_Protocol/Reference/Reference.html#//apple_ref/doc/uid/TP40008228-CH1-SW14

保存

Fetch获取对象

基础

官方文档:https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreData/Articles/cdFetching.html

范例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

request.result = [NSPredicate predicateWithFormat:

@"(%@ <= longitude) AND (longitude <= %@)"

@"AND (%@ <= latitude) AND (latitude <= %@)",

@(minLongitude), @(maxLongitude), @(minLatitude), @(maxLatitude)];

//取消将值放到row cache中。

request.returnsObjectsAsFaults = NO;

request.fetchLimit = 200;

//执行fetch

NSError *error = nil;

NSArray *stops = [moc executeFetchRequest:request error:&error];

NSAssert(stops != nil, @"Failed to execute %@: %@", request, error);

//二次遍历

NSPredicate *exactPredicate = [self exactLatitudeAndLongitudePredicateForCoordinate:self.location.coordinate];

stops = [stops filteredArrayUsingPredicate:exactPredicate];

- (NSPredicate *)exactLatitudeAndLongitudePredicateForCoordinate:(CLLocationCoordinate2D)pointOfInterest;

{

return [NSPredicate predicateWithBlock:^BOOL(Stop *evaluatedStop, NSDictionary *bindings) {

CLLocation *evaluatedLocation = [[CLLocation alloc] initWithLatitude:evaluatedStop.latitude longitude:evaluatedStop.longitude];

CLLocationDistance distance = [self.location distanceFromLocation:evaluatedLocation];

return (distance < self.distance);

}];

}

//子查询

NSPredicate *timePredicate = [NSPredicate predicateWithFormat:@"(%@ <= departureTime) && (departureTime <= %@)”, startDate, endDate];

NSPredicate *predicate = [NSPredicate predicateWithFormat:

@"(SUBQUERY(stopTimes, $x, (%@ <= $x.departureTime) && ($x.departureTime <= %@)).@count != 0)”, startDate, endDate];

//文本搜索

NSString *searchString = @"U Görli";

predicate = [NSPredicate predicateWithFormat:@"name BEGINSWITH %@", searchString];

导入大量数据

应用Bundle里的SQLite文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
NSFileManager* fileManager = [NSFileManager defaultManager];

NSError *error;

if([fileManager fileExistsAtPath:self.storeURL.path]) {

NSURL *storeDirectory = [self.storeURL URLByDeletingLastPathComponent];

NSDirectoryEnumerator *enumerator = [fileManager enumeratorAtURL:storeDirectory

includingPropertiesForKeys:nil

options:0

errorHandler:NULL];

NSString *storeName = [self.storeURL.lastPathComponent stringByDeletingPathExtension];

//遍历目录下是否有重复

for (NSURL *url in enumerator) {

if (![url.lastPathComponent hasPrefix:storeName]) continue;

[fileManager removeItemAtURL:url error:&error];

}

// 处理错误

}

NSString* bundleDbPath = [[NSBundle mainBundle] pathForResource:@"seed" ofType:@"sqlite"];

[fileManager copyItemAtPath:bundleDbPath toPath:self.storeURL.path error:&error];

//真机删除会失效,所以使用版本号来进行区分新旧

NSString* bundleVersion = [infoDictionary objectForKey:(NSString *)kCFBundleVersionKey];

NSString *seedVersion = [[NSUserDefaults standardUserDefaults] objectForKey@"SeedVersion"];

if (![seedVersion isEqualToString:bundleVersion]) {

// 复制源数据库

}

// ... 导入成功后

NSDictionary *infoDictionary = [NSBundle mainBundle].infoDictionary;

[[NSUserDefaults standardUserDefaults] setObject:bundleVersion forKey:@"SeedVersion"];

导入范例

https://github.com/objcio/issue-4-importing-and-fetching

版本迁移

Mapping Models

NSMigrationManager能够推断两个版本模型的映射关系,但是如果版本跨度大了就力不从心了。

Progressive Migrations渐进式迁移

实现原理是两个版本之间确保正常,升级时按照一个版本一个版本渐进式的升级方式,比如最新的版本是第四版,如果用户使用的是第二版的,那么升级是就是先从第二版升级到第三版,然后再从第三版升级到第四版。完整范例:https://github.com/objcio/issue-4-core-data-migration 主要代码来自Marcus Zarrahttps://twitter.com/mzarra ,他的书关于Core Data的值得一看,http://pragprog.com/book/mzcd2/core-data

迁移策略

NSEntityMigrationPolicy这个类不光能够修改Entity的属性和关系,还能够自定义一些操作完成每个Entity的迁移。例如在Entity Mapping的Custom Polity里写上自定义的polity的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
NSNumber *modelVersion = [mapping.userInfo valueForKey:@"modelVersion"];

if (modelVersion.integerValue == 2) {

NSMutableArray *sourceKeys = [sourceInstance.entity.attributesByName.allKeys mutableCopy];

NSDictionary *sourceValues = [sourceInstance dictionaryWithValuesForKeys:sourceKeys];

NSManagedObject *destinationInstance = [NSEntityDescription insertNewObjectForEntityForName:mapping.destinationEntityName

inManagedObjectContext:manager.destinationContext];

NSArray *destinationKeys = destinationInstance.entity.attributesByName.allKeys;

for (NSString *key in destinationKeys) {

id value = [sourceValues valueForKey:key];

// 避免value为空

if (value && ![value isEqual:[NSNull null]]) {

[destinationInstance setValue:value forKey:key];

}

}

}

NSMutableDictionary *authorLookup = [manager lookupWithKey:@"authors"];

// 检查该作者是否已经被创建了

NSString *authorName = [sourceInstance valueForKey:@"author"];

NSManagedObject *author = [authorLookup valueForKey:authorName];

if (!author) {

// 创建作者

// ...

// 更新避免重复

[authorLookup setValue:author forKey:authorName];

}

[destinationInstance performSelector:@selector(addAuthorsObject:) withObject:author];

//源存储和目的存储之间的关系

[manager associateSourceInstance:sourceInstance

withDestinationInstance:destinationInstance

forEntityMapping:mapping];

return YES;

NSMigrationManager的category方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@implementation NSMigrationManager (Lookup)

- (NSMutableDictionary *)lookupWithKey:(NSString *)lookupKey

{

NSMutableDictionary *userInfo = (NSMutableDictionary *)self.userInfo;

// 这里检查一下是否已经建立了 userInfo 的字典

if (!userInfo) {

userInfo = [@{} mutableCopy];

self.userInfo = userInfo;

}

NSMutableDictionary *lookup = [userInfo valueForKey:lookupKey];

if (!lookup) {

lookup = [@{} mutableCopy];

[userInfo setValue:lookup forKey:lookupKey];

}

return lookup;

}

@end

更复杂的迁移

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
NSArray *users = [sourceInstance valueForKey:@"users"];

for (NSManagedObject *user in users) {

NSManagedObject *file = [NSEntityDescription insertNewObjectForEntityForName:@"File"

inManagedObjectContext:manager.destinationContext];

[file setValue:[sourceInstance valueForKey:@"fileURL"] forKey:@"fileURL"];

[file setValue:destinationInstance forKey:@"book"];

NSInteger userId = [[user valueForKey:@"userId"] integerValue];

NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"User"];

request.predicate = [NSPredicate predicateWithFormat:@"userId = %d", userId];

NSManagedObject *user = [[manager.destinationContext executeFetchRequest:request error:nil] lastObject];

[file setValue:user forKey:@"user"];

}

数据量大时的迁移改造,利用CoreData提供的chunks数据块方式。官方文档https://developer.apple.com/library/ios/documentation/cocoa/Conceptual/CoreDataVersioning/Articles/vmCustomizing.html#//apple_ref/doc/uid/TP40004399-CH8-SW9

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
NSArray *mappingModels = @[mappingModel]; // 我们之前建立的那个模型

if ([self.delegate respondsToSelector:@selector(migrationManager:mappingModelsForSourceModel:)]) {

NSArray *explicitMappingModels = [self.delegate migrationManager:self

mappingModelsForSourceModel:sourceModel];

if (0 < explicitMappingModels.count) {

mappingModels = explicitMappingModels;

}

}

for (NSMappingModel *mappingModel in mappingModels) {

didMigrate = [manager migrateStoreFromURL:sourceStoreURL

type:type

options:nil

withMappingModel:mappingModel

toDestinationURL:destinationStoreURL

destinationType:type

destinationOptions:nil

error:error];

}

- (NSArray *)migrationManager:(MHWMigrationManager *)migrationManager

mappingModelsForSourceModel:(NSManagedObjectModel *)sourceModel

{

NSMutableArray *mappingModels = [@[] mutableCopy];

NSString *modelName = [sourceModel mhw_modelName];

if ([modelName isEqual:@"Model2"]) {

// 把该映射模型加入数组

}

return mappingModels;

}

- (NSString *)mhw_modelName

{

NSString *modelName = nil;

NSArray *modelPaths = // get paths to all the mom files in the bundle

for (NSString *modelPath in modelPaths) {

NSURL *modelURL = [NSURL fileURLWithPath:modelPath];

NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];

if ([model isEqual:self]) {

modelName = modelURL.lastPathComponent.stringByDeletingPathExtension;

break;

}

}

return modelName;

}

建立单元测试,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83

- (void)setUpCoreDataStackMigratingFromStoreWithName:(NSString *)name

{

NSURL *storeURL = [self temporaryRandomURL];

[self copyStoreWithName:name toURL:storeURL];

NSURL *momURL = [[NSBundle mainBundle] URLForResource:@"Model" withExtension:@"momd"];

self.managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:momURL];

NSString *storeType = NSSQLiteStoreType;

MHWMigrationManager *migrationManager = [MHWMigrationManager new];

[migrationManager progressivelyMigrateURL:storeURL

ofType:storeType

toModel:self.managedObjectModel

error:nil];

self.persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel];

[self.persistentStoreCoordinator addPersistentStoreWithType:storeType

configuration:nil

URL:storeURL

options:nil

error:nil];

self.managedObjectContext = [[NSManagedObjectContext alloc] init];

self.managedObjectContext.persistentStoreCoordinator = self.persistentStoreCoordinator;

}

- (NSURL *)temporaryRandomURL

{

NSString *uniqueName = [NSProcessInfo processInfo].globallyUniqueString;

return [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingString:uniqueName]];

}

- (void)copyStoreWithName:(NSString *)name toURL:(NSURL *)url

{

// 每次创建一个唯一的url以保证测试正常运行

NSBundle *bundle = [NSBundle bundleForClass:[self class]];

NSFileManager *fileManager = [NSFileManager new];

NSString *path = [bundle pathForResource:[name stringByDeletingPathExtension] ofType:name.pathExtension];

[fileManager copyItemAtPath:path

toPath:url.path error:nil];

}

//在测试类中复用

- (void)setUp

{

[super setUp];

[self setUpCoreDataStackMigratingFromStoreWithName:@"Model1.sqlite"];

}

调试迁移一个有用的启动参数是-com.apple.CoreData.MigrationDebug,设置1就会在console收到迁移数据时会出现的特殊的情况的信息。如果设置-com.apple.CoreData.SQLDebug 为 1还能够在console看到实际操作的SQL语句。

Core Data的并行处理

性能测试

  • 加上-com.apple.CoreData.SQLDebug1 作为启动参数传递给应用程序可以得到的输出
1
2
3
4
5
6
7

sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZIDENTIFIER, t0.ZLATITUDE, t0.ZLONGITUDE, t0.ZNAME FROM ZSTOP t0 WHERE (? <= t0.ZLONGITUDE AND t0.ZLONGITUDE <= ? AND ? <= t0.ZLATITUDE AND t0.ZLATITUDE <= ?) LIMIT 100

annotation: sql connection fetch time: 0.0008s

annotation: total fetch execution time: 0.0013s for 15 rows.

实际生成的SQL是:

1
2
3
4
5
6
SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZIDENTIFIER, t0.ZLATITUDE, t0.ZLONGITUDE, t0.ZNAME FROM ZSTOP t0

WHERE (? <= t0.ZLONGITUDE AND t0.ZLONGITUDE <= ? AND ? <= t0.ZLATITUDE AND t0.ZLATITUDE <= ?)

LIMIT 200

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

% cd TrafficSearch

% sqlite3 transit-data.sqlite

SQLite version 3.7.13 2012-07-17 17:46:21

Enter ".help" for instructions

Enter SQL statements terminated with a ";"

sqlite> EXPLAIN QUERY PLAN SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZIDENTIFIER, t0.ZLATITUDE, t0.ZLONGITUDE, t0.ZNAME FROM ZSTOP t0

...> WHERE (13.30845219672199 <= t0.ZLONGITUDE AND t0.ZLONGITUDE <= 13.33441458422844 AND 52.42769566863058 <= t0.ZLATITUDE AND t0.ZLATITUDE <= 52.44352370653525)

...> LIMIT 100;

0|0|0|SEARCH TABLE ZSTOP AS t0 USING INDEX ZSTOP_ZLONGITUDE_INDEX (ZLONGITUDE>? AND ZLONGITUDE<?) (~6944 rows)

输出

1
2
0|0|0|SEARCH TABLE ZSTOP AS t0 USING INDEX ZSTOP_ZLONGITUDE_ZLATITUDE (ZLONGITUDE>? AND ZLONGITUDE<?) (~6944 rows)