16、iOS开发:SQLite与Core Data数据存储实战

iOS开发:SQLite与Core Data数据存储实战

在iOS应用开发中,数据存储是一项关键任务。SQLite和Core Data是常用的数据存储解决方案,下面将详细介绍它们的特点、使用方法及操作步骤。

1. SQLite数据库概述

SQLite是一种轻量级的数据库,它将整个数据库存储在一个单一的文件中,具有快速、可靠且易于在应用中实现的优点。而且,使用SQLite无需安装任何额外的软件,苹果已经为开发者处理好了相关事宜。

不过,SQLite也存在一些局限性:
- 单用户数据库 :SQLite设计为单用户数据库,不适合在多人同时访问同一数据库的环境中使用,否则可能导致数据丢失或损坏。
- 数据库大小限制 :在商业环境中,数据库可能会变得非常大,而SQLite处理大型数据库时可能会出现性能问题,更适合处理较小的数据库。
- 备份和恢复功能不足 :与企业级数据库解决方案相比,SQLite缺乏一些备份和数据恢复功能。

2. Core Data简介

苹果为了简化数据库开发的挑战,推出了Core Data框架。开发者无需熟悉SQL语言,通过Core Data就可以更轻松地与数据库进行交互。Core Data源自NeXT的Enterprise Object Framework,使用它比直接与SQLite数据库交互要简单得多。

3. 创建Core Data项目

以下是创建一个新的Core Data项目的具体步骤:
1. 打开Xcode,选择“File ➤ New ➤ Project”。要创建一个iOS Core Data项目,在左侧菜单中选择“Application”(位于iOS标题下方),然后选择“Single View Application”。
2. 点击“Next”,在下一个屏幕中输入项目名称,这里使用“BookStore”。
3. 确保语言设置为Objective - C,在底部找到“Use Core Data”复选框并勾选,然后点击“Next”。
4. 选择项目的保存位置,点击“Create”。完成后,新的项目将打开,此时会有一个“BookStore.xcdatamodeld”文件,该文件称为数据模型,包含了将存储在Core Data中的数据信息。

4. 数据模型设计

在项目的“BookStore”文件夹中,有一个“BookStoreCoreData.xcdatamodeld”文件,点击该文件打开,窗口分为四个部分:
- 实体(Entities) :位于左侧,是要存储在数据库中的对象或项目。在数据库术语中,实体相当于表。
- 属性(Attributes) :位于右上角窗口,是关于实体的信息片段。例如,一本书是一个实体,书名就是该实体的一个属性。在数据库中,属性相当于列,由实体创建的对象称为行。
- 关系(Relationships) :中间窗口显示实体的所有关系,关系用于连接一个实体与另一个实体。
- 获取属性(Fetched Properties) :屏幕右下角部分处理获取属性,可用于创建数据过滤器,但超出了本文的范围。

下面是创建实体和属性的具体步骤:

创建“Book”实体
  1. 点击窗口左下角的加号,或选择“Editor ➤ Add Entity”。
  2. 在左侧将实体命名为“Book”,注意实体名称首字母必须大写。
  3. 添加属性:点击窗口右下角的加号,或选择“Editor ➤ Add Attribute”。依次添加“title”(书名)、“price”(价格)、“yearPublished”(出版年份)等属性。
    • “title”属性:名称首字母必须小写,数据类型选择“String”,用于存储文本信息。
    • “price”属性:数据类型选择“Decimal”,用于存储带小数的数字,可处理货币值。
    • “yearPublished”属性:名称采用“yearPublished”的格式,数据类型选择“Integer 32”,用于存储整数。
创建“Author”实体
  1. 添加一个新的实体,命名为“Author”。
  2. 为该实体添加“lastName”和“firstName”属性,数据类型均为“String”。
创建实体关系
  • 点击“Book”实体,点击并按住屏幕右下角的加号,选择“Add Relationship”,将关系命名为“author”,并在“Destination”下拉菜单中选择“Author”。
  • 点击“Author”实体,同样点击加号选择“Add Relationship”,将关系命名为“books”(因为一个作者可以有多本书),在“Destination”中选择“Book”,在“Inverse”中选择上一步创建的关系,在右侧的“Utilities”窗口中选择“Data Model Inspector”,将关系类型设置为“To Many”。
