IOS data storage

Time:2022-5-5

IOS data storage

https://www.jianshu.com/p/19fdbf81a086

How to store data

  • 1. Documents

  • 3、NSUserDefaults

  • 2. Database

file

  • 1. Sandbox

  • 2、Plist

  • 3. Nskeyedarchiver archiving / nskeyedunarchiver de archiving

NSUserDefaults

database

  • 1、SQLite3

  • 2、FMDB

  • 3、Core Data

file

Sandbox

The data stored in IOS localization is saved in a sandbox.

  • Documents: iTunes will back up the directory. It is generally used to store data that needs to be persisted.

  • Library / caches: cache, iTunes will not back up this directory. When the memory is insufficient, it will be cleared. When the application is not running, it may be cleared,. Generally, it stores non important data with large volume and does not need to be backed up.

  • Library / preference: iTunes will also back up this directory, which can be used to store some preferences.

  • TMP: iTunes will not back up this directory to save temporary data. The data in this directory will be cleared when the application exits.

Get sandbox file:

//Gets the path to the documents directory

NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;

//Get the path of the filename file in the document directory

NSString *filePath = [documentPath stringByAppendingPathComponent:fileName];

//Get library / caches directory path

NSString *path = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;

//Get the filename file path under the library / caches directory

NSString *filePath = [path stringByAppendingPathComponent:fileName];

//Get temp path

NSString *tmp = NSTemporaryDirectory();

//Get the path of filename file under temp

NSString *filePath = [tmp stringByAppendingPathComponent:fileName];

Of which:

  • Nsdocumentdirectory: the first parameter represents which file to find. It is an enumeration.

  • Nsuserdomainmask: also an enumeration, indicating that the search scope is limited to the sandbox directory of the current application..

  • Yes (expandtilde): the third parameter is a bool value. The full write form of the home directory in IOS is / user / username. If you fill in yes for this parameter, it means full write. If you fill in no, it means’ ~ ‘.

plist

You can write a dictionary or array directly to a file. In addition, for nsstring, nsdata, nsnumber and other types, you can also use the writetofile: atomically: method to directly write the object to the file, but the type is empty.

storage

//Data to save

NSDictionary *dict = [NSDictionary dictionaryWithObject:@”iOS cookbook” forKey:@”bookName”];

//Get path

NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;

NSString *filePath = [documentPath stringByAppendingPathComponent:@”test.plist”];

//Write data

[dict writeToFile:filePath atomically:YES];

read

//File path

NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;

NSString *filePath = [documentPath stringByAppendingPathComponent:@”test.plist”];

//Parse data

NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:filePath];

NSString *result = dict[@”bookName”];

NSLog(@”%@”, result);

Nskeyedarchiver archiving / nskeyedunarchiver de archiving

Three usage scenarios of nskeyedarchiver:

  • 1. Archive / de file a single simple object.

//Get archive file path

NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;

NSString *filePath = [documentPath stringByAppendingPathComponent:@”knight”];

//Archive the string @ “test” and write it to filepath

[NSKeyedArchiver archiveRootObject:@”test” toFile:filePath];

//Unpack the data according to the path filepath of the saved data

NSString *result = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];

//Print archived data

NSLog(@”%@”,result);

  • 2. Archive / de file multiple objects

//Get archive path

NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;

NSString *filePath = [documentPath stringByAppendingPathComponent:@”test”];

//Nsmutabledata used to host data

NSMutableData *data = [[NSMutableData alloc] init];

//Archive object

NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];

//Three data to be saved

NSString *name = @”jack”;

int age = 18;

double height = 1.80;

//Using encodeobject: method to archive data

[archiver encodeObject:name forKey:@”name”];

[archiver encodeInt:age forKey:@”age”];

[archiver encodeDouble:height forKey:@”height”];

//End archiving

[archiver finishEncoding];

//Write data (store data)

[data writeToFile:filePath atomically:YES];

//Nsmutabledata is used to carry the data from de filing

NSMutableData *resultData = [[NSMutableData alloc] initWithContentsOfFile:filePath];

//De filing object

NSKeyedUnarchiver *unArchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:resultData];

//Separate three data files

NSString *resultName = [unArchiver decodeObjectForKey:@”name”];

int resultAge = [unArchiver decodeIntForKey:@”age”];

double resultHeight = [unArchiver decodeDoubleForKey:@”height”];

//End de filing

[unArchiver finishDecoding];

//Print

