【iOS】iOS persistence

1 Persistence purpose

  1. Quick display and improved experience
    • The data that has been loaded does not need to be loaded from the network (disk) again the next time the user views it, and will be displayed directly to the user.
  2. Save user traffic (save server resources)
    • Cache larger resource data so that the next display does not require downloading and consuming traffic.
    • At the same time, the number of server visits is reduced and server resources are saved. (picture)
  3. Use offline.
    • The data browsed by the user does not require an Internet connection and can be viewed again.
    • Some functions eliminate dependence on the Internet. (Baidu offline map, book reader)
    • When there is no network, the user is allowed to perform operations and will be synchronized to the server the next time the network is connected.
  4. Record user actions
    • Draft: For operations that require a large cost for the user, each step of the user is cached. After the user interrupts the operation, the last operation will be continued directly the next time the user operates.
    • Read content tag cache to help users identify what has been read.
    • Search history cache

2 Classification of data persistence methods

2.1 Memory cache

  • Definition
    : For data that is used frequently, after the data is loaded from the network or disk into the memory, it is not destroyed immediately after use. It is loaded directly from the memory the next time it is used.
  • Case
    iOS system image loading - [UIImage imageNamed:@"imageName"]
    network image loading third-party library:SDWebImage
  • Implementation
    Technical means for implementing memory caching include those provided by Apple NSURLCache, NSCacheas well as open source caching libraries that have advantages in performance and API YYCache, PINCacheetc.

2.2 Disk cache

  • Definition
    : Write the data loaded from the network and generated by user operations to the disk. The next time the user views and continues the operation, it will be loaded directly from the disk for use.
  • Case:
    user input content draft caching (such as: comments, text editing)
    network image loading third-party library: SDWebImage
    search history caching
  • accomplish
    1. NSUserDefault
      Suitable for caching small-scale data and weak business-related data.
    2. keychain
      KeychainIt is a storage mechanism with reversible encryption provided by Apple, which is commonly used for various needs to store usernames and passwords. In addition, Keychainit is system-level storage and can be iCloudsynchronized. Even if Appit is deleted, Keychainthe data is still retained and can be read directly by the user next time it is installed App. It is usually used to store the user's unique identification string. iCloudTherefore, sensitive small data that needs to be encrypted and synchronized are generally Keychainaccessed.
    3. File storage
      • Plist: Generally structured data can Plistbe persisted in the following ways :
      • archive: Archivemethod can access data that follows the protocol. What is more convenient is that objects are used for access. However, the intermediate serialization and deserialization requires a certain amount of performance. It can be used when you want to use objects for direct disk access. .
      • Stream: Refers to file storage, generally used to store data such as pictures, video files, etc.
    4. Database storage
      The database is suitable for accessing some relational data; it can be used when there are a large number of conditional query and sorting requirements.
      • Core Data:Officially packaged by AppleORM(Object Relational Mapping)
      • FMDB: One of githubthe most popular iOS sqlitepackaging open source libraries
      • WCDB: The WeChat team’s open source implementation based on the sqlite package they use, has ORM(Object Relational Mapping)features, support iOS, and Android.
      • Realm: A cross-platform ( , ) mobile database Y Combinatoropen sourced by the incubated entrepreneurial team .iOSAndroid

2.3 Which caching solution should be used

Choose according to your needs:

  • Simple data storage can directly write files and key-value access.
  • If you need to search and sort according to some conditions, you can use relational storage methods such as SQLite.
  • You do not want small-capacity data (username, password, token) to be cleared after the App is deleted to be stored in the keychain.

3 Introduction to sandbox mechanism

iOSThe sandbox mechanism in is a security system. In order to ensure system security, iOSeach application will create its own sandbox file (storage space) when installed. An application can only access its own sandbox files and cannot access the sandbox files of other applications. When an application needs to request or receive data from the outside, it must pass permission authentication, otherwise, the data cannot be obtained. All non-code files should be saved here, such as property files plist, text files, images, icons, media resources, etc.

3.1 The relationship between sandbox mechanism and persistence

Persistence in iOS refers to the ability to retain data on a storage device after the app exits. The sandbox mechanism is a security measure provided by the iOS system for each application. By limiting each application to its own sandbox, it ensures that the application can only access its own data and cannot access the data of other applications. .

