Location>code7788 >text

iOS Development Fundamentals 135-Core Data

Popularity:568 ℃/2024-07-22 14:59:28

Using Core Data in Objective-C (OC) is an effective tool for managing model-level objects in iOS app development.Core Data uses ORM (Object-Relational Mapping) techniques to abstract and manage data. This not only saves time, but also reduces programming errors. The following is a detailed introduction to using Core Data, including sample code and some analysis that goes deeper into the underlying layers.

basic concept

  1. Persistence Container (NSPersistentContainer): Introduced in iOS 10, it encapsulates the settings of the Core Data stack, including the Managed Object Model (NSManagedObjectModel), the persistent storage coordinator (NSPersistentStoreCoordinator), and context (NSManagedObjectContext)。

  2. Hosted Object Model (NSManagedObjectModel): Describes the data model of the application, including entities (Entity) and the relationships between those entities.

  3. Persistent Storage Coordinator (NSPersistentStoreCoordinator): Responsible for coordinating managed object contexts and persistent storage.

  4. Context (NSManagedObjectContext): Used to manage objects in memory. When creating, reading, updating, and deleting operations are performed, these changes temporarily occur only in the context until the changes are saved to the persistence layer.

usage example

The following is a simple example of creating and querying objects using Core Data:

Step 1: Configure the Data Model

First, create the data model file through Xcode's Data Model Editor (.xcdatamodeld). Assuming that a definition ofPerson Entity withname cap (a poem)age Two attributes.

Step 2: Setting Up the Persistence Container

Set the persistence container in the AppDelegate:

#import <CoreData/>

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (readonly, strong) NSPersistentContainer *persistentContainer;

- (void)saveContext;

@end

@implementation AppDelegate

@synthesize persistentContainer = _persistentContainer;

// lazy loading (computing) persistentContainer
- (NSPersistentContainer *)persistentContainer {
    // If the container has been initialized,Direct return
    if (_persistentContainer != nil) {
        return _persistentContainer;
    }
    
    // Use the name MyModel model file to create the container
    _persistentContainer = [[NSPersistentContainer alloc] initWithName:@"MyModel"];
    [_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
        if (error != nil) {
            // error handling,实际应用中应该替换为更合适的error handling
            NSLog(@"Unresolved error %@, %@", error, );
            abort();
        }
    }];
    return _persistentContainer;
}
@end

Step 3: Add and Query with Core Data

Add and query data in the right places (e.g. ViewController):

#import ""
#import <CoreData/>

- (void)insertNewPersonWithName:(NSString *)name age:(int)age {
    AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    NSManagedObjectContext *context = ;
    
    // Create a new Person physical object
    NSManagedObject *newPerson = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:context];
    [newPerson setValue:name forKey:@"name"];
    [newPerson setValue:@(age) forKey:@"age"];
    
    NSError *error = nil;
    // Save to persistence layer
    if (![context save:&error]) {
        NSLog(@"fail to save: %@, %@", error, );
    }
}

- (NSArray *)fetchPersons {
    AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    NSManagedObjectContext *context = ;
    
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Person"];
    
    NSError *error = nil;
    NSArray *results = [context executeFetchRequest:fetchRequest error:&error];
    if (!results) {
        NSLog(@"Inquiry Failure: %@, %@", error, );
    }
    return results;
}

in-depth analysis

Core Data's underlying layer uses SQLite as the default persistence method (although you can choose in-memory or custom solutions), but developers don't need to interact directly with the database; all operations are done through the objects and APIs described above. the Core Data framework is responsible for converting these operations into SQLite commands and executing them.

Core Data Performance Optimization

  • Batch request. iOS 8 introduces bulk deletes and updates so that operations can be performed directly in the persistence layer without loading data into memory, greatly improving efficiency.

  • Pre-acquisition. For frequently accessed associated objects, prefetching can be used to reduce the number of queries.

  • Lightweight Migration. For data model changes, manually handling data structure changes is avoided through lightweight migration.