NSLog(@”name = %@, age = %d, height = %.2f”, resultName, resultAge, resultHeight);

  • 3. Archive and save custom objects

    If you want to archive and unpack a custom object, you must first make the object comply with the nscoding protocol.

@interface DDAppConfigModel : NSObject <NSCoding>

@property(nonatomic, copy) NSString *paramName;

@property(nonatomic, copy) NSString *paramValue;

@property(nonatomic, copy) NSString *version;

@property(nonatomic, copy) NSString *delFlag;

@property(nonatomic, copy) NSString *type;

  • (instancetype)initWithDict:(NSDictionary *)dict;

  • (void)updateWithDict:(NSDictionary *)dict;

@end

There are two methods for nscoding protocol:

  • (void)encodeWithCoder:(NSCoder *)aCoder

This method is called when archiving, and the encodeobject: forkey: Archive variable is used in the method.

  • (instancetype)initWithCoder:(NSCoder *)aDecoder

This method is called when de filing, and the variable is read out using decodeobject: forkey in the method.

@implementation DDAppConfigModel

  • (id)initWithCoder:(NSCoder *)aDecoder

{

if (self = [super init])

{

self.paramName = [aDecoder decodeObjectForKey:@"paramName"];

self.paramValue = [aDecoder decodeObjectForKey:@"paramValue"];

self.version = [aDecoder decodeObjectForKey:@"version"];

self.delFlag = [aDecoder decodeObjectForKey:@"delFlag"];

self.type = [aDecoder decodeObjectForKey:@"type"];

}

return self;

}

  • (void)encodeWithCoder:(NSCoder *)aCoder

{

[aCoder encodeObject:self.paramName forKey:@”paramName”];

[aCoder encodeObject:self.paramValue forKey:@”paramValue”];

[aCoder encodeObject:self.version forKey:@”version”];

[aCoder encodeObject:self.delFlag forKey:@”delFlag”];

[aCoder encodeObject:self.type forKey:@”type”];

}

//Class method, using nskeyedarchiver to archive data

  • (void)saveConfig:(DDAppConfigModel *)config

{

NSString *docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;

NSString *path = [docPath stringByAppendingPathComponent:@"DDAppConfigModel.plist"];

[NSKeyedArchiver archiveRootObject:config toFile:path];

}

//Class method, using nskeyedunarchiver to unpack data

  • (DDAppConfigModel *)getConfig

{

NSString *docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;

NSString *path=[docPath stringByAppendingPathComponent:@"DDAppConfigModel.plist"];

DDAppConfigModel *config = [NSKeyedUnarchiver unarchiveObjectWithFile:path];

return config;

}

@end

NSUserDefaults

It is generally used to record some settings, such as user ID, whether the switch is on, etc. Record settings through key value pairs.

The data types that nsuserdefaults can store include: nsdata, nsstring, nsnumber, nsdate, nsarray, nsdictionary. If you want to store other types, you need to convert to the previous type before you can store them with nsuserdefaults.

//General setting data

define kSetUserDefaults(key, value) ([USER_DEFAULT setObject:value forKey:key], [USER_DEFAULT synchronize])

//Universal data acquisition

define kUserDefaults(key) [USER_DEFAULT objectForKey:key]

database

SQLite3

  • Add the library file libsqlite3 0.tbd

  • Import header file #import

  • Open database

  • Create table

  • Add, delete, modify and query

  • close database

Recommended reference:https://mp.weixin.qq.com/s?__biz=MzAwNDY1ODY2OQ==&mid=2649286361&idx=1&sn=78bbcda7f41a14291ad71289e4821f71&scene=0#rd

FMDB

Fmdb encapsulates the C language API of SQLite, which is more object-oriented.

Three classes in fmdb:

  • Fmdatabase: it can be understood as a database.

  • Fmresultset: the result set of the query.

  • Fmdatabasequeue: using multithreading, multiple queries and updates can be executed. Thread safe.

Create a database.

An SQLite database file needs to be provided when fmdatabase is created. We usually provide one DB file path.

NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:@”tmp.db”];

FMDatabase *db = [FMDatabase databaseWithPath:path];

Open database

Before interacting with the database, we need to open the database first. Use the database file operation handle DB obtained in the previous step

if (![db open])

{

db = nil;

return;

}

Create table

Use set statements to create tables.