Persistence and sandboxing mechanisms iOSare closely related in applications because applications need to persist data in a sandbox. iOSSeveral ways are provided to achieve data persistence, all within the application's sandbox. The following is iOSthe relationship between persistence and sandbox mechanism:

  • File storage persistence: In iOSapplications, you can use the file system to persist data, such as saving data to files. iOSAn application can only create, read, and write files in its own sandbox directory and cannot access other applications' files. Common file system persistence methods include using NSFileManagerthe class to perform file operations, or using NSDatathe or NSStringclass methods to write data to files.
  • UserDefaultsPersistence: NSUserDefaultsIt is a simple persistence method that can be used to save user preferences, configuration information, etc. Although the data is persisted, it is still stored in the application's sandbox and can only be accessed by the current application.
  • Database persistence: It saves data to SQLitea database or other data storage. These are also ways of doing data persistence within an application's sandbox.
  • KeychainPersistence: KeychainIt is iOSa way to securely store sensitive data, such as passwords, keys, etc. KeychainThe data is also persisted within the application's sandbox and has higher security protection to ensure that only the application itself can access the data.

In general, iOSpersistence operations in are performed within the application's sandbox and are restricted by the sandbox mechanism, ensuring that applications can only access their own data, ensuring data isolation and security between applications. sex.

3.2 Directory structure of the sandbox

3.2.1 Get the sandbox path of the application:

// 获取沙盒根目录路径
    NSString *path = NSHomeDirectory();

Note: Each time you compile the code, a new sandbox path will be generated. Note that compilation is not started, so when running on a simulated machine or a real machine, the sandbox path obtained by each run will be different. The online version of the app will not be the same on a real machine. A new sandbox path will be generated.

The above code obtains the path of the current application directory. This directory is the sandbox of the application. There are 4 folders in this directory: , , , . The current application can only Documentsaccess Libraryfiles SystemDatain tmpthis directory.
Insert image description here

3.2.2 Introduction to commonly used C functions for accessing sandbox directories

//文件路径搜索
FOUNDATION_EXPORT NSArray<NSString *> *NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory directory, NSSearchPathDomainMask domainMask, BOOL expandTilde);

The return value of this method is an array. Since there is only one unique path in iPhone, you can directly take the first element of the array.

The function of this function is to return an array of paths in the specified search directory. It accepts three parameters:

  1. directory: NSSearchPathDirectoryType enumeration value used to specify the directory to search. Common enumeration values ​​include:
//常用的NSSearchPathDirectory枚举值
typedef NS_ENUM(NSUInteger, NSSearchPathDirectory) {
    
    
    NSApplicationDirectory = 1,             // supported applications (Applications)
    NSDemoApplicationDirectory,             // unsupported applications, demonstration versions (Demos)
    NSAdminApplicationDirectory,            // system and network administration applications (Administration)
    NSLibraryDirectory,                     // various documentation, support, and configuration files, resources (Library)
    NSUserDirectory,                        // user home directories (Users)
    NSDocumentationDirectory,               //  Library 下的(Documentation)模拟器上没有创建
    NSDocumentDirectory,                    // documents (Documents)
};
  1. domainMask: NSSearchPathDomainMaskA bitmask of type that specifies the scope of the search path. Common enumeration values ​​include:
typedef NS_OPTIONS(NSUInteger, NSSearchPathDomainMask) {
    
    
    NSUserDomainMask = 1,       // 用户目录 - 基本上就用这个。 
    NSLocalDomainMask = 2,      // 本地
    NSNetworkDomainMask = 4,    // 网络 
    NSSystemDomainMask = 8,     // 系统
    NSAllDomainsMask = 0x0ffff  // 所有 
};
  1. expandTilde: BOOLType parameter, specifying whether to expand the tilde ~. If set to YES, tildes in the path ~will be expanded to the user's home directory path.

The value is NO: The Caches directory path is ~/Library/Caches
The value is YES: The Caches directory path is /var/mobile/Containers/Data/Application/E7B438D4-0AB3-49D0-9C2C-B84AF67C752B/Library/Caches

3.2.3 Example of obtaining sandbox directory

//获取沙盒根路径
NSString *path = NSHomeDirectory();
NSLog(@"沙盒根路径:%@", path);
//Document路径
NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSLog(@"Document目录路径:%@", docDir);
// 获取Library的目录路径
NSString *libDir = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject];
NSLog(@"Libarary目录路径:%@", libDir);
// 获取Caches目录路径
NSString *cachesDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
NSLog(@"Cacheas目录路径:%@", cachesDir);
// library Preference
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSLog(@"偏好设置目录路径:%@", defaults);
// 获取tmp目录路径
NSString *tmpDir =  NSTemporaryDirectory();
NSLog(@"tmp目录路径:%@", tmpDir);

