MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Objective-C 在 iOS 数据存储与管理中的应用

2022-06-101.2k 阅读

一、iOS 数据存储概述

在 iOS 应用开发中,数据存储与管理是至关重要的环节。应用往往需要保存各种类型的数据,如用户设置、应用状态、本地缓存数据等。根据数据的特性和使用场景,iOS 提供了多种数据存储方式,包括但不限于以下几种:

  1. 属性列表(Property List):一种简单的数据存储格式,用于存储基本数据类型(如字符串、数字、数组、字典等)的集合。属性列表文件通常以.plist为扩展名。
  2. 归档(Archiving):也称为序列化,它允许将复杂的对象层次结构转换为字节流,以便存储到文件中或通过网络传输。在 Objective - C 中,通过遵循NSCoding协议来实现对象的归档和解档。
  3. SQLite 数据库:一种轻量级的关系型数据库,非常适合在移动设备上使用。iOS 提供了 SQLite 的 API,可用于创建、查询和管理数据库。
  4. Core Data:是一个功能强大的框架,用于管理应用程序的模型层数据。它提供了对象 - 关系映射(ORM)功能,使得开发者可以以面向对象的方式操作数据,而无需编写大量的 SQL 语句。

Objective - C 作为 iOS 开发的传统语言,为这些数据存储方式提供了良好的支持,下面将详细介绍 Objective - C 在这些数据存储与管理方式中的应用。

二、Objective - C 使用属性列表进行数据存储

2.1 属性列表基础

属性列表是一种 XML 格式的文件,它可以存储特定类型的对象,主要包括NSStringNSNumberNSDateNSArrayNSDictionary以及NSData。这些对象被称为属性列表对象,因为它们都可以直接转换为属性列表格式。

2.2 写入属性列表文件

在 Objective - C 中,将数据写入属性列表文件非常简单。假设我们有一个包含用户设置的字典,我们想将其保存到属性列表文件中。以下是示例代码:

NSDictionary *userSettings = @{
    @"username": @"JohnDoe",
    @"isLoggedIn": @YES,
    @"preferredLanguage": @"en"
};
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *plistPath = [documentsDirectory stringByAppendingPathComponent:@"UserSettings.plist"];
[userSettings writeToFile:plistPath atomically:YES];

在上述代码中,首先创建了一个包含用户设置的字典userSettings。然后通过NSSearchPathForDirectoriesInDomains函数获取应用程序的文档目录路径,接着在文档目录路径后追加文件名UserSettings.plist,得到完整的属性列表文件路径plistPath。最后,使用字典的writeToFile:atomically:方法将字典内容写入到属性列表文件中,atomically:YES表示以原子方式写入,即先写入一个临时文件,写入成功后再将临时文件重命名为目标文件,这样可以保证文件的完整性。

2.3 读取属性列表文件

读取属性列表文件也同样简单,示例代码如下:

NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *plistPath = [documentsDirectory stringByAppendingPathComponent:@"UserSettings.plist"];
NSDictionary *readSettings = [NSDictionary dictionaryWithContentsOfFile:plistPath];
if (readSettings) {
    NSString *username = readSettings[@"username"];
    NSNumber *isLoggedIn = readSettings[@"isLoggedIn"];
    NSString *preferredLanguage = readSettings[@"preferredLanguage"];
    NSLog(@"Username: %@, Is Logged In: %@, Preferred Language: %@", username, isLoggedIn, preferredLanguage);
} else {
    NSLog(@"Failed to read UserSettings.plist");
}

这段代码首先获取属性列表文件的路径,然后使用NSDictionarydictionaryWithContentsOfFile:方法从文件中读取数据并创建字典。如果读取成功,就可以从字典中获取相应的用户设置信息并打印出来;如果读取失败,则打印错误信息。

三、Objective - C 中的归档与解档

3.1 NSCoding 协议

归档与解档是将对象转换为字节流进行存储或传输,并在需要时将字节流还原为对象的过程。在 Objective - C 中,通过遵循NSCoding协议来实现对象的归档和解档。NSCoding协议定义了两个必需的方法:

  • encodeWithCoder::在归档时调用,用于将对象的属性编码到编码器(NSCoder)中。
  • initWithCoder::在解档时调用,用于从解码器(NSCoder)中初始化对象的属性。

3.2 自定义对象的归档和解档示例

假设有一个自定义的Person类,我们想对其进行归档和解档。首先定义Person类并遵循NSCoding协议:

#import <Foundation/Foundation.h>

@interface Person : NSObject <NSCoding>

@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;

- (instancetype)initWithName:(NSString *)name age:(NSInteger)age;

@end

@implementation Person

- (instancetype)initWithName:(NSString *)name age:(NSInteger)age {
    self = [super init];
    if (self) {
        _name = name;
        _age = age;
    }
    return self;
}