NSString *sql = @”create table bulktest1 (id integer primary key autoincrement, x text);”

             "create table bulktest2 (id integer primary key autoincrement, y text);"

             "create table bulktest3 (id integer primary key autoincrement, z text);"

             "insert into bulktest1 (x) values ('XXX');"

             "insert into bulktest2 (y) values ('YYY');"

             "insert into bulktest3 (z) values ('ZZZ');";

success = [db executeStatements:sql];

query

ExecuteQuery is used to execute query statements and fmresultset is used to store query results.

FMResultSet *s = [db executeQuery:@”SELECT * FROM myTable”];

while ([s next]) {

//retrieve values for each record

}

The data in the result set can be extracted in the following ways.

while ([rs next])

{

if (oldIDs == nil) oldIDs = [[NSMutableArray alloc] init];

[oldIDs addObject:[rs stringForColumnIndex:0]];

}

In addition, fmresultset can retrieve data in an appropriate format through the following methods.

intForColumn:

longForColumn:

longLongIntForColumn:

boolForColumn:

doubleForColumn:

stringForColumn:

dateForColumn:

dataForColumn:

dataNoCopyForColumn:

UTF8StringForColumn:

objectForColumn:

to update

Insert, delete or update data by calling – (bool) executeupdate: (nsstring *) SQL withparameterdictionary: (nsdictionary *) arguments.

Insert data or update data.

NSString *sql = ![rs next] ? INSERT_STATEMENT : UPDATE_STATEMENT;

BOOL success = [db executeUpdate:sql withParameterDictionary:[self pr_toDBDictionary]];

if (!success)

{

STLogDBLastError(db);

result = NO;

return;

}

Delete data

BOOL success = [db executeUpdate:@”DELETE FROM STAppLog WHERE userID = ? AND appLogID = ?;”, self.userID, self.appLogID];

if (!success)

{

STLogDBLastError(db);

result = NO;

return;

}

Multithreaded operation database

Fmdatabasequeue implements the creation and management of fmdatabase.

FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath];

[queue inDatabase:^(FMDatabase *db) {

[db executeUpdate:@"INSERT INTO myTable VALUES (?)", @1];

[db executeUpdate:@"INSERT INTO myTable VALUES (?)", @2];

[db executeUpdate:@"INSERT INTO myTable VALUES (?)", @3];

FMResultSet *rs = [db executeQuery:@"select * from foo"];

while ([rs next]) {

    …

}

}];

affair

reference resources:https://blog.csdn.net/x32sky/article/details/45531229

[queue inTransaction:^(FMDatabase *db, BOOL *rollback) {

[db executeUpdate:@"INSERT INTO myTable VALUES (?)", @1];

[db executeUpdate:@"INSERT INTO myTable VALUES (?)", @2];

[db executeUpdate:@"INSERT INTO myTable VALUES (?)", @3];

if (whoopsSomethingWrongHappened) {

    *rollback = YES;

    return;

}

// etc ...

}];

close database

[db close];

Fmdatabase source code analysis

Manage the database. You need to specify a file to store data. For example: XXX / xxx / Evian db

Then, open the database by calling the following method:

  • (BOOL)openWithFlags:(int)flags vfs:(NSString *)vfsName {

if SQLITE_VERSION_NUMBER >= 3005000

if (_db) {

    return YES;

}

int err = sqlite3_open_v2([self sqlitePath], (sqlite3**)&_db, flags, [vfsName UTF8String]);

if(err != SQLITE_OK) {

    NSLog(@"error opening!: %d", err);

    return NO;

}

if (_maxBusyRetryTimeInterval > 0.0) {

    // set the handler

    [self setMaxBusyRetryTimeInterval:_maxBusyRetryTimeInterval];

}

return YES;

else

NSLog(@"openWithFlags requires SQLite 3.5");

return NO;

endif

}

VFS is the abbreviation of virtual file system. It is mainly used to unify the access of operating system files on different platforms, shield the underlying hardware media and provide a unified access interface.

We can see through SQLite3_ open_ V2 this function opens the handle of the database on the specified path:_ db.

Later, we can use this handle to access the database, such as creating a database, creating a table, inserting data, updating data, querying data, etc.

Fmresultset source code analysis