5. 创建托管对象子类

为了让代码能够识别新创建的实体,需要创建托管对象子类:
1. 按住Shift键,选择“Book”实体和“Author”实体,然后选择“Editor ➤ Create NSManagedObject Subclass”。
2. 在弹出的屏幕中选择“BookStore”数据模型,点击“Next”。
3. 选择要创建托管对象的实体(“Book”和“Author”),点击“Next”。
4. 选择存储位置,点击“Options”按钮确保语言设置为Objective - C,然后点击“Create”。此时项目中会添加八个文件,其中“Book+CoreDataProperties.h”和“Author+CoreDataProperties.h”等文件包含了新创建实体的信息。

下面是“Book+CoreDataProperties.h”文件的部分内容:

#import "Book.h"
NS_ASSUME_NONNULL_BEGIN
@interface Book (CoreDataProperties)
@property (nullable, nonatomic, retain) NSString *title;
@property (nullable, nonatomic, retain) NSDecimalNumber *price;
@property (nullable, nonatomic, retain) NSNumber *yearPublished;
@property (nullable, nonatomic, retain) Author *author;
@end
NS_ASSUME_NONNULL_END
6. 托管对象上下文

在Core Data中,每个托管对象都应该存在于一个托管对象上下文中。托管对象上下文负责跟踪对象的更改、执行撤销操作以及将数据写入数据库。这样可以一次性保存多个更改,而不是逐个保存,从而提高保存记录的速度。开发者无需手动跟踪对象的更改,托管对象上下文会自动处理这些事情。

7. 设置用户界面

以下是设置用户界面的详细步骤:
1. 在项目的“BookStore”文件夹中,点击“Main.storyboard”文件,Xcode将在编辑窗口中打开它。
2. 在空白窗口中,从对象库添加功能对象。在屏幕右下角的搜索字段中输入“table”,找到“Table View Controller”和“Table View”,将“Table View”拖到视图中。
3. 为了在“Table View”中创建单元格,在对象库中搜索“cell”,将“Table View Cell”拖到表格中。
4. 选择单元格,在右侧的“Attributes Inspector”中将“Style”设置为“Basic”,将“Identifier”设置为“Cell”。
5. 通常将“Table View”放在“Navigation Controller”中,以便在表格视图中添加“Add”按钮。选择场景框中的视图控制器(带有黄色图标的那个),从应用程序菜单中选择“Editor ➤ Embed In ➤ Navigation Controller”。
6. 在导航栏上添加一个“UIBarButtonItem”,在对象库中搜索“bar button”,将其拖到视图的导航栏右上角。
7. 选择“Bar Button Item”,将“System Item”更改为“Add”,按钮将显示为加号图标。
8. 将界面与代码连接起来:
- 按住Control键,将“Table View”拖到“Document Outline”中的视图控制器,在弹出窗口中选择“dataSource”和“delegate”,需要Control - 拖动两次。
- 点击Xcode窗口右上角的“Assistant Editor”图标,打开代码和故事板。按住Control键,将“Add”按钮拖到右侧的视图控制器代码中,设置连接类型为“Action”,并为新方法命名,例如“addNew”。
- 将“UITableView”从左侧面板拖到右侧代码的顶部,创建一个名为“tableView”的出口。

8. 代码实现

接下来,需要对代码进行一些修改和添加,以实现界面的功能:
1. 打开“ViewController.h”文件,修改类声明:

@interface ViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>

这表明控制器可以作为表格视图的委托和数据源。

  1. 在“ViewController.h”文件顶部添加Core Data的导入语句:
#import <CoreData/CoreData.h>
  1. 在“ViewController.h”文件中添加托管对象上下文变量:
@interface ViewController : UIViewController <UITableViewDataSource, UITableViewDelegate> {
    NSManagedObjectContext * managedObjectContext ;
}
  1. 在“ViewController.m”文件的“viewDidLoad”方法中初始化托管对象上下文:
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
managedObjectContext = appDelegate.managedObjectContext;
  1. 在“ViewController.m”文件顶部添加必要的导入语句:
#import "AppDelegate.h"
#import "Book.h"
  1. 添加查询数据库记录的方法“loadBooks”:
