The original link

Students with development experience should be clear, whether front-end, client some data need to be local landing; In this chapter, we will take a look at the common methods of iOS data access.

Before we get into the details of iOS data, we need to understand the concepts of bundles and sandboxes.

Bundles and sandboxes

Bundles and sandboxes are specific places where iOS data is stored;

1. Bundle

A Bundle is a directory that contains the resources that your program will use. These resources contain such things as images, sounds, compiled code, NIB files, etc

The path to use pathForResource is the path under the APP in the real environment

 // Load the info.plist resources. MainBundle resources are placed under the RootFolder
    NSString *infoPath= [[NSBundle mainBundle] pathForResource:@"Info" ofType:@"plist"];
    NSLog(@"Info path: %@",infoPath);
    // Find the image path
    NSString *imgPath = [[NSBundle mainBundle] pathForResource:@"ff_IconAdd_25x25_@2x" ofType:@"png"];
    NSLog(@"Image path: %@",imgPath);

    UIImageView *img = [[UIImageView alloc] initWithFrame:CGRectMake(100.100.100.100)];
    img.image = [UIImage imageWithContentsOfFile:imgPath];

    [self.view addSubview:img];

Copy the code

The output is:

Info Path: /Users/xxx/Library/Developer/CoreSimulator/Devices/8EA1B565-17F4-47FF-8ECA-C23F3EF4594A/data/Containers/Bundle/Applicati On /76750996-9428-448E-8662-553CA22DCB28/NSBundle /Users/xxxx/Library/Developer/CoreSimulator/Devices/8EA1B565-17F4-47FF-8ECA-C23F3EF4594A/data/Containers/Bundle/Applicat Ion /76750996-9428-448E-8662-553CA22DCB28/NSBundle use. app/[email protected]Copy the code

Other common methods


// Get the XML file
NSString *filePath = [[NSBundle mainBundle] pathForResouse:@"re" ofType:@"xml"];
NSData *data = [[NSData alloc] initWithContentsOfFile:filePath];
// Get the path to the app package readme. TXT file
NSString *path = [[NSBundle mainBundle] pathForResource:@"readme" ofType:@"txt"];


// App resource directory path
NSString *resPath = [[NSBundle mainBundle] resourcePath];

// Get the app package path
NSString *path = [[NSBundle mainBundle] bundlePath];

// Get the program's main bundle by using the following method
NSBundle *otherBundle = [NSBundle mainBundle];

// Get a.bundle from the resource directory
NSString *path = [resPath stringByAppendingPathComponent:@"a.bundle"];
NSBundle *bundle = [NSBundle bundleWithPath:path];

// Once we have the bundle, we can access the resource files in it.
NSString path = [otherBundle pathForImageResource:@"img"];
NSImage *img = [[NSImage alloc] initWithContentsOfFile:path];

// The bundle can contain a library. If we get a class from the library, the bundle connects to the library and looks for that class:
Class newClass = [otherBundle classNamed:@"Person"];
id person = [[newClass alloc] init];

// If you do not know the name of the class, you can also find it by looking for the main class
Class aClass = [otherBundle principalClass];
id classInstance = [[aClass alloc] init];

// As you can see, NSBundle has many uses. In this chapter, NSBundle is responsible (in the background) for loading nib files. We can also load niB files using NSBundle instead of using NSWindowController:
BOOL flag = [NSBundle loadNibNamed:@"ViewController" owner:someObject];
// Note: we specify an object someObject as the File "s Owner of niB

// Get the property list
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"ViewControllers" ofType:@"plist"]] -- -- -- -- -- -- -- --Copy the code

Obtain mainBundle content information

NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]; / / version version number nsstrings * version2 = [[NSBundle mainBundle] objectForInfoDictionaryKey: @ "CFBundleShortVersionString"]. // The application identifier: Bundle identifier NSString *bundleId = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"]; //Bundle name NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"]; / / the name of the application show nsstrings * appName = [[NSBundle mainBundle] objectForInfoDictionaryKey: @ "CFBundleDisplayName"]. #define XLocalizedString(key, comment)[[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:nil]Copy the code

2. The sandbox