Output result:
Insert image description here

3.2.4 Introduction to Sandbox Directory

  • Documents: Save persistent data and back it up. Generally used to store data that needs to be persisted.
    Generally in our projects, we will store some key data such as user login information and search history here.
// 获取Documents目录路径
NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];

This folder is backed up by default, to iCloud.
Note: iCloud backup will automatically back up the user's iOS device via Wi-Fi every day.
We can add a string to the end of the obtained path to create a file name:

NSString *filename = [docDir stringByAppendingPathComponent:@"data.txt"];

In this way, ours filenameis a complete .txtdirectory of type files.

  • Library: By default, settings and other status information are stored. cachesAll directories except subdirectories will be icludesynchronized.
    • Application Support: This directory contains files that the application uses to run but should be hidden from the user, such as new levels for games.
    • Caches: Save the data generated when the application is running and need to be persisted. Generally, it stores non-important data that is large and does not need to be backed up, such as the cache of audio, video and pictures requested by the network. In iOS 5.0and later versions, Cacheswhen the system disk space is very low, the system may delete the directory in rare cases (this will not happen when the APP is running), so try to ensure that the files in this path are available when the APP is re-run. rebuild.
    • Cooikes: The system will automatically save Appthe network request cookieas a file.
    • Preferences: Saves all preferences for the app. UserDefaultsThe generated plistfiles will be saved in this directory.
    • SplashBoard: Store the startup screen cache. The cache file format is ktx, which is essentially a picture. If the startup screen does not take effect, you can consider deleting the relevant cache files under the path to solve the problem.
  • SystemData: Stores system data and has no externally exposed interface.
  • tmp: Temporary folder (the system will delete the files inside from time to time).

4 Persistent data storage methods

4.1 plist file (serialization)

The plist file is saved in the directory as an XML file. The following types can be serialized:

NSString;//字符串
NSMutableString;//可变字符串
NSArray;//数组
NSMutableArray;//可变数组
NSDictionary;//字典
NSMutableDictionary;//可变字典
NSData;//二进制数据
NSMutableData;//可变二进制数据
NSNumber;//基本数据
NSDate;//日期

Here we use NSDictionary as an example. Other types are similar to this method:

- (void) writeToPlist {
    
    
    // 定义plistName
    NSString *plistName = @"test.plist";
    // 存取路径
    NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
    // 打印路径
    NSLog(@"%@", path);
    // 连接路径
    NSString *filePath = [path stringByAppendingPathComponent:plistName];
    
    // 定义字典文件
    NSDictionary *dict = @{
    
    @"SXTTTTT":@"10"};
    
    //序列化
    [dict writeToFile:filePath atomically:YES];
}

Whether to write atomically to the auxiliary file first, a method of writing files to increase security, is generally YES.

Check the printed path and find that there is indeed one more test.plistfile.
Insert image description here

Open this file and take a look.
Insert image description here

You can see that the value is indeed stored inside this file.

This value can be retrieved using dictionaryWithContentsOfFile:class methods:

    NSDictionary *dict1 = [NSDictionary dictionaryWithContentsOfFile:filePath];
    NSLog(@"%@", dict1);

result:
Insert image description here

4.2 preference: preference settings

Preferences ( Preferences) is a simple data persistence method in iOS development, used to save application configuration information, user preferences and other data. In iOS, you can use NSUserDefaultsthe class to load and save preferences.

NSUserDefaultsIs a singleton class used to access the application's preferences. It can store various types of data, such as Boolean values, integers, floating point numbers, strings, arrays, dictionaries, etc. This data will be persisted to the application's sandbox, and even if the application exits, the data can be read the next time it is started.

NSUserDefaults: Simple data can be read and written quickly, and custom types cannot be stored.
UserDefaultsWhen setting data, instead of writing it immediately, the data in the cache is written to the local disk regularly based on the timestamp. Therefore, after calling setthe method, the application may terminate before the data is written to the disk. If the above problem occurs, you can force writing by calling synchornizethe method .[defaults synchornize];

Advantages of preference storage:

  • You don't need to worry about the file name, the system will automatically generate a file name for you.
  • Quickly store key-value pairs.

UserDefaultsLet’s try to register an account and password using