- (NSArray *)loadBooks {
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"Book"];
    NSArray *bookArray = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
    return bookArray;
}
  1. 添加表格视图的数据源方法:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [[self loadBooks] count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
    }
    Book *myBook = [[self loadBooks] objectAtIndex:indexPath.row];
    cell.textLabel.text = myBook.title;
    return cell;
}
  1. 在“addNew”方法中添加代码以添加新的书籍对象:
- (IBAction)addNew:(id)sender {    
    Book *myBook = [NSEntityDescription insertNewObjectForEntityForName:@"Book" inManagedObjectContext:managedObjectContext];
    myBook.title = [NSMutableString stringWithFormat:@"My Book%lu", (unsigned long)[self loadBooks].count];
    [managedObjectContext save:nil];
    [self.tableView reloadData];     
}
9. 运行应用

完成上述步骤后,构建并运行应用程序。多次点击加号按钮,将向对象存储中添加新的“Book”对象。即使退出应用并重新启动,数据仍然会保留。

通过以上步骤,你可以利用SQLite和Core Data在iOS应用中实现数据的存储和管理。这种方式不仅简单高效,还能确保数据的持久化,为用户提供更好的使用体验。

下面用mermaid流程图展示创建Core Data项目的流程:

graph TD;
    A[打开Xcode] --> B[选择File ➤ New ➤ Project];
    B --> C[选择iOS Application ➤ Single View Application];
    C --> D[点击Next,输入项目名BookStore];
    D --> E[确保语言为Objective - C,勾选Use Core Data];
    E --> F[点击Next];
    F --> G[选择保存位置,点击Create];

同时,为了更清晰地展示实体和属性的创建过程,我们可以用表格来呈现:
| 操作步骤 | 实体/属性 | 名称 | 数据类型 | 备注 |
| ---- | ---- | ---- | ---- | ---- |
| 1 | 实体 | Book | - | 名称首字母大写 |
| 2 | 属性 | title | String | 名称首字母小写 |
| 3 | 属性 | price | Decimal | - |
| 4 | 属性 | yearPublished | Integer 32 | 两单词属性,首单词小写,第二个单词大写 |
| 5 | 实体 | Author | - | 名称首字母大写 |
| 6 | 属性 | lastName | String | - |
| 7 | 属性 | firstName | String | - |

iOS开发:SQLite与Core Data数据存储实战

10. 代码分析

为了更好地理解前面添加的代码,下面对各个部分进行详细分析。

托管对象上下文初始化
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
managedObjectContext = appDelegate.managedObjectContext;

这部分代码的作用是获取应用程序的委托对象,然后将托管对象上下文指向应用委托中的托管对象上下文。这样做的好处是在整个应用中使用同一个托管对象上下文,方便数据的管理和操作。

查询数据库记录方法 loadBooks
- (NSArray *)loadBooks {
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"Book"];
    NSArray *bookArray = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
    return bookArray;
}

此方法用于从数据库中查询所有的 Book 对象。首先创建一个 NSFetchRequest 对象,指定要查询的实体名称为 Book 。然后通过托管对象上下文执行这个请求,将结果存储在 bookArray 中并返回。

表格视图数据源方法
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [[self loadBooks] count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
    }
    Book *myBook = [[self loadBooks] objectAtIndex:indexPath.row];
    cell.textLabel.text = myBook.title;
    return cell;
}
  • numberOfSectionsInTableView 方法:告诉表格视图只有一个分区。
  • tableView:numberOfRowsInSection 方法:通过调用 loadBooks 方法获取书籍数组,并返回数组的元素个数,即表格视图的行数。
  • tableView:cellForRowAtIndexPath 方法:创建或重用一个表格视图单元格,从 loadBooks 数组中获取对应行的 Book 对象,将书名赋值给单元格的文本标签,最后返回该单元格。
添加新书籍方法 addNew
- (IBAction)addNew:(id)sender {    
    Book *myBook = [NSEntityDescription insertNewObjectForEntityForName:@"Book" inManagedObjectContext:managedObjectContext];
    myBook.title = [NSMutableString stringWithFormat:@"My Book%lu", (unsigned long)[self loadBooks].count];
    [managedObjectContext save:nil];
    [self.tableView reloadData];     
}