Each IOS APP runs inside a sandbox to ensure it doesn’t tamper with or read information from other apps.

Data Container contains the following three directories:

  • Documents: Some important data generated by the application at runtime that needs to be stored for a long time is stored in this file.

  • Library:

    • Cache: Stores cached files, such as data or data downloaded from the Internet. It is used to store non-important data that needs to be used for a long time and has a large amount of data. iTunes
    • Prefrence: Saves all the preferences of the app, such as accounts, Settings, etc. Automatically managed by the system
  • TMP: saves temporary data files generated during application running. Files in this file may be deleted when the mobile phone is restarted, the system space is insufficient, or the application is closed

3. Sandbox path access

  • Sandbox entrance

    NSLog(@"%@",NSHomeDirectory());

  • Sandbox TMP directory

    NSLog(@"%@",NSTemporaryDirectory());

  • Sandbox Documents directory

// 1: splicing mode
NSLog(@"% @",[NSHomeDirectory() stringByAppendingPathComponent:@"Documents"]);
// Sandbox Documents directory 2: Recommended way to use
NSLog(@"% @",[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]);
Copy the code
  • Sandbox Library directory
   // 1: splicing mode
   NSLog(@"% @",[NSHomeDirectory() stringByAppendingPathComponent:@"Library"]);
   // Sandbox Library directory 2: recommended usage
   NSLog(@"% @",[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) firstObject]);
Copy the code
  • The Caches directory under sandbox Library suggests how to use them
    NSLog(@"% @",[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject]);
Copy the code
  • The Preferences directory in the Sandbox Library can only be concatenated. Library+Preferences is used here

NSLog(@"%@",[[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:@"Preferences"]);

Second, data access: PLIST

A PList file is essentially an XML file stored as a key:value.

1. Read the plist file

Read the info.plist of the system

NSDictionary *sysInfo = NSBundle.mainBundle.infoDictionary;
NSLog(@"% @",sysInfo);
NSLog(@"% @",sysInfo[@"CFBundleShortVersionString"]);

Copy the code

Custom plist file

    // You can use nsbundle. mainBundle pathForResource to read files in the project project directory
    NSString *userPath = [NSBundle.mainBundle pathForResource:@"user" ofType:@"plist"];
    NSDictionary *userDic = [NSDictionary dictionaryWithContentsOfFile:userPath];
    NSLog(@"username= %@ , password = %@",userDic[@"username"],userDic[@"password"]);

    // Parse a complex plist; Value is array
    NSString *cityPath = [NSBundle.mainBundle pathForResource:@"cityData" ofType:@"plist"];
    NSDictionary *cityDic = [NSDictionary dictionaryWithContentsOfFile:cityPath];

    NSArray *allKeys = cityDic.allKeys;
    for (int i=0; i<allKeys.count; i++) {
        NSLog(@" key = %@ , value = %@",allKeys[i],cityDic[allKeys[i]]);
    }

    // Read the data in Documents in the sandbox
    NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES)[0];
    NSString *dataPath = [path stringByAppendingPathComponent:@"data.plist"];
    NSDictionary *dataDict = [NSDictionary dictionaryWithContentsOfFile:path];
    NSLog(@"% @", dataDict);


Copy the code

Pay attention to the point

  • Plist file reception requires an NSDictionary
  • The value corresponding to the key in the PList file needs to use the appropriate type to receive data. (Tips: In the case of Array, use NSArray to receive)
  • Plist files are read using:dictionaryWithContentsOfFilemethods

3. Write the plist file

    // Store citydata.plist to Documents in the sandbox
    NSString *saveFilePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:@"city.plist"];
    [cityDic writeToFile:saveFilePath atomically:YES];

    // Write some data
    NSString *fujianPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:@"Fujian plist".];
    [cityDic[@"Fujian"] writeToFile:fujianPath atomically:YES];
Copy the code

Data access: NSUserDefaults

1. Basic Use of NSUserDefaults (log in to Demo)

NSUserDefaults Stores preferences, which do not require paths and are generally used to store information such as accounts and passwords.

