股票场内基金交易,没时间盯盘?
在《以文件形式存储数据的几种方式》一文中,我介绍了一些轻量级的数据持久化方案及其利弊。这篇文章我将简单介绍如何通过 CoreData 实现大批量数据的存取。
CoreData 是苹果针对 Mac 和 iOS 平台开发的一个框架,主要用来将模型对象的状态持久化到磁盘。它能够让开发者以面向对象而非 sql 语句的方式操作数据,提高了开发效率。
关键术语
CoreData 对于初学者的第一个难点就是扑面而来的一堆新术语。我的建议是在重点理解其相关工作流程的基础上多加练习。这里有一张术语表格,并不要求马上掌握,而是希望在之后的学习使用过程中慢慢熟悉。
工作流程
CoreData 繁杂的术语源自其众多可用组件。当他们为了协同工作组合到一起时,“看上去”显得有些复杂,不过如果我们理解了工作流程进行分块处理,大多数情况下单个组件的设置都相当简单。
最常见的工作流程如图所示:
尽管上面表格已经列出,这里仍要对几个关键概念进行介绍:
-
NSManagedObjectModel:模型对象。对应我们定义的模型文件;
-
NSManagedObjectContext:模型上下文。管理其所包含的模型对象,并负责和数据库进行交互(增删改查)。一个上下文对应一个数据库;
-
NSPersistentStoreCoordinator:持久化存储协调器。作用是上下文和数据库需要交互时在中间调节。拥有一个属于自己的持久化存储(NSPersistentStore),可以在文件系统中与 SQLite 数据库交互。
使用步骤
创建模型文件
如果新建项目时点选了“use Core Data”,那么初始会默认创建一个以项目名字命名的模型文件:
想要自己创建模型文件的话,在项目中右键新建文件-> Core Data -> Data Model。
在模型文件中添加实体
相当于数据库中的建表。我们新建一个 School 模型文件,在其中添加 Student 和 Association 两个实体,注意首字母要大写:
创建实体类
在项目中右键新建文件-> Core Data -> NSManagedObject subclass -> 选择对应的模型文件和实体,就可以看到新创建出来的实体类:
注意到模型属性全部放到了 CoreDataProperties 这个分类文件中,苹果的解释是如果模型文件发生变动,那么相关的回收、更新等操作都只会在分类文件中进行,不会影响到主文件,因此我们可以在主文件中放心的自定义属性和方法。
初始化模型上下文,关联生成数据库
对于项目自带的模型文件生成的模型对象,在 AppDelegate 类中已经做好了初始化设置,我们可以直接拿到 managedObjectContext 对象进行后续语数据库的交互。
而对于自定义的模型文件,我们需要自己进行设置:
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 |
@interface ViewController () // 模型上下文 @property (strong, nonatomic) NSManagedObjectContext *schoolContext; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; [self setupSchoolContext]; } - (void)setupSchoolContext{ // 加载模型对象 NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"School" withExtension:@"momd"]]; // 初始化持久化存储协调器 NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; // 设置数据库文件路径 NSString *dbPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject stringByAppendingPathComponent:@"School.sqlite"]; // 添加持久化存储库,一般采用 SQLite [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[NSURL fileURLWithPath:dbPath] options:nil error:nil]; // 初始化模型上下文 self.schoolContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; self.schoolContext.persistentStoreCoordinator = psc; } |
运行项目,可以在沙盒的 Document 目录中看到生成了三个数据库文件:
与数据库交互(增删改查)
create
通过 NSEntityDescription 类的方法
1 2 3 4 5 6 |
/* context:模型上下文 entityName:想要进行添加的实体类类名 */ + (__kindof NSManagedObject *)insertNewObjectForEntityForName:(NSString *)entityName inManagedObjectContext:(NSManagedObjectContext *)context; |
获得一个新的实体类对象,相当于在表中添加了一个新元素。下面我们分别向 Association 和 Student 两个实体类添加元素:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
- (IBAction)createButtonClicked { Association *literaryAssociation = [NSEntityDescription insertNewObjectForEntityForName:[NSString stringWithFormat:@"%@",[Association class]] inManagedObjectContext:self.schoolContext]; literaryAssociation.name = @"literary association"; Association *footballAssociation = [NSEntityDescription insertNewObjectForEntityForName:[NSString stringWithFormat:@"%@",[Association class]] inManagedObjectContext:self.schoolContext]; footballAssociation.name = @"football association"; for (int i = 0; i < 100; i++) { Student *student = [NSEntityDescription insertNewObjectForEntityForName:[NSString stringWithFormat:@"%@",[Student class]] inManagedObjectContext:self.schoolContext]; student.name = [NSString stringWithFormat:@"Tim%02d",i]; student.age = @(18 + arc4random_uniform(10)); student.association = (arc4random_uniform(2) > 0)?literaryAssociation:footballAssociation; } // 保存到数据库 NSError *error = nil; [self.schoolContext save:&error]; } |
read
查询操作主要是通过模型上下文的方法:
1 2 |
- (nullable NSArray *)executeFetchRequest:(NSFetchRequest *)request error:(NSError **)error; |
获得符合 NSFetchRequest 对象条件的实体类对象数组。NSFetchRequest 对象可以通过初始化方法:
1 2 3 |
+ (instancetype)fetchRequestWithEntityName:(NSString*)entityName; - (instancetype)initWithEntityName:(NSString*)entityName; |
确定需要查询的实体类。
下面是一个最简单的遍历查询:
1 2 3 4 5 6 7 8 9 10 11 |
- (IBAction)readButtonClicked { NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:[NSString stringWithFormat:@"%@",[Student class]]]; NSError *error = nil; NSArray *students = [self.schoolContext executeFetchRequest:request error:&error]; if (!error) { for (Student *student in students) { NSLog(@"nsme is %@, age is %@, association is %@",student.name, student. age,student.association.name); } } } |
更为详细的查询方式实际上是通过 NSFetchRequest 对象的不同属性进行设置。
-
条件查询,模糊查询
设置 predicate 属性,通过 NSPredicate 类方法输入查询语句。
12345// 查询 age 字段中大于等于"20"的元素request.predicate = [NSPredicate predicateWithFormat:@"age >= %@", @"20"];// 查询 name 字段中包含"1"的元素request.predicate = [NSPredicate predicateWithFormat:@"name like %@", @"*1*"]; -
分页查询
设置 fetchLimit 和 fetchOffset 属性。
12345// 分页查询request.fetchLimit = 5; // 每一页数据数量int num = 10; // 页号request.fetchOffset = (num - 1) * request.fetchLimit; // 分页起始索引 -
排序
设置 sortDescriptors 属性,通过 NSSortDescriptor 类对指定字段排序。
1234// 排序NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"age" ascending:NO];request.sortDescriptors = @[sort];
update,delete
这两个操作在流程上差不多,都是遍历 NSFetchRequest 对象查询所得的实体对象数组进行设置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:[NSString stringWithFormat:@"%@",[Student class]]]; /* 查询设置 */ NSError *error = nil; NSArray *students = [self.schoolContext executeFetchRequest:request error:&error]; if (!error) { for (Student *student in students) { // update 操作 student.age = @20; // delete 操作 [self.schoolContext deleteObject:student] } } // 保存到数据库 error = nil; [self.context save:&error]; |
想获得去掉 5 元限制的证券账户吗?

如果您想去掉最低交易佣金 5 元限制,使用微信扫描左边小程序二维码,访问微信小程序「优财助手」,点击底部菜单「福利」,阅读文章「通过优财开证券账户无最低交易佣金 5 元限制」,按照文章步骤操作即可获得免 5 元证券账户,股票基金交易手续费率万 2.5。
请注意,一定要按照文章描述严格操作,如错误开户是无法获得免 5 元证券账户的。