当点击添加按钮时,此方法会被调用。首先创建一个新的 Book 对象并插入到托管对象上下文中,然后为书名赋值,接着保存托管对象上下文的更改,最后重新加载表格视图的数据,以显示新添加的书籍。

11. 错误处理

在实际开发中,需要对可能出现的错误进行处理。例如,在保存托管对象上下文和执行获取请求时,可能会出现错误。可以对前面的代码进行修改,添加错误处理逻辑。

- (NSArray *)loadBooks {
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"Book"];
    NSError *error;
    NSArray *bookArray = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
    if (error) {
        NSLog(@"Error fetching books: %@", error.localizedDescription);
        return nil;
    }
    return [bookArray mutableCopy];
}

- (IBAction)addNew:(id)sender {    
    Book *myBook = [NSEntityDescription insertNewObjectForEntityForName:@"Book" inManagedObjectContext:managedObjectContext];
    myBook.title = [NSMutableString stringWithFormat:@"My Book%lu", (unsigned long)[self loadBooks].count];
    NSError *error;
    if (![managedObjectContext save:&error]) {
        NSLog(@"Error saving context: %@", error.localizedDescription);
    }
    [self.tableView reloadData];     
}

loadBooks 方法中,使用 NSError 对象来捕获执行获取请求时可能出现的错误,并在出现错误时打印错误信息。在 addNew 方法中,同样使用 NSError 对象来捕获保存托管对象上下文时的错误,并进行相应的处理。

12. 性能优化

随着数据量的增加,可能会出现性能问题。可以采取以下措施进行性能优化:

批量插入数据

如果需要插入大量数据,可以使用批量插入的方式,减少保存操作的次数。

- (void)batchInsertBooks:(NSArray *)books {
    for (NSDictionary *bookInfo in books) {
        Book *myBook = [NSEntityDescription insertNewObjectForEntityForName:@"Book" inManagedObjectContext:managedObjectContext];
        myBook.title = bookInfo[@"title"];
        myBook.price = bookInfo[@"price"];
        myBook.yearPublished = bookInfo[@"yearPublished"];
    }
    NSError *error;
    if (![managedObjectContext save:&error]) {
        NSLog(@"Error saving context: %@", error.localizedDescription);
    }
    [self.tableView reloadData];
}
分页查询

当数据量很大时,一次性查询所有数据可能会导致性能下降。可以采用分页查询的方式,每次只查询部分数据。

- (NSArray *)loadBooksWithOffset:(NSInteger)offset limit:(NSInteger)limit {
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"Book"];
    [fetchRequest setFetchOffset:offset];
    [fetchRequest setFetchLimit:limit];
    NSError *error;
    NSArray *bookArray = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
    if (error) {
        NSLog(@"Error fetching books: %@", error.localizedDescription);
        return nil;
    }
    return [bookArray mutableCopy];
}
13. 总结

通过本文的介绍,我们了解了如何在iOS应用中使用SQLite和Core Data进行数据的存储和管理。从创建Core Data项目、设计数据模型、设置用户界面到实现代码逻辑,每一个步骤都进行了详细的说明。同时,还介绍了代码分析、错误处理和性能优化的相关内容。

在实际开发中,根据应用的需求和数据量的大小,可以灵活选择合适的存储方案。SQLite适合小型数据库,而Core Data则提供了更高级的功能和更方便的操作方式。通过合理使用这些技术,可以提高开发效率,确保数据的安全和持久化。

下面用mermaid流程图展示添加新书籍的流程:

graph TD;
    A[点击Add按钮] --> B[创建新的Book对象];
    B --> C[设置Book对象的属性];
    C --> D[保存托管对象上下文];
    D --> E[重新加载表格视图数据];

另外,为了更清晰地展示性能优化的方法,我们用表格来呈现:
| 优化方法 | 说明 |
| ---- | ---- |
| 批量插入数据 | 减少保存操作次数,提高插入大量数据的性能 |
| 分页查询 | 每次只查询部分数据,避免一次性查询大量数据导致性能下降 |

通过以上的介绍和示例,希望能帮助你更好地掌握在iOS应用中使用SQLite和Core Data进行数据存储和管理的技术。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值