/ / data
- (void)saveForPreference {

    // Get the preference object
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

    // Store data
    [defaults setObject:@"mrgao" forKey:@"name"];
    [defaults setInteger:24 forKey:@"age"];

    // Synchronous call, write to file immediately, do not write this method will be asynchronous, with delay
    [defaults synchronize];
}
/ / read the data
- (void)readForPreference {

    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

    NSString *name = [defaults objectForKey:@"name"];
    NSInteger *age = [defaults integerForKey:@"age"];

    NSLog(@"%@-------%ld",name,age);
}



Copy the code

Here’s an example of saving a user name and password:

// // viewController.m // NSBundle #import "viewController.h" @interface ViewController () @property (weak, nonatomic) IBOutlet UITextField *userName; @property (weak, nonatomic) IBOutlet UITextField *password; - (IBAction)savePassword:(id)sender; - (IBAction)login:(id)sender; @property (weak, nonatomic) IBOutlet UISwitch *remenber; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; NSDictionary *info = [self readForPreference]; self.userName.text =info[@"name"]; self.password.text =info[@"pass"]; // Dictionary type, pass Bool value, need to convert to NSNumber; Bool self.remenber.on =[info[@"isOn"] boolValue]; } // save data - (void)saveForPreference {NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSString *username = self.userName.text; NSString *password = self.password.text; // Store data [defaults setObject:username forKey:@"username"]; [defaults setObject:password forKey:@"password"]; // [defaults setInteger:24 forKey:@"age"]; // Write the method to the file immediately. If you do not write this method, you will start writing it asynchronously. } - (NSDictionary *)readForPreference {NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSString *name = [defaults valueForKey:@"username"]; NSString *pass = [defaults valueForKey:@"password"]; Boolean isOn = [defaults boolForKey:@"isOn"]; // NSInteger *age = [defaults integerForKey:@"age"]; NSLog(@"%@-------%@",name,pass); Return @{@"name":name,@"pass":pass,@"isOn": [NSNumber numberWithBool:isOn]}; } - (IBAction)savePassword:(id)sender { UISwitch *swc = (UISwitch *)sender; NSLog(@"%@", self.remenber.isOn ? @"YES" : @"NO"); NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; [defaults setBool:self.remenber.isOn forKey:@"isOn"]; [defaults synchronize]; } - (IBAction)login:(id)sender { [self saveForPreference]; } @endCopy the code

Pay attention to the point

  • NSDictionary can’t propagate a Bool directly, so you have to convert it to NSNumber and then convert it to Bool when you read it;

  • NSLog(@”%@”, boolValue? @”YES” : @”NO”);

  • Remember to call [defaults Synchronize] for storing preferences; storage

So where do we end up storing the data we saved in NSUserDefaults?

The sandbox contains a Library->Preferences Library that is used to store all the Preferences of the application, such as accounts and Preferences, and is automatically managed by the system.

Let’s find out:

Conclusion: NSUserDefaults data ends up in the Library->Preferences folder under the sandbox and is of type PList

2. Create new feature pages using NSUserDefaults

The so-called new feature page, is in a certain version of an APP, according to the time to show the feature page; I won’t show it when I open it again. For example: When king of Glory updated for the new season, the opening season introduction animation will play, when we open again this season will not play this animation.


- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
    // Override point for customization after application launch.
    self.window = [[UIWindow alloc] initWithWindowScene:(UIWindowScene *)scene];
    // Get the version of the current application
    NSString *currentVersion = NSBundle.mainBundle.infoDictionary[@"CFBundleShortVersionString"];
    // Get the local version
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSString *localVersion = [defaults valueForKey:@"version"];
    // Display the new feature page if the version is updated
    if(! [currentVersion isEqualToString:localVersion]) { UIViewController *newVC = [[UIViewController alloc] init]; newVC.view.backgroundColor = UIColor.greenColor; self.window.rootViewController= newVC;// Save the current version
        [defaults setObject:currentVersion forKey:@"version"];
        [defaults synchronize];
    }else{
        UIViewController *normalVC = [[UIViewController alloc] init];
        normalVC.view.backgroundColor = UIColor.redColor;
        self.window.rootViewController= normalVC;
    }

    [self.window makeKeyAndVisible];
}

Copy the code

Data access: archiving -NSKeyedArchiver