- (void)writeToUserDefaults {
    
    
    // 获取偏好设置对象
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    
    // 存储数据
    [defaults setObject:@"ouhaijian" forKey:@"name"];
    [defaults setObject:@"666666" forKey:@"password"];
    
    // 同步调用,立刻写到文件中,不写这个方法会异步,有延迟
    [defaults synchronize];
}
- (void)readFromUserDefaults {
    
    
    // 获取偏好设置对象
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    
    // 获取数据
    NSString *name = [defaults objectForKey:@"name"];
    NSString *password = [defaults objectForKey:@"password"];
    
    NSLog(@"name:%@", name);
    NSLog(@"password:%@", password);
}

operation result:
Insert image description here

Note that after setting the preference value, you need to call the synchronize method to save the data to disk immediately to ensure data persistence. However, in iOS 7 and above, the synchronize method will automatically save the data at the appropriate time, so this operation is no longer necessary.

When we cd to the file directory in the sandbox preference, we can see that there is a new file in it. What is stored in this file is the account we stored.
Insert image description here

Open this file and take a look.
Insert image description here

4.3 NSKeyedArchiver archiving and unarchiving

NSKeyedArchiver(Archiving): Archiving is generally used when saving custom objects. Because plistthe file cannot save custom objects. If there is a custom object saved in a dictionary, if this object is written to a file, it will not generate plista file. If the object is of type NSString, NSDictionary, NSArray, NSData, etc., it can be archived and restored NSNumberdirectly .NSKeyedArchiver

But before we use archiving, we must abide by NSSecureCodingthe agreement. The old version only needs to follow NSCodingthe method of archiving and unarchiving, but after the iOS13 update, it will not work. We must abide by NSSecureCodingthe agreement, and NSSecureCodingthe agreement also follows the original NSCodingone . protocol, but we also need to follow one of its supportsSecureCodingmethods so that we can archive successfully.

Because NSSecureCodingthe protocol also follows NSCodingthe original protocol, he also has - (void)encodeWithCoder:(NSCoder *)codermethods and - (id)initWithCoder:(NSCoder *)codermethods:

-(void)encodeWithCoder:(NSCoder *)coder, this method will be called every time the object is archived. Generally, in this method, you specify how to archive each instance variable in the object. Instance variables can be archived using the encodeObject:forKey: method.

-(id)initWithCoder:(NSCoder *)coder, this method will be called every time an object is restored (decoded) from the file. Generally, this method specifies how to decode the data in the file into instance variables of the object. You can use the decodeObject:forKey method to decode the instance variables.

For example, create a new SXPerson class:

#import <Foundation/Foundation.h>

@interface SXPerson : NSObject <NSSecureCoding>
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) int age;
@end

@implementation SXPerson
- (void)encodeWithCoder:(NSCoder *)coder {
    
    
    [coder encodeObject:self.name forKey:@"name"];
    [coder encodeInt:self.age forKey:@"age"];
}

- (id)initWithCoder:(NSCoder *)coder {
    
    
    if (self = [super init]) {
    
    
        self.name = [coder decodeObjectForKey:@"name"];
        self.age = [coder decodeIntForKey:@"age"];
    }
    return self;
}

+ (BOOL)supportsSecureCoding {
    
    
    return YES;
}
@end

To archive and unarchive:

// 归档
- (void)writeUseNSKeyedArchiver {
    
    
    SXPerson *person = [[SXPerson alloc] init];
    person.age = 10;
    person.name = @"蔡徐坤";
    NSError * __autoreleasing *error = NULL;
    
    // 获得Document的全路径
    NSString *docu = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
    // 获得新文件的全路径
    NSString *path = [docu stringByAppendingPathComponent:@"SXPerson.data"];
    // 将对象封装为Data并归档
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:person requiringSecureCoding:YES error:error];
    if (error) {
    
    
        NSLog(@"writeUseNSKeyedArchiver:%@", *error);
    } else {
    
    
        [data writeToFile:path atomically:YES];
    }
}