Let’s take a look at the main interface of fmdatabase:

  • (FMResultSet *)executeQuery:(NSString )sql withArgumentsInArray:(NSArray)arrayArgs orDictionary:(NSDictionary *)dictionaryArgs orVAList:(va_list)args {

    //First judge whether the handle exists, that is, the entry of the operation database

    if (![self databaseExists]) {

      return 0x00;
    

    }

    //Is the query being executed

    if (_isExecutingStatement) {

      [self warnInUse];
    
      return 0x00;
    

    }

    _isExecutingStatement = YES;

    int rc = 0x00;

    //Define an stmt to store the result set

    sqlite3_stmt *pStmt = 0x00;

    //It is mainly to destroy stmt to prevent memory leakage

    FMStatement *statement = 0x00;

    FMResultSet *rs = 0x00;

    if (_traceExecution && sql) {

      NSLog(@"%@ executeQuery: %@", self, sql);
    

    }

    if (_shouldCacheStatements) {

      statement = [self cachedStatementForQuery:sql];
    
      pStmt = statement ? [statement statement] : 0x00;
    
      [statement reset];
    

    }

    if (!pStmt) {

      rc = sqlite3_prepare_v2(_db, [sql UTF8String], -1, &pStmt, 0);
    
      if (SQLITE_OK != rc) {
    
          if (_logsErrors) {
    
              NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
    
              NSLog(@"DB Query: %@", sql);
    
              NSLog(@"DB Path: %@", _databasePath);
    
          }
    
          if (_crashOnErrors) {
    
              NSAssert(false, @"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
    
              abort();
    
          }
    
          sqlite3_finalize(pStmt);
    
          _isExecutingStatement = NO;
    
          return nil;
    
      }
    

    }

    id obj;

    int idx = 0;

    int queryCount = sqlite3_bind_parameter_count(pStmt); // pointed out by Dominic Yu (thanks!)

    // If dictionaryArgs is passed in, that means we are using sqlite’s named parameter support

    if (dictionaryArgs) {

      for (NSString *dictionaryKey in [dictionaryArgs allKeys]) {
    
          // Prefix the key with a colon.
    
          NSString *parameterName = [[NSString alloc] initWithFormat:@":%@", dictionaryKey];
    
          if (_traceExecution) {
    
              NSLog(@"%@ = %@", parameterName, [dictionaryArgs objectForKey:dictionaryKey]);
    
          }
    
          // Get the index for the parameter name.
    
          int namedIdx = sqlite3_bind_parameter_index(pStmt, [parameterName UTF8String]);
    
          FMDBRelease(parameterName);
    
          if (namedIdx > 0) {
    
              // Standard binding from here.
    
              [self bindObject:[dictionaryArgs objectForKey:dictionaryKey] toColumn:namedIdx inStatement:pStmt];
    
              // increment the binding count, so our check below works out
    
              idx++;
    
          }
    
          else {
    
              NSLog(@"Could not find index for %@", dictionaryKey);
    
          }
    
      }
    

    }

    else {

      while (idx < queryCount) {
    
          if (arrayArgs && idx < (int)[arrayArgs count]) {
    
              obj = [arrayArgs objectAtIndex:(NSUInteger)idx];
    
          }
    
          else if (args) {
    
              obj = va_arg(args, id);
    
          }
    
          else {
    
              //We ran out of arguments
    
              break;
    
          }
    
          if (_traceExecution) {
    
              if ([obj isKindOfClass:[NSData class]]) {
    
                  NSLog(@"data: %ld bytes", (unsigned long)[(NSData*)obj length]);
    
              }
    
              else {
    
                  NSLog(@"obj: %@", obj);
    
              }
    
          }
    
          idx++;
    
          [self bindObject:obj toColumn:idx inStatement:pStmt];
    
      }
    

    }

    if (idx != queryCount) {

      NSLog(@"Error: the bind count is not correct for the # of variables (executeQuery)");
    
      sqlite3_finalize(pStmt);
    
      _isExecutingStatement = NO;
    
      return nil;
    

    }

    FMDBRetain(statement); // to balance the release below

    if (!statement) {

      statement = [[FMStatement alloc] init];
    
      [statement setStatement:pStmt];
    
      if (_shouldCacheStatements && sql) {
    
          [self setCachedStatement:statement forQuery:sql];
    
      }
    

    }

    // the statement gets closed in rs’s dealloc or [rs close];

    rs = [FMResultSet resultSetWithStatement:statement usingParentDatabase:self];

    [rs setQuery:sql];

    NSValue *openResultSet = [NSValue valueWithNonretainedObject:rs];

    [_openResultSets addObject:openResultSet];

    [statement setUseCount:[statement useCount] + 1];

    FMDBRelease(statement);

    _isExecutingStatement = NO;

    return rs;

}

Fmdatabasequeue source code analysis

  • (void)inDatabase:(void (^)(FMDatabase *db))block {

ifndef NDEBUG

/* Get the currently executing queue (which should probably be nil, but in theory could be another DB queue

 * and then check it against self to make sure we're not about to deadlock. */

FMDatabaseQueue *currentSyncQueue = (__bridge id)dispatch_get_specific(kDispatchQueueSpecificKey);

assert(currentSyncQueue != self && "inDatabase: was called reentrantly on the same queue, which would lead to a deadlock");

endif

FMDBRetain(self);

dispatch_sync(_queue, ^() {

    FMDatabase *db = [self database];

    block(db);

    if ([db hasOpenResultSets]) {

        NSLog(@"Warning: there is at least one open result set around after performing [FMDatabaseQueue inDatabase:]");

if defined(DEBUG) && DEBUG

        NSSet *openSetCopy = FMDBReturnAutoreleased([[db valueForKey:@"_openResultSets"] copy]);

        for (NSValue *rsInWrappedInATastyValueMeal in openSetCopy) {

            FMResultSet *rs = (FMResultSet *)[rsInWrappedInATastyValueMeal pointerValue];

            NSLog(@"query: '%@'", [rs query]);

        }

endif

    }

});

FMDBRelease(self);

}

How to use it? Please see the following call:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

//1. Query all logs.

NSArray *arrLog = [self dd_fetchAppLogs];

});

  • (NSArray *)dd_fetchAppLogs