// 归档方法
- (void)encodeWithCoder:(NSCoder *)aCoder {
    [aCoder encodeObject:self.name forKey:@"name"];
    [aCoder encodeInteger:self.age forKey:@"age"];
}

// 解档方法
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    NSString *name = [aDecoder decodeObjectForKey:@"name"];
    NSInteger age = [aDecoder decodeIntegerForKey:@"age"];
    return [[self class] initWithName:name age:age];
}

@end

在上述代码中,Person类定义了nameage两个属性,并实现了NSCoding协议的两个方法。encodeWithCoder:方法将nameage属性编码到编码器中,initWithCoder:方法从解码器中读取属性并初始化对象。

接下来是归档和解档的使用示例:

// 归档
Person *person = [[Person alloc] initWithName:@"Alice" age:30];
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *archivePath = [documentsDirectory stringByAppendingPathComponent:@"Person.archive"];
NSData *archiveData = [NSKeyedArchiver archivedDataWithRootObject:person];
[archiveData writeToFile:archivePath atomically:YES];

// 解档
NSData *readArchiveData = [NSData dataWithContentsOfFile:archivePath];
Person *readPerson = [NSKeyedUnarchiver unarchiveObjectWithData:readArchiveData];
if (readPerson) {
    NSLog(@"Name: %@, Age: %ld", readPerson.name, (long)readPerson.age);
} else {
    NSLog(@"Failed to unarchive Person");
}

在归档部分,首先创建一个Person对象,然后获取文档目录路径并构建归档文件路径archivePath。通过NSKeyedArchiverarchivedDataWithRootObject:方法将Person对象转换为NSData,再将NSData写入文件。在解档部分,从文件中读取NSData,然后使用NSKeyedUnarchiverunarchiveObjectWithData:方法将NSData还原为Person对象,并打印对象的属性。

四、Objective - C 操作 SQLite 数据库

4.1 SQLite 简介

SQLite 是一种轻量级的关系型数据库,它以文件形式存在,无需单独的服务器进程。在 iOS 开发中,SQLite 被广泛用于本地数据存储,尤其是在需要复杂数据结构和查询操作的场景下。

4.2 引入 SQLite 库

在 Objective - C 项目中使用 SQLite,需要先引入 SQLite 库。在 Xcode 中,可以通过以下步骤引入:

  1. 选择项目导航栏中的项目。
  2. 在“Build Phases”选项卡中,展开“Link Binary With Libraries”。
  3. 点击“+”按钮,搜索并添加libsqlite3.dylib库。

4.3 基本数据库操作示例

以下是使用 Objective - C 进行 SQLite 数据库基本操作(创建表、插入数据、查询数据)的示例代码:

#import <sqlite3.h>
#import <Foundation/Foundation.h>

// 数据库文件路径
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *databasePath = [documentsDirectory stringByAppendingPathComponent:@"MyDatabase.db"];

// 打开数据库
sqlite3 *database;
if (sqlite3_open([databasePath UTF8String], &database) != SQLITE_OK) {
    NSLog(@"Failed to open database");
    return;
}

// 创建表
const char *createTableSQL = "CREATE TABLE IF NOT EXISTS Users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER)";
char *errorMsg;
if (sqlite3_exec(database, createTableSQL, NULL, NULL, &errorMsg) != SQLITE_OK) {
    NSLog(@"Failed to create table: %s", errorMsg);
    sqlite3_free(errorMsg);
}

// 插入数据
const char *insertSQL = "INSERT INTO Users (name, age) VALUES (?,?)";
sqlite3_stmt *insertStmt;
if (sqlite3_prepare_v2(database, insertSQL, -1, &insertStmt, NULL) == SQLITE_OK) {
    const char *name = "Bob";
    int age = 25;
    sqlite3_bind_text(insertStmt, 1, name, -1, SQLITE_TRANSIENT);
    sqlite3_bind_int(insertStmt, 2, age);
    if (sqlite3_step(insertStmt) != SQLITE_DONE) {
        NSLog(@"Failed to insert data");
    }
    sqlite3_finalize(insertStmt);
}

// 查询数据
const char *selectSQL = "SELECT id, name, age FROM Users";
sqlite3_stmt *selectStmt;
if (sqlite3_prepare_v2(database, selectSQL, -1, &selectStmt, NULL) == SQLITE_OK) {
    while (sqlite3_step(selectStmt) == SQLITE_ROW) {
        int id = sqlite3_column_int(selectStmt, 0);
        const char *name = (const char *)sqlite3_column_text(selectStmt, 1);
        int age = sqlite3_column_int(selectStmt, 2);
        NSLog(@"ID: %d, Name: %s, Age: %d", id, name, age);
    }
    sqlite3_finalize(selectStmt);
}

// 关闭数据库
sqlite3_close(database);