// 解档
- (void)readUseNSKeyedArchiver {
    
    
    NSError * __autoreleasing *error = NULL;
    // 获得Document的全路径
    NSString *docu = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
    // 获得新文件的全路径
    NSString *path = [docu stringByAppendingPathComponent:@"SXPerson.data"];
    // 从path路径中获取data数据
    NSData *unData = [NSData dataWithContentsOfFile:path];
    // 创建解档操作的客户端允许的类集合
        // 这个集合应包括自定义对象的属性的类
    NSSet *allowedClassesSet = [NSSet setWithObjects:[NSString class], [SXPerson class], nil];
    SXPerson *person = (SXPerson *)[NSKeyedUnarchiver unarchivedObjectOfClasses:allowedClassesSet fromData:unData error:error];
    // 打印结果
    if (error) {
    
    
        NSLog(@"readUseNSKeyedArchiver:%@", *error);
    } else {
    
    
        NSLog(@"%@-%d", person.name, person.age);
    }
}

Create a set of classes allowed by the client for the unarchive operation. This set should include classes for custom object properties, otherwise a warning will be given. This warning does not cause errors in the unarchive operation, but may be disabled in a future version. To avoid future compatibility issues, it is recommended that you add all classes that need to be unarchived to the set of classes allowed by the client when unarchiving.

result:
Insert image description here

We can also see that Documentthere is an additional SXStudent.datafile in the directory:
Insert image description here

You can vimopen this file and take a look. You can probably understand that it is an object that stores NSKeyedArchiver archives:
Insert image description here

Finally, another point is that archiving operations are sometimes used to implement deep copy operations of objects (especially custom objects). Through archiving, we can serialize the original object into binary data, and recreate a brand new object during unarchiving, including its internal reference objects. In this way, the new object is completely independent from the original object, and modifications to one object will not affect the other.

4.4 Database storage (this part is too much, I will learn it separately in the future)

  • SQLite:
    It is the current mainstream embedded relational database. Its main features are lightweight and cross-platform. Currently, many embedded operating systems use it as the first choice for database.
  • CoreData:
    CoreDataIt is a framework that only appeared after iOS5. It is essentially SQLitean encapsulation of the right. It provides ORMthe function of object-relational mapping (), that is, it can convert OC objects into data and save them in SQLitedatabase files. It can also save them in database files. The data in the database is restored into OCobjects. In this process, there is no need to manually write any SQLstatements. CoreDataIt encapsulates the operation process of the database and OCthe conversion process of data and objects in the database. By CoreDatamanaging your application's data model, you can greatly reduce the amount of code you need to write.
  • FMDB:
    It is a third-party framework that handles data storage. The framework is an sqliteencapsulation of the right. The entire framework is very lightweight but flexible, and more object-oriented.
  • SQLiteThe difference between and CoreData:
    CoreDatawhen an object is updated, its associated objects will also be updated, which is equivalent to when you update a table, its associated other tables will also be updated.
    CoreDataProvides a simpler performance management mechanism that can limit the total number of query records. This class will automatically update its cache.
    In terms of multi-table query, CoreDatait is not SQLintuitive and there are no operations such as outer joins and left joins.

5 supplement

5.1 What are serialization and deserialization, and what are they used for?

  • Serialization: The process of converting an object into a sequence of bytes
  • Deserialization: Recover a sequence of bytes into an object
  • Function: Write objects to files or databases and read them out

5.2 plist file in the program

plistFull name Property List, attribute list file, which is a file used to store serialized objects. The extension of the attribute list file is plist, so it is usually called plista file. The file is xmlformatted.

Here we will add the plist file in the program and its corresponding writing and reading operations.

The plist file in the program must be created first and then used:
Insert image description here

Source code:

// 写入程序plist
- (void)setDataToPlist {
    
    
    // 第一参数:文件名
    // 第二参数:文件后缀
    NSString *plist = [[NSBundle mainBundle] pathForResource:@"show" ofType:@"plist"];
    NSDictionary *dict = @{
    
    @"蔡徐坤":@"name", @10:@"age"};
    [dict writeToFile:plist atomically:YES];
}

// 读取程序plist
- (void)getDataFromPlist {
    
    
    NSString *plist = [[NSBundle mainBundle] pathForResource:@"show" ofType:@"plist"];
    NSLog(@"%@", plist);
    NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:plist];
    NSLog(@"%@", dict);
}

operation result:
Insert image description here

Although we created this file in the project project, the data does not seem to be saved in this file, at least not in the project package.

We cd into the printed directory:
Insert image description here

Use vim to look at this file with the same name:
Insert image description here

You can see that the data is saved to the application's Resource Bundle.

When we compile the project, Xcode seems to copy all resource files (including .plist files) into the application's Resource Bundle. This ensures that the contents of the resource file are found and read correctly at runtime.

Guess you like

Origin blog.csdn.net/m0_63852285/article/details/131942820
ios