{

__block NSMutableArray *appLogs = nil;

WSELF;

[[FMDatabaseQueue sharedInstance] inDatabase:^(FMDatabase *db) {

    SSELF;

    FMResultSet *rs = [db executeQuery:@"SELECT * FROM STAppLog;"];

    @onExit {

        [rs close];

    };

    if (rs == nil)

    {

        STLogDBLastError(db);

        return;

    }

    while ([rs next])

    {

        @autoreleasepool {

            if (appLogs == nil) appLogs = [[NSMutableArray alloc] init];

            STAppLog *appLog = [self mj_objectWithKeyValues:rs.resultDictionary];

            [appLogs addObject:appLog];

        }

    }

}];

return appLogs;

}

From the source code, we can know that the query operation will execute the query operation of the sub thread in the queue created in fmdb.

CoreData

reference resources:https://www.jianshu.com/p/d9ee92cd3483

Create database table

Create a table structure in the mesasqlite designer, and then copy the generated SQL for use. This can avoid errors caused by tapping the code by hand.

Save the SQL as a file and put it into the Xcode project.

coreData.png

Data model management class nsmanagedobjectmodel

Through nsmanagedobjectmodel, you can read the created data model file as a model management class object. An entity is similar to a table structure in a database.

Persistent storage coordinator class nspersientstorecoordinator

Nspersistent storecoordinator establishes the relationship between the data model and the local file or database, and reads the local data into memory or persistently saves the modified temporary data.

Data object management context nsmanagedobjectcontext

Nsmanagedobjectcontext is the core class for data management. We use this class to add, delete, modify and query data.

How to use

Generally, we need to create a XXX in the project Xcdatamodeld file. Then create a table in this file

coreData1.png

Create an nsmanagedobjectmodel object

First, we need to know where the data model file is and load the nsmanagedobjectmodel through the data model file.

  • (NSManagedObjectModel *)managedObjectModel

{

if (_managedObjectModel != nil) {

    return _managedObjectModel;

}

NSURL *modelURL = [DDBITrackKitBUNDLE URLForResource:@"Model" withExtension:@"momd"];

_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];

return _managedObjectModel;

}

Create nspersistent storecoordinator object

The managedobjectmodel object is needed to create the persistentstorecoordinator object. Persistent local storage is created according to the model structure of the objectmodel. At the same time, the persistentstorecoordinator object needs to know where the database file is in order to open a new database.

  • (NSPersistentStoreCoordinator *)persistentStoreCoordinator

{

if (_persistentStoreCoordinator != nil) {

    return _persistentStoreCoordinator;

}

//Specify a database file path

NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"DDBITrackKit.sqlite"];

NSError *error = nil;

//Create the nspersistent storecoordinator object through the managedobjectmodel object.

_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];

//Specify that the underlying storage method is SQLite database.

if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {

    //

    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);

    abort();

}

return _persistentStoreCoordinator;

}

Create the nsmanagedobjectcontext object.