Archiving is generally used to save custom objects. Because plist files cannot hold custom objects. If a dictionary holds custom objects, if the dictionary is written to a file, it will not generate a PList file.

1. Basic use of NSKeyedArchiver

NSArray, NSString, NSInteger, NSDictionary and other data types are native to iOS. Archiving is supported natively.


@interface NSKeyedArchiverViewController ()
- (IBAction)archive:(id)sender;
- (IBAction)unarchive:(id)sender;
@property(strong,nonatomic) NSData *data;
@end

@implementation NSKeyedArchiverViewController

- (IBAction)archive:(id)sender {
// Data store. In this case, serialize an array
    self.data =[NSKeyedArchiver archivedDataWithRootObject:@[@"a"The @"b"The @"c"] requiringSecureCoding:YES error:nil];
}

- (IBAction)unarchive:(id)sender {
// Read the data and convert it to an array
  NSArray *array=  [NSKeyedUnarchiver unarchivedObjectOfClass:NSArray.class fromData:self.data error:nil];
    NSLog(@"% @",array);
}
@end
Copy the code

1. NSKeyedArchiver User-defined object archive

As mentioned above, NSArray, NSString, NSInteger, NSDictionary and other data types come with iOS. Archiving is supported natively, but it is different for custom objects.

If we archive our custom objects as above:

 NSError *error;
    self.userData= [NSKeyedArchiver archivedDataWithRootObject:user requiringSecureCoding:YES error:&error];
    if(error! =nil){ NSLog(@"% @",error);
    }
Copy the code

An error is guaranteed: we did not make the custom object comply with the NSSecureCoding protocol

Domain=NSCocoaErrorDomain Code=4866 "The data couldn’t be written because it isn’t in the correct format." UserInfo={NSUnderlyingError=0x6000016d0210 {Error Domain=NSCocoaErrorDomain Code=4864 "This decoder will only decode classes that adopt NSSecureCoding. Class 'User' does not adopt it." UserInfo={NSDebugDescription=This decoder will only decode classes that adopt NSSecureCoding. Class 'User' does not adopt it.}}}
Copy the code

So, we have the following steps for custom object archiving:

    1. Make custom objects implement THE NSSecureCoding protocol;
    1. Implement at least three methods in the NSSecureCoding protocol;
    • encodeWithCoder
    • initWithCoder
    • supportsSecureCoding

The array that we’ve been using has actually helped us implement encodeWithCoder and initWithCoder; So how do we write it?

#import <Foundation/ foundation. h> NS_ASSUME_NONNULL_BEGIN @interface User: NSObject<NSSecureCoding> @property(nonatomic,copy) NSString *userName; @property(nonatomic,assign) int age; @end NS_ASSUME_NONNULL_ENDCopy the code

Several methods to implement the protocol in user.m

#import "user.h" @implementation User The objects in the stream - (void) encodeWithCoder: nonnull coder NSCoder *) {/ / remember all the objects are serialized [coder encodeObject: self. The userName forKey:@"userName"]; [coder encodeInt:self.age forKey:@"age"]; } // decode, Initialize stream as object - (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder {if(self = [super init]){self.userName = [coder decodeObjectForKey:@"userName"]; self.age = [coder decodeIntForKey:@"age"]; } return self; } // Whether security encoding is supported + (BOOL)supportsSecureCoding{return YES; } @endCopy the code

Archive and file the User object:

- (IBAction)archive:(id)sender {
    // Custom objects need to be stored in a sandbox
    User *user = [[User alloc] init];
    user.userName=@"mrgaogang";
    user.age=123;
    NSError *error;
    self.userData= [NSKeyedArchiver archivedDataWithRootObject:user requiringSecureCoding:YES error:&error];
    if(error! =nil){ NSLog(@"% @",error);
    }

    // We can also sandbox archived data
    NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"User.data"];
    [self.userData writeToFile:filePath atomically:YES];
}