在上述代码中,首先获取数据库文件路径,然后使用sqlite3_open函数打开数据库。接着使用sqlite3_exec函数执行 SQL 语句创建表,如果创建失败则打印错误信息。插入数据时,先使用sqlite3_prepare_v2函数准备 SQL 语句,然后使用sqlite3_bind_textsqlite3_bind_int函数绑定参数,最后使用sqlite3_step函数执行插入操作。查询数据时,同样准备 SQL 语句,通过sqlite3_step函数遍历结果集,使用sqlite3_column_intsqlite3_column_text函数获取列的值。最后,使用sqlite3_close函数关闭数据库。

五、Objective - C 与 Core Data

5.1 Core Data 框架概述

Core Data 是 iOS 开发中用于数据持久化和管理的强大框架。它提供了对象 - 关系映射(ORM)功能,允许开发者以面向对象的方式操作数据,而无需编写大量的 SQL 语句。Core Data 管理对象模型、数据存储协调器以及对象上下文,使得数据的增删改查操作更加直观和易于维护。

5.2 创建 Core Data 项目

在 Xcode 中创建新项目时,可以选择启用 Core Data。在项目设置中,会自动生成一个.xcdatamodeld文件,用于定义数据模型。在数据模型编辑器中,可以创建实体(类似于数据库中的表)、属性(类似于表中的列)以及实体之间的关系。

5.3 基本 Core Data 操作示例

假设我们在数据模型中定义了一个名为Book的实体,有title(字符串类型)和author(字符串类型)两个属性。以下是使用 Objective - C 进行 Core Data 基本操作(插入、查询、更新、删除)的示例代码:

#import "AppDelegate.h"
#import "Book+CoreDataClass.h"

@interface AppDelegate ()

@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;

@end

@implementation AppDelegate

// 获取应用程序的持久化容器
- (NSPersistentContainer *)persistentContainer {
    static NSPersistentContainer *persistentContainer = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        persistentContainer = [[NSPersistentContainer alloc] initWithName:@"MyCoreDataModel"];
        [persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
            if (error != nil) {
                NSLog(@"Unresolved error %@, %@", error, error.userInfo);
            }
        }];
    });
    return persistentContainer;
}

// 插入数据
- (void)insertBookWithTitle:(NSString *)title author:(NSString *)author {
    NSManagedObjectContext *context = self.persistentContainer.viewContext;
    Book *book = [NSEntityDescription insertNewObjectForEntityForName:@"Book" inManagedObjectContext:context];
    book.title = title;
    book.author = author;
    NSError *error;
    if (![context save:&error]) {
        NSLog(@"Failed to save: %@", error);
    }
}

// 查询数据
- (NSArray<Book *> *)fetchAllBooks {
    NSManagedObjectContext *context = self.persistentContainer.viewContext;
    NSFetchRequest *fetchRequest = [Book fetchRequest];
    NSError *error;
    NSArray<Book *> *books = [context executeFetchRequest:fetchRequest error:&error];
    if (error) {
        NSLog(@"Failed to fetch: %@", error);
        return nil;
    }
    return books;
}

// 更新数据
- (void)updateBook:(Book *)book withTitle:(NSString *)title author:(NSString *)author {
    NSManagedObjectContext *context = self.persistentContainer.viewContext;
    book.title = title;
    book.author = author;
    NSError *error;
    if (![context save:&error]) {
        NSLog(@"Failed to save: %@", error);
    }
}

// 删除数据
- (void)deleteBook:(Book *)book {
    NSManagedObjectContext *context = self.persistentContainer.viewContext;
    [context deleteObject:book];
    NSError *error;
    if (![context save:&error]) {
        NSLog(@"Failed to save: %@", error);
    }
}

@end

在上述代码中,persistentContainer方法用于获取应用程序的持久化容器,它负责管理数据模型、数据存储协调器和对象上下文。insertBookWithTitle:author:方法用于插入新的Book对象,首先获取对象上下文,然后使用NSEntityDescriptioninsertNewObjectForEntityForName:inManagedObjectContext:方法创建新的Book对象,并设置其属性,最后通过对象上下文的save:方法保存更改。fetchAllBooks方法用于查询所有的Book对象,通过创建NSFetchRequest并执行它来获取结果。updateBook:withTitle:author:方法用于更新Book对象的属性,同样通过对象上下文保存更改。deleteBook:方法用于删除Book对象,调用对象上下文的deleteObject:方法并保存更改。

通过以上内容,详细介绍了 Objective - C 在 iOS 数据存储与管理中的多种应用方式,开发者可以根据具体的需求和场景选择合适的数据存储方式来构建功能强大且稳定的 iOS 应用。在实际开发中,还需要考虑数据的安全性、性能优化以及数据迁移等问题,以确保应用的质量和用户体验。