Object handles are needed to manipulate data, and each handle manages the corresponding objectmodel object. Generally, we will create a main handle. The child handle is then used to perform multithreaded operations.

It should be noted that the clause handles created by each thread cannot communicate directly with each other. As a result, they may crash, fail to query data, or query wrong data. If communication is required, the objectid of the entity needs to be passed to another thread, and then the context in this thread executes the corresponding query, and then changes the data of the queried object. Quite cumbersome.

When saving data. You can call the save: function of the sub handle, and then get the notification of this data change in our notification center.

Create master handle

  • (NSManagedObjectContext *)mainManagedObjectContext

{

if (_mainManagedObjectContext != nil) {

    return _mainManagedObjectContext;

}

_mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];

_mainManagedObjectContext.persistentStoreCoordinator = [self persistentStoreCoordinator];

return _mainManagedObjectContext;

}

Create a multithreaded child handle

  • (NSManagedObjectContext *)privateContext

{

//Setting up a persistent store coordinator and two independent contexts has proved to be a good way to process core data in the background.

NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];

context.persistentStoreCoordinator = [self persistentStoreCoordinator];

return context;

}

Create notifications of data changes.

[[NSNotificationCenter defaultCenter] addObserverForName:NSManagedObjectContextDidSaveNotification object:nil queue:nil usingBlock:^(NSNotification *note) {

    NSManagedObjectContext *moc = self.mainManagedObjectContext;

    if (note.object != moc) {

        [moc performBlock:^{

            [moc mergeChangesFromContextDidSaveNotification:note];

        }];

    }

}];

Query data

Query the required entity object through nsentitydescription.

  • (instancetype)findOrCreateWithIdentifier:(id)identifier inContext:(NSManagedObjectContext *)context

{

id object = nil;

NSString *entityName = NSStringFromClass(self);

if (identifier)

{

    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:entityName];

    fetchRequest.predicate = [NSPredicate predicateWithFormat:@"appLogID = %@", identifier];

    fetchRequest.fetchLimit = 1;

    fetchRequest.returnsObjectsAsFaults = NO;

    NSError *error = nil;

    id object = [[context executeFetchRequest:fetchRequest error:&error] lastObject];

    if (object == nil) {

        object = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:context];

    }

}

else

{

    object = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:context];

}

return object;

}

Or:

  • (NSArray *)dd_fetchAllAppLog

{

NSManagedObjectContext *context = [DatabaseHelper shareInstance].store.privateContext;

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

[request setReturnsObjectsAsFaults:NO];

NSError *error;

NSArray *arrResult = [context executeFetchRequest:request error:&error];

return arrResult;

}

Update data

First query the object, then update the fields of the object response, and finally call the save function to save

  • (void)dd_saveOrUpdateWithContext:(NSManagedObjectContext *)localContext completion:(DatabaseCompletion)completion

{

DDBILog *tAppLog = [DDBILog findOrCreateWithIdentifier:self.appLogID inContext:localContext];

tAppLog.accuracy         = self.accuracy;

[localContext save:NULL];

if (completion) {

    completion(YES, nil);

}

}

delete object

  • (void)dd_delete:(DatabaseCompletion)completion

{

NSManagedObjectContext *localContext = [DatabaseHelper shareInstance].store.privateContext;

[self dd_deleteWithContext:localContext completion:completion];

}

  • (void)dd_deleteWithContext:(NSManagedObjectContext *)context completion:(DatabaseCompletion)completion

{

[self MR_deleteEntityInContext:context];

[context save:NULL];

}

Internal implementation of MR

  • (BOOL) MR_deleteEntityInContext:(NSManagedObjectContext *)context

{

NSError *error = nil;

NSManagedObject *entityInContext = [context existingObjectWithID:[self objectID] error:&error];

[MagicalRecord handleErrors:error];

if (entityInContext) {

    [context deleteObject:entityInContext];

}

return YES;

}

It can be seen from here that the data transferred between threads is the corresponding entity queried through the objectid. Then call the deleteobject function of context to delete this entity.

Author: Lai hongquyan

Link:https://www.jianshu.com/p/19fdbf81a086

Source: developeppaper

The copyright belongs to the author. For commercial reprint, please contact the author for authorization. For non-commercial reprint, please indicate the source.

Recommended Today

Modify user information changeinfo

When judging the persistence layer: Problem: there is such a problem when modifying user information. For example: the user’s email is not required. It was not empty originally. At this time, the user deletes the mailbox information and submits it. At this time, if it is not empty to judge whether it needs to be […]