seal inside

For Core Data usage, secondary encapsulation can improve code reusability and make external calls more concise. We can create a singleton classCoreDataManagerto manage common Core Data operations such as additions, deletions, and modifications.

First, you need to make sure that your data model (.xcdatamodeld file) is set up, for example, here let's say we have aPersonof Entity, which has two properties:name(String type) andage(Int16 type).

Step 1: Create Core Data Management Class

#import <Foundation/>
#import <CoreData/>

@interface CoreDataManager : NSObject

@property (readonly, strong) NSPersistentContainer *persistentContainer;
+ (instancetype)sharedManager;
- (void)saveContext;
- (void)insertPersonWithName:(NSString *)name age:(NSNumber *)age completion:(void(^)(BOOL success, NSError *error))completion;
- (void)fetchAllPersons:(void(^)(NSArray *persons, NSError *error))completion;

@end

@implementation CoreDataManager

+ (instancetype)sharedManager {
    static CoreDataManager *sharedManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedManager = [[self alloc] init];
    });
    return sharedManager;
}

- (NSPersistentContainer *)persistentContainer {
    @synchronized (self) {
        if (_persistentContainer == nil) {
            _persistentContainer = [[NSPersistentContainer alloc] initWithName:@"YourModelName"];
            [_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
                if (error != nil) {
                    NSLog(@"Unresolved error %@, %@", error, );
                    abort();
                }
            }];
        }
    }
    return _persistentContainer;
}

- (void)saveContext {
    NSManagedObjectContext *context = ;
    NSError *error = nil;
    if ([context hasChanges] && ![context save:&error]) {
        NSLog(@"Unresolved error %@, %@", error, );
        abort();
    }
}

- (void)insertPersonWithName:(NSString *)name age:(NSNumber *)age completion:(void(^)(BOOL success, NSError *error))completion {
    NSManagedObjectContext *context = ;
    NSManagedObject *newPerson = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:context];
    [newPerson setValue:name forKey:@"name"];
    [newPerson setValue:age forKey:@"age"];
    
    NSError *error = nil;
    if (![context save:&error]) {
        NSLog(@"Error saving context: %@, %@", error, );
        completion(NO, error);
    } else {
        completion(YES, nil);
    }
}

- (void)fetchAllPersons:(void(^)(NSArray *persons, NSError *error))completion {
    NSManagedObjectContext *context = ;
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Person"];
    
    NSError *error = nil;
    NSArray *results = [context executeFetchRequest:fetchRequest error:&error];
    if (error) {
        NSLog(@"Failed to fetch persons: %@, %@", error, );
        completion(nil, error);
    } else {
        completion(results, nil);
    }
}

@end

Using the encapsulated CoreDataManager

This shows how to use theCoreDataManagerPerform data manipulation:

// Insert newPersonboyfriend
[[CoreDataManager sharedManager] insertPersonWithName:@"John Doe" age:@25 completion:^(BOOL success, NSError *error) {
    if (success) {
        NSLog(@"Person added successfully");
    } else {
        NSLog(@"Failed to add person: %@", );
    }
}];

// Get all thePersonboyfriend
[[CoreDataManager sharedManager] fetchAllPersons:^(NSArray * _Nonnull persons, NSError * _Nonnull error) {
    if (error) {
        NSLog(@"Failed to fetch persons: %@", );
    } else {
        for (NSManagedObject *person in persons) {
            NSString *name = [person valueForKey:@"name"];
            NSNumber *age = [person valueForKey:@"age"];
            NSLog(@"Fetched person: %@, age: %@", name, age);
        }
    }
}];

With the above encapsulation, we can accomplish this by calling simple methods on thePersonObjects of the addition, deletion, modification and checking operations , without having to care about the specific implementation details of Core Data. This greatly improves the readability and maintainability of the code.

summarize

Core Data is a powerful framework that makes data management easier by encapsulating complex underlying details. To use Core Data effectively, you must understand the principles behind it and follow best practices in designing your application.