- (IBAction)unarchive:(id)sender {
    // Retrieve our archived data from the sandbox
    NSData *data = [NSData dataWithContentsOfFile:[NSTemporaryDirectory() stringByAppendingPathComponent:@"User.data"]];

    User *user= [NSKeyedUnarchiver unarchivedObjectOfClass:User.class fromData:data error:nil];

    NSLog(@"userName = %@ , age = %d",user.userName,user.age);

}
Copy the code
# Print status
userName = mrgaogang , age = 123
Copy the code

Five, data access: database – SQlite3

1. Brief introduction to SQLite

SQLite is an ACID compliant relational database management system contained in a relatively small C library. SQLite is embedded, has a small memory footprint, and supports Windows, Linux, and Unix operating systems. So the current Android /ios versions are supported

Data types supported by SQLite:

  • NULL: a NULL value
  • INTEGER: plastic value
  • REAL: a floating point value
  • TEXT: indicates a TEXT character string. The encoding mode is UTF-8 or UTF-16
  • BLOB: Binary file storage

Sqlite:

  • Sqlite3_open: opens the database
  • Sqlite3_close: closes the database
  • Sqlite3_exec: Executes the function
  • Sqlite3_prepare_v2: used in the query to obtain the result set
  • Sqlite3_step: used in the query to traverse the result set
  • Sqlite3_column_text: used in query to obtain the column value

2. Basic use of SQlite3 – engineering construction and custom Model

(1) Introduce sqlite3 package

Since SQlite3 doesn’t come with ios, we need additional introductions:

(2) Customize the Model

Since the operation database, the corresponding mapping must be an object, so we first construct Model:

// // person. h // SQLite database operation #import <Foundation/ foundation. h> NS_ASSUME_NONNULL_BEGIN @interface Person: NSObject @property(nonatomic,copy) NSString *name; @property(nonatomic,copy) NSString *phone; @property(nonatomic,copy) NSString *address; - (instancetype) initWithName:(NSString *) name andPhone:(NSString *) phone andAddress:(NSString *) address; @end NS_ASSUME_NONNULL_ENDCopy the code

And implement the corresponding constructor

// // person. m // SQLite database operation #import "person. h" @implementation Person - (instancetype)initWithName:(NSString *)name andPhone:(NSString *)phone andAddress:(NSString *)address{ if(self = [super init]){ self.name= name; self.phone=phone; self.address=address; } return self; } - (NSString *)description { return [NSString stringWithFormat:@"name = %@ , phone = %@, address = %@", self.name,self.phone,self.address]; } @endCopy the code

3. Sqlite operation tools (add, delete, change and check)

Since operations on the database are independent, we should not put them in UI/Controller, but in a separate file; In order to facilitate the subsequent maintenance and program development.

(1) Declare the SQL operation method externally

Six operations on the database are mainly demonstrated here:

  • Database creation
  • Table creation
  • Data addition
  • Deletion of data
  • Update of data
  • Query of data
// // dbtools. h // SQLite database operation #import <Foundation/ foundation.h > #import "person.h" ns_ASsume_nonNULl_BEGin@interface DBTools: NSObject // createDB - (void) createDB; // createTable - (void) createTable:(NSString *) tableName; // Insert data - (void) insertPerson (Person *) Person; - (void) deletePerson:(NSString *) personName; // update data - (void) updatePerson:(Person *) Person; Query all data - (NSArray<Person *> *) queryPerson; // query by condition - (NSArray<Person *> *) queryPersonByName:(NSString *) personName; @property(nonnull,copy) NSString *dbName; @end NS_ASSUME_NONNULL_ENDCopy the code

(2) Add, delete, change and check the database

// // dbtools.m // SQLite database operation #import "dbtools.h" #import "person.h" #import "sqlite3.h" @interface DBTools(){sqlite3 *sqlite; } @end@implementation DBTools // create database in sandbox - (void)createDB{// 1. Find the sandbox nsstrings * docPath = [NSSearchPathForDirectoriesInDomains (NSDocumentDirectory NSUserDomainMask, YES) firstObject]; / / 2. The splicing database nsstrings * dbPath = [docPath stringByAppendingPathComponent: [NSString stringWithFormat:@"%@.db",self.dbName]]; NSLog(@"%@",dbPath); // 3. Create a database, pass two parameters: 1: database name (c language string), 2: Int result = sqlite3_open([dbPath UTF8String], &sqLite); If (result == SQLITE_OK){NSLog(@" database created successfully "); }else{NSLog(@" database creation failed "); }} // closeDB - (void) {sqlite3_close(sqlite); ExecSQL - (void) execSQL:(NSString *) SQL {// 1. To execute, open the database [self createDB]; // create SQL statement NSLog(@"%@", SQL); char *error; Int result= sqlite3_exec(sqlite, [SQL UTF8String], NULL, NULL, &error); If (result == SQLITE_OK){NSLog(@" SQL executed successfully "); }else{NSLog(@" SQL failed, %s",error); } // 4. Be sure to close the database [self closeDB]; } - (void)createTable:(NSString *)tableName{NSString * SQL = [NSString stringWithFormat: @"create table %@(id integer primary key autoincrement, name text , phone text ,address text)",tableName]; [self execSQL: sql]; } - (void)insertPerson:(Person *) Person {NSString * SQL = [NSString stringWithFormat: @"insert into person (name , phone ,address) values ('%@','%@','%@')",person.name,person.phone,person.address]; [self execSQL: sql]; } - (void)deletePerson:(NSString *)personName{NSString * SQL = [NSString stringWithFormat: @"delete from person where name='%@'",personName]; [self execSQL: sql]; } - (void)updatePerson:(Person *) Person {NSString * SQL = [NSString stringWithFormat: @"update person set phone='%@' ,address='%@' where name='%@'",person.phone,person.address,person.name]; [self execSQL:sql]; } query all - (NSArray<Person *> *)queryPerson{[self createDB]; NSString *sql = [NSString stringWithFormat: @"select name,phone,address from person"]; sqlite3_stmt *stmt; Int result= SQLITe3_prepare_v2 (sqLite, [SQL UTF8String], -1, &STMT, NULL); NSMutableArray *array = [NSMutableArray array]; If (result == SQLITE_OK){while (sqlite3_step(STMT) == SQLITE_ROW) {if(result == SQLITE_OK){while (sqlite3_step(STMT) == SQLITE_ROW) { Const char* cName= (const char*) sqlite3_column_text(STMT, 0); const char *cPhone= (const char*) sqlite3_column_text(stmt, 1); const char *cAddress= (const char*) sqlite3_column_text(stmt, 2); / / remember change c string to nsstrings Person * Person = [[Person alloc] initWithName: [nsstrings stringWithUTF8String: cName] andPhone:[NSString stringWithUTF8String:cPhone] andAddress:[NSString stringWithUTF8String:cAddress]]; [array addObject:person]; }}else{NSLog(@" query failed "); } [self closeDB]; return array; } // query - (NSArray<Person *> *)queryPersonByName:(NSString *)personName{[self createDB]; NSString *sql = [NSString stringWithFormat: @"select * from person where name = '%@'",personName]; sqlite3_stmt *stmt; Int result= SQLITe3_prepare_v2 (sqLite, [SQL UTF8String], -1, &STMT, NULL); NSMutableArray *array = [NSMutableArray array]; If (result == SQLITE_OK){while (sqlite3_step(STMT) == SQLITE_ROW) {if(result == SQLITE_OK){while (sqlite3_step(STMT) == SQLITE_ROW) { String const char* cPhone= (const char*) sqlite3_column_text(STMT, 0); const char *cAddress= (const char*) sqlite3_column_text(stmt, 1); Person * Person = [[Person alloc] initWithName:personName andPhone:[NSString stringWithUTF8String:cPhone] andAddress:[NSString stringWithUTF8String:cAddress]]; [array addObject:person]; }}else{NSLog(@" query failed "); } [self closeDB]; return array; } @endCopy the code

(3) Call add, delete, change and check

// // viewController.m // SQLite database operation #import "viewController.h" #import "dbtools.h" @interface ViewController () - (IBAction)createDB:(id)sender; - (IBAction)createTable:(id)sender; - (IBAction)insert:(id)sender; - (IBAction)deleteData:(id)sender; - (IBAction)updateData:(id)sender; - (IBAction)queryData:(id)sender; @property(nonatomic,strong) DBTools *dbTools; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. self.dbTools = [[DBTools alloc] init]; self.dbTools.dbName = @"person"; } // create database - (IBAction)createDB:(id)sender {[self.dbtools createDB]; } // createTable - (IBAction)createTable (id)sender {[self.dbTools createTable:@"person"]; } // insert data - (IBAction)insert (id)sender {Person * Person = [[Person alloc] initWithName:@"mrgaogang" @"12345678900" andAddress:@" Shenzhen, Guangdong, China "]; [self.dbTools insertPerson:person]; } // deleteData - (IBAction)deleteData:(id)sender {[self.dbTools deletePerson: @"mrgaogang"]; } // updateData - (IBAction)updateData:(id)sender {NSArray *array = [self.dbtools queryPersonByName:@"mrgaogang"]; if(array.count > 0){ Person *person = [array firstObject]; [self.dbTools updatePerson:person ]; }} // queryData - (IBAction)queryData:(id)sender {NSArray *array = [self.dbtools queryPersonByName:@"mrgaogang"]; if(array.count > 0){ for (Person *p in array) { NSLog(@"%@",p); } } } @endCopy the code

Vi. Use of FMDB

What is FMDB?

  • FMDB is an iOS platform SQLite database framework
  • FMDB encapsulates SQLite’s C API as an OC

The advantages of FMDB

  • It is more object-oriented and saves a lot of troublesome and redundant C language code
  • Compared to Apple’s own Core Data framework, it is more lightweight and flexible
  • Multithread safe database operation method is provided to effectively prevent data chaos

1. Installation of FMDB

You can also copy the FMDB from the source code and put it inside the project. If you don’t know how to use Cocospods, please refer to this article: Use Cocospods

pod init
Copy the code

Then edit the Podfile and add FMDB:


# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'

target 'Use of FMDB' do
    # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
    use_frameworks!

    # Pods for MyApp2

    pod 'FMDB'
    # pod 'FMDB/FTS' # FMDB with FTS
    # pod 'FMDB/standalone' # FMDB with latest SQLite amalgamation source
    # pod 'FMDB/standalone/FTS' # FMDB with latest SQLite amalgamation source and FTS
    # pod 'FMDB/SQLCipher' # FMDB with SQLCipher
end

Copy the code
pod install

Copy the code

Open after installation: use of FMDB xcworkspace Remember: use of FMDB xcworkspace not use of FMDB xcodeProj

If compiling has the following error

Solution: Remove Pods.frameworks, leaving Pods_. Framework

2. Establishment of Model

The Model is almost like the Person before it. Declare Model properties and constructors

#import <Foundation/ foundation. h> NS_ASSUME_NONNULL_BEGIN @interface Student: NSObject @property(nonatomic,copy) NSString *name; @property(nonatomic,assign) int age; @property (nonatomic,copy) NSString *sex; - (instancetype)initWithName:(NSString *) name andAge:(int) age andSex:(NSString *) sex; @end NS_ASSUME_NONNULL_ENDCopy the code

Implement the Model constructor and description methods

#import <Foundation/ foundation. h> NS_ASSUME_NONNULL_BEGIN @interface Student: NSObject @property(nonatomic,copy) NSString *name; @property(nonatomic,assign) int age; @property (nonatomic,copy) NSString *sex; - (instancetype)initWithName:(NSString *) name andAge:(int) age andSex:(NSString *) sex; @end NS_ASSUME_NONNULL_ENDCopy the code

3. Add, delete, change and check the database

In fact, the overall process is similar to the pure operation sqlite3, with the following changes:

  • Used after successful library creationFMDatabasestorage

  • Instead of operating directly on sqlite3_exec, useexecuteUpdatePerform additions, deletions and modifications;

  • useexecuteUpdateInstead of using string concatenation, use placeholders?And,executeUpdateThe first parameter is SQL, followed by variable parameters, the number of parameters and?Is consistent with the number of;

  • useexecuteUpdateFor basic types such as ints, use this method@ ()Package pass parameters.

  • Query operation directly usedexecuteQueryAnd use something more intuitiveFMResultSetStore result set

  • Getting data from the result set also uses the more intuitive:stringForColumn , intForColumnSuch as access to

#import "DBTools.h" #import "FMDB.h" @interface DBTools () @property (strong,nonatomic) FMDatabase *fmdb; @end @implementation DBTools - (void) createDB{ // 1. Find a sandbox path nsstrings * docPath = [NSSearchPathForDirectoriesInDomains (NSDocumentDirectory NSUserDomainMask, YES) firstObject]; / / 2. Find the db file nsstrings * fileName = [docPath stringByAppendingPathComponent: @ "student. Db"]. self.fmdb = [FMDatabase databaseWithPath:fileName]; BOOL success = [self.fmdb open]; If (success){NSLog(@" open database successfully "); }else{NSLog(@" failed to open database "); }} - (void) createTable{// Open database [self createDB]; NSString *sql = @"create table t_stu(id integer primary key autoincrement, name text, age integer, sex text)"; BOOL isSuccess = [self.fmdb executeUpdate:sql]; If (isSuccess){NSLog(@" create table successfully "); } else{NSLog(@" create table failed "); } // Close the database [self.fmdb close]; } - (void) insertStudent:(Student *) student{ [self createDB]; NSString *sql = @"insert into t_stu(name,age,sex) values (? ,? ,?) "; Age is an int, age is an int, age is an int Ways so needs to be converted to Boolean isSuccess = [self. FMDB executeUpdate: SQL, student. The name, @ (student. Age), student. Sex]; If (isSuccess){NSLog(@" insert successfully "); } else{NSLog(@" insert failed "); } // Close the database [self.fmdb close]; } - (void) deleteStudent:(NSString *) stuName{ [self createDB]; NSString *sql = @"delete from t_stu where name=?" ; BOOL isSuccess = [self.fmdb executeUpdate:sql,stuName]; If (isSuccess){NSLog(@" delete successfully "); } else{NSLog(@" delete failed "); } // Close the database [self.fmdb close]; } - (void) updateStudent:(Student *) student{ [self createDB]; NSString *sql = @"update t_stu set age=? , sex=? where name=?" ; BOOL isSuccess = [self.fmdb executeUpdate:sql,@(student.age),student.sex,student.name]; If (isSuccess){NSLog(@" update successful "); } else{NSLog(@" update failed "); } // Close the database [self.fmdb close]; } - (NSArray<Student *>*) queryStudentByName:(NSString *) stuName{ [self createDB]; NSString *sql = @"select name,age,sex from t_stu where name=?" ; FMResultSet *resultSet=[self.fmdb executeQuery:sql,stuName]; NSMutableArray *array = [NSMutableArray array]; while ([resultSet next]) { NSString *name = [resultSet stringForColumn:@"name"]; int age = [resultSet intForColumn:@"age"]; NSString *sex = [resultSet stringForColumn:@"sex"]; Student *stu = [[Student alloc] initWithName:name andAge:age andSex:sex]; [array addObject:stu]; } // Close the database [self.fmdb close]; return array; } @endCopy the code

4. Transaction operations


- (void)insertStudents:(NSArray<Student *> *)students{
    [self createDB];
    // Start the transaction
    [self.fmdb beginTransaction];
    
    @try {
        for (Student *student in students) {
            NSString *sql = @"insert into t_stu(name,age,sex) values (? ,? ,?) ";
            Age is an int, so we need to convert it to an object
            BOOL isSuccess = [self.fmdb executeUpdate:sql,student.name,@(student.age),student.sex];
            
            if(isSuccess){
                NSLog(@"Insert successful");
            }
            else{
                NSLog(@"Insert failed"); }}}@catch (NSException *exception) {
        // Roll back data
        [self.fmdb rollback];
    } @finally {
        // Submit data
        [self.fmdb commit];
    }
    
    // Close the database
    [self.fmdb close];
}

Copy the code

Testing:

    NSMutableArray *array = [NSMutableArray array];
    for (int i=0; i<1000; i++) {
         Student *stu = [[Student alloc] initWithName:@"mrgaogang" andAge:i andSex:@"male"];
        [array addObject:stu];
    }
    [self.dbTools insertStudents: array];

Copy the code

reference

  • NSBundle details and iOS package contents
  • IOS12 + Objective – C + Xcode tutorial