iOS數(shù)據(jù)存儲(chǔ)方式(Core Data/Keycahin/NSUserDefault...)

前言

在iOS開發(fā)中數(shù)據(jù)存儲(chǔ)的方式可以歸納為磁盤緩存和內(nèi)存緩存:磁盤緩存分為兩類:文件、數(shù)據(jù)庫(kù)存儲(chǔ)。

  • 文件存儲(chǔ):NSKeyedArchiver歸檔、NSUserDefault偏好設(shè)置,歸檔或者plist文件存儲(chǔ)可以選擇保存到沙盒中,偏好設(shè)置系統(tǒng)規(guī)定只能保存到沙盒的Library/Preferences目錄。
  • 數(shù)據(jù)庫(kù)存儲(chǔ):使用FMDB、SQLite,通過(guò)SQL直接訪問(wèn)數(shù)據(jù)庫(kù),也可以通過(guò)ORM進(jìn)行對(duì)象關(guān)系映射訪問(wèn)數(shù)據(jù)庫(kù)。這兩種方式對(duì)應(yīng)iOS中FMDB、SQLite和Core Data的內(nèi)容,在此將重點(diǎn)進(jìn)行分析:
  • 內(nèi)存緩存:蘋果提供NSCache、NSMutableURL通過(guò)設(shè)置相應(yīng)策略實(shí)現(xiàn)內(nèi)存緩存

iOS數(shù)據(jù)持久化方式

  1. NSKeyedArchiver
  2. Keychain
  3. NSUserDefault
  4. Core Data
  5. FMBD
  6. SQLite
  7. Realm

iOS數(shù)據(jù)臨時(shí)存儲(chǔ)方式

  1. NSCache
  2. NSMutableURL

NSKeyedArchiver(歸檔)

NSKeyedArchiver 蘋果開發(fā)文檔是這么描述

NSKeyedArchiver, a concrete subclass of NSCoder
, provides a way to encode objects (and scalar values) into an architecture-independent format that can be stored in a file. When you archive a set of objects, the class information and instance variables for each object are written to the archive. NSKeyedArchiver
’s companion class, NSKeyedUnarchiver
, decodes the data in an archive and creates a set of objects equivalent to the original set.

NSKeyedArchiver是NSCoder的具體子類,提供了一種方法來(lái)將對(duì)象(和標(biāo)量值)編碼成與體系結(jié)構(gòu)無(wú)關(guān)的格式可以存儲(chǔ)在一個(gè)文件中。 當(dāng)您將一組對(duì)象歸檔時(shí),每個(gè)對(duì)象的類信息和實(shí)例變量都被寫入到文檔中。NSKeyedArchiver的伙伴類,NSKeyedUnarchiver,在一個(gè)存檔對(duì)其解碼并且創(chuàng)建一組與原始集相同的對(duì)象。

特點(diǎn):

  • 存儲(chǔ)自定義模型對(duì)象和Foundation對(duì)象數(shù)據(jù)
    NSKeyedArchiver歸檔相對(duì)較plist存儲(chǔ)而言,它可以直接存儲(chǔ)自定義模型對(duì)象,而plist文件需要將模型轉(zhuǎn)為字典才可以存儲(chǔ)自定義對(duì)象模型;
  • 歸檔不能存儲(chǔ)大批量數(shù)據(jù),存儲(chǔ)數(shù)據(jù)到文件是將所有的數(shù)據(jù)一下子存儲(chǔ)到文件中,從文件中讀取數(shù)據(jù)也是一下子讀取所有的數(shù)據(jù);

缺點(diǎn):

添加、刪除、更新數(shù)據(jù)需要將所有數(shù)據(jù)解檔在執(zhí)行相應(yīng)處理,數(shù)據(jù)量大有可能會(huì)性能低情況

使用

  • 遵循NSCoding 協(xié)議,需要實(shí)現(xiàn)兩個(gè)方法: -initWithCoder:(解檔)和 encodeWithCoder:(歸檔)。
  • 遵循NSCoding協(xié)議的類可以被序列化和反序列化,這樣可以歸檔到磁盤上或分發(fā)到網(wǎng)絡(luò)上。

User類

@interface User : NSObject <NSCoding>

@property (nonatomic, copy) NSString *userName;

@property (nonatomic, copy) NSString *password;

@end     

@implementation User

#pragma mark - NSCoding
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    
    if (!(self = [super init])) {
        return nil;
    }
    
    self.userName = [aDecoder decodeObjectForKey:@"userName"];
    
    self.password = [aDecoder decodeObjectForKey:@"password"];
    
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder {
    
    [aCoder encodeObject:self.userName forKey:@"userName"];
    
    [aCoder encodeObject:self.password forKey:@"password"];
    
    
}

- (NSString *)description {
//    if (_userName) {
//        return [NSString stringWithFormat:@"user: %@   password: %@",self.userName,self.password];
//    }
    NSMutableString *str = @"".mutableCopy;
    [str appendString:@"user:{\n"];
    if (self.userName) [str appendFormat:@"  userName:%@,\n", self.userName];
   
    if (self.password) [str appendFormat:@"  service:%@,\n", self.password];
    [str appendString:@"}"];
    return str;
}

@end

NSKeyedArchiverExample:

- (IBAction)addClick:(id)sender {
    
    // 歸檔
    User *user = [[User alloc] init];
    
    user.userName = @"linmingjun";
    user.password = @"5788";
    
    
    NSMutableString *str = @"".mutableCopy;
    [str appendString:@"LTKeychain:{\n"];
    
    [str appendFormat:@"歸檔userName:%@   password: %@\n\n", user.userName, user.password];
    
    
    [str appendString:@"}"];
    
    _detailTextView.text = str;

    
}


- (IBAction)selectClick:(id)sender {
    
    // 解檔
    User *unUser = [NSKeyedUnarchiver unarchiveObjectWithFile:@"/Users/lmj/Desktop/user"];
    NSMutableString *str = @"".mutableCopy;
    [str appendString:@"LTKeychain:{\n"];
    
    [str appendFormat:@"解檔userName:%@   password: %@  \n", unUser.userName , unUser.password];
    
    [str appendString:@"}"];
    
    _detailTextView.text = str;
}

程序運(yùn)行結(jié)果

Keychain(鑰匙扣)

Keychain 蘋果官方文檔是這樣描述
Reference:Keychain Service

Computer users typically have to manage multiple accounts that require logins with user IDs and passwords. Secure FTP servers, AppleShare servers, database servers, secure websites, instant messaging accounts, and many other services require authentication before they can be used. Users often respond to this situation by making up very simple, easily remembered passwords, by using the same password over and over, or by writing passwords down where they can be easily found. Any of these cases compromises security.
The Keychain Services API provides a solution to this problem. By making a single call to this API, an application can store login information on a keychain where the application can retrieve the information—also with a single call—when needed. A keychain is an encrypted container that holds passwords for multiple applications and secure services. Keychains are secure storage containers, which means that when the keychain is locked, no one can access its protected contents. In OS X, users can unlock a keychain—thus providing trusted applications access to the contents—by entering a single master password. In iOS, each application always has access to its own keychain items; the user is never asked to unlock the keychain. Whereas in OS X any application can access any keychain item provided the user gives permission, in iOS an application can access only its own keychain items.

計(jì)算機(jī)用戶通常要管理多個(gè)賬戶,需要登錄用戶id密碼。 安全FTP服務(wù)器、AppleShare服務(wù)器、數(shù)據(jù)庫(kù)服務(wù)器、安全的網(wǎng)站,即時(shí)通訊賬號(hào),和許多其他服務(wù)要求身份驗(yàn)證才可以使用。 用戶經(jīng)常應(yīng)對(duì)這種情況通過(guò)編造很簡(jiǎn)單,容易記住的密碼,通過(guò)使用相同的密碼,或通過(guò)密碼寫下來(lái),他們可以很容易地找到。

鑰匙鏈服務(wù)API提供了一個(gè)解決這個(gè)問(wèn)題的。 通過(guò)單個(gè)調(diào)用此API,應(yīng)用程序可以將登錄信息存儲(chǔ)在一個(gè)鑰匙鏈,應(yīng)用程序可以使用單個(gè)時(shí)調(diào)用檢索信息也需要的。 一個(gè)鑰匙鏈是一個(gè)加密的容器保存密碼用于多個(gè)應(yīng)用程序和安全服務(wù)。 鑰匙是安全存儲(chǔ)容器,這意味著當(dāng)鑰匙鏈鎖著的,沒(méi)有人可以訪問(wèn)它的保護(hù)內(nèi)容。 在OS X中,用戶可以解鎖keychain-thus提供受信任應(yīng)用程序訪問(wèn)內(nèi)容進(jìn)入一個(gè)主密碼。 在iOS,每個(gè)應(yīng)用程序總是能夠訪問(wèn)自己的密鑰鏈項(xiàng);用戶不要求解鎖鑰匙鏈。 而在OS X的任何應(yīng)用程序都可以訪問(wèn)任何密鑰鏈項(xiàng)提供了用戶賦予權(quán)限,在iOS應(yīng)用程序只能訪問(wèn)自己的密鑰鏈項(xiàng)。


通常情況下,我們用NSUserDefaults存儲(chǔ)數(shù)據(jù)信息,但是對(duì)于一些私密信息,比如密碼、證書等等,就需要使用更為安全的keychain了。keychain里保存的信息不會(huì)因App被刪除而丟失,在用戶重新安裝App后依然有效,數(shù)據(jù)還在。

功能:

Reference:Keychain Services Programming Guide

大多數(shù)iOS應(yīng)用程序只需要使用鑰匙鏈服務(wù)添加一個(gè)新密碼鑰匙鏈,修改現(xiàn)有的密鑰鏈項(xiàng),在需要的時(shí)候或檢索一個(gè)密碼。 鑰匙鏈完成這些任務(wù)的服務(wù)提供了以下功能:
SecItemAdd
將一個(gè)條目添加到鑰匙鏈

SecItemUpdate
修改現(xiàn)有的密鑰鏈項(xiàng)

SecItemCopyMatching
應(yīng)用程序通過(guò)調(diào)用字典包含屬性,確定密鑰鏈項(xiàng)。

使用

- (IBAction)addClick:(id)sender {
    
    SetPasswordBlock setupPasswordBlock = ^(NSString * account, NSString * password){
        NSError *error = nil;
        
        [LTKeychain setPassword:account forService:kService account:account error:&error];
        
        return error;
        
    };
    
    //    NSError *error = setupPasswordBlock(@"hell",@"123");
    
    NSString *account = @"LinMingJun";
    NSString *password = @"MyAccount";
    NSString *account2 = @"LinMingjunTwo";
    NSString *password2 = @"MyAccount";
    
    NSError *setPasswordError = setupPasswordBlock(account,password);
    NSError *setPasswordError2 = setupPasswordBlock(account2,password2);
    
    NSMutableString *str = @"".mutableCopy;
    [str appendString:@"LTKeychain:{\n"];
    
    [str appendFormat:@"添加account:%@   setPasswordError%@: \n\n", account, setPasswordError ? @"失敗" : @"成功"];
    
    [str appendFormat:@"添加account2:%@   setPasswordError2%@: %@\n\n", account, setPasswordError2 ? @"失敗" : @"成功", setPasswordError2];
    
    
    [str appendString:@"}"];
    
    _detailTextView.text = str;
    
    
}


- (IBAction)deleteClick:(id)sender {
    // delete
    NSMutableString *str = @"".mutableCopy;
    [str appendString:@"LTKeychain:{\n"];
    
    if ([LTKeychain deletePassWordForService:kService account:@"LinMingJun" error:nil]) {
        [str appendFormat:@"delete success! \n\n"];
    
    } else {
        [str appendFormat:@"delete fail! \n\n"];
    }
    
    [str appendString:@"}"];
    
    _detailTextView.text = str;
}
- (IBAction)updateClick:(id)sender {
    
    
    
}
- (IBAction)selectClick:(id)sender {
    // select password
    NSError *error = nil;
    NSString *password =  [LTKeychain passwordForService:kService account:@"MyAccount" error:&error];
    
    NSArray *array = [LTKeychain allAccounts];

    NSMutableString *str = @"".mutableCopy;
    [str appendString:@"LTKeychain:{\n"];

    
    [str appendFormat:@"select: password = %@ \n\n", password];

    [str appendFormat:@"select: array = %@ \n\n", array];
    
    [str appendString:@"}"];
    
    _detailTextView.text = str;
    
}
程序運(yùn)行截圖

<b>根據(jù)YYKeyChain提供的思路,自己書寫了一遍 </b>

LTKeychain.h

//
//  LTKeychain.h
//  DataStorageExample
//
//  Created by lmj  on 16/5/26.
//  Copyright (c) 2016年 linmingjun. All rights reserved.
//  https://developer.apple.com/library/mac/documentation/Security/Reference/keychainservices/

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

@class LTKeychainQuery;
@interface LTKeychain : NSObject

#pragma mark - Classic methods

/**
*/
+ (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;
+ (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account;

/**
*/
+ (NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;
+ (NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account;

/**
*/
+ (BOOL)deletePassWordForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;
+ (BOOL)deletePassWordForService:(NSString *)serviceName account:(NSString *)account;


/**
*/
+ (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;
+ (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account;


/**
*/
+ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account;
+ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error;


/**
*/
+ (NSArray *)allAccounts;
+ (NSArray *)allAccounts:(NSError *)error;
+ (NSArray *)accountsForService:(NSString *)serviceName;
+ (NSArray *)accountsForService:(NSString *)serviceName error:(NSError *__autoreleasing *)error;

@end


/*! Similar.h 
***********************************************
*** OSStatus values unique to Security APIs ***
***********************************************
enum
{
errSecSuccess                               = 0,        No error.
errSecUnimplemented                         = -4,       Function or operation not implemented.
errSecIO                                    = -36,     I/O error (bummers)
errSecOpWr                                  = -49,     file already open with with write permission
errSecParam                                 = -50,      One or more parameters passed to a function where not valid.
errSecAllocate                              = -108,     Failed to allocate memory.
errSecUserCanceled                          = -128,     User canceled the operation.
errSecBadReq                                = -909,     Bad parameter or invalid state for operation.
errSecInternalComponent                     = -2070,
errSecNotAvailable                          = -25291,   No keychain is available. You may need to restart your computer.
errSecDuplicateItem                         = -25299,   The specified item already exists in the keychain.
errSecItemNotFound                          = -25300,   The specified item could not be found in the keychain.
errSecInteractionNotAllowed                 = -25308,   User interaction is not allowed.
errSecDecode                                = -26275,   Unable to decode the provided data.
errSecAuthFailed                            = -25293,   The user name or passphrase you entered is not correct.
};
*/
typedef NS_ENUM(NSUInteger, LTKeychainErrorParam) {
LTKeychainErrorParamSuccess = 0,
LTKeychainErrorParamUnimplemented = -4,
LTKeychainErrorParamIO = -36,
LTKeychainErrorParamOpWr,
LTKeychainErrorParamParam,
LTKeychainErrorParamAllocate,
LTKeychainErrorParamUserCanceled,
LTKeychainErrorParamBadReq,
LTKeychainErrorParamInternalComponent,
LTKeychainErrorParamNotAvailable,
LTKeychainErrorParamDuplicationItem,
LTKeychainErrorParamItemNotFound,
LTKeychainErrorParamInteractionNotAllowed,
LTKeychainErrorParamDecode,
LTKeychainErrorParamAuthFailed,
};




/*!
@enum kSecAttrAccessible Value Constants
@discussion Predefined item attribute constants used to get or set values
in a dictionary. The kSecAttrAccessible constant is the key and its
value is one of the constants defined here.
When asking SecItemCopyMatching to return the item's data, the error
errSecInteractionNotAllowed will be returned if the item's data is not
available until a device unlock occurs.
@constant kSecAttrAccessibleWhenUnlocked Item data can only be accessed
while the device is unlocked. This is recommended for items that only
need be accesible while the application is in the foreground.  Items
with this attribute will migrate to a new device when using encrypted
backups.
@constant kSecAttrAccessibleAfterFirstUnlock Item data can only be
accessed once the device has been unlocked after a restart.  This is
recommended for items that need to be accesible by background
applications. Items with this attribute will migrate to a new device
when using encrypted backups.
@constant kSecAttrAccessibleAlways Item data can always be accessed
regardless of the lock state of the device.  This is not recommended
for anything except system use. Items with this attribute will migrate
to a new device when using encrypted backups.
@constant kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly Item data can
only be accessed while the device is unlocked. This class is only
available if a passcode is set on the device. This is recommended for
items that only need to be accessible while the application is in the
foreground. Items with this attribute will never migrate to a new
device, so after a backup is restored to a new device, these items
will be missing. No items can be stored in this class on devices
without a passcode. Disabling the device passcode will cause all
items in this class to be deleted.
@constant kSecAttrAccessibleWhenUnlockedThisDeviceOnly Item data can only
be accessed while the device is unlocked. This is recommended for items
that only need be accesible while the application is in the foreground.
Items with this attribute will never migrate to a new device, so after
a backup is restored to a new device, these items will be missing.
@constant kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly Item data can
only be accessed once the device has been unlocked after a restart.
This is recommended for items that need to be accessible by background
applications. Items with this attribute will never migrate to a new
device, so after a backup is restored to a new device these items will
be missing.
@constant kSecAttrAccessibleAlwaysThisDeviceOnly Item data can always
be accessed regardless of the lock state of the device.  This option
is not recommended for anything except system use. Items with this
attribute will never migrate to a new device, so after a backup is
restored to a new device, these items will be missing.
*/

typedef NS_ENUM(NSUInteger, LTKeychainAccessible) {
LTKeychainAccessibleNone = 0, ///< no value
LTKeychainAccessibleWhenUnlocked,
LTKeychainAccessibleAfterFirstUnlock,
LTKeychainAccessibleAlways,
LTKeychainAccessibleWhenPasscodeSetThisDeviceOnly,
LTKeychainAccessibleWhenUnlockedThisDeviceOnly,
LTKeychainAccessibleAfterFirstUnlockThisDeviceOnly,
LTKeychainAccessibleAlwaysThisDeviceOnly,
};



typedef NS_ENUM(NSUInteger, LTKeychainSynchronizationMode) {
LTKeychainSynchronizationModeAny = 0,

LTKeychainSynchronizationModeNO,

LTKeychainSynchronizationModeYES,

};



/**
Similar SecItem.h

@enum Attribute Key Constants
@discussion Predefined item attribute keys used to get or set values in a
dictionary. Not all attributes apply to each item class. The table
below lists the currently defined attributes for each item class:

kSecClassGenericPassword item attributes:
kSecAttrAccessible
kSecAttrAccessControl
kSecAttrAccessGroup
kSecAttrCreationDate
kSecAttrModificationDate
kSecAttrDescription
kSecAttrComment
kSecAttrCreator
kSecAttrType
kSecAttrLabel
kSecAttrIsInvisible
kSecAttrIsNegative
kSecAttrAccount
kSecAttrService
kSecAttrGeneric

typedef 
kSecAttrSynchronizable

*/

@interface LTKeychainQuery : NSObject

/** item attribute keys  */
@property (nonatomic, copy) NSString *service;

@property (nonatomic, copy) NSString *account;

@property (nonatomic, copy) NSString *label;

@property (nonatomic, copy) NSNumber *type;

@property (nonatomic, copy) NSString *creator;

@property (nonatomic, copy) NSNumber *comment;

@property (nonatomic, copy) NSNumber *description;

@property (nonatomic, readonly, strong) NSDate *modificationDate;

@property (nonatomic, readonly, strong) NSDate *creationDate;

@property (nonatomic, copy) NSString *accessGroup;

/** password */

/**
Convenience accessor for setting and getting a password string. Passes through
to `passwordData` using UTF-8 string encoding.
*/
@property (nonatomic, copy) NSString *password;

/** Root storage for password information */
@property (nonatomic, copy) NSData *passwordData;

/**
This property automatically transitions between an object and the value of
`passwordData` using NSKeyedArchiver and NSKeyedUnarchiver.
*/
@property (nonatomic, copy) id<NSCoding> passwordObject;

/** Accessible strategy */
@property (nonatomic) LTKeychainAccessible accessible; //  kSecAttrAccessible
/** 
Specifies a dictionary key whose value is
a CFBooleanRef indicating whether the item in question can be synchronized. 

1. To add a new item which can be synced to other devices, or to obtain
synchronizable results from a query, supply this key with a value of
“kCFBooleanTrue”

2. or has a value of
“kCFBooleanFalse”, then no synchronizable items will be added or returned.

3. A predefined value, "kSecAttrSynchronizableAny", may be provided instead of
kCFBooleanTrue if both synchronizable and non-synchronizable results are
desired.


return type CFTypeRef
*/
@property (nonatomic) LTKeychainSynchronizationMode synchronizable NS_AVAILABLE_IOS(7_0); //  kSecAttrSynchronizable



- (BOOL)saveItem:(NSError **)error;


- (BOOL)deleteItem:(NSError **)error;


- (NSArray *)selectAllItems:(NSError **)error;

- (BOOL)selectItem:(NSError **)error;




@end

NSUserDefault(偏好設(shè)置)

蘋果官方文檔:NSUserDeulat

NSUserDefaults適合存儲(chǔ)輕量級(jí)的本地?cái)?shù)據(jù),存儲(chǔ)保存引導(dǎo)頁(yè)頁(yè)的數(shù)據(jù)或用戶名、密碼,

特點(diǎn):

  • NSUserDefaults存儲(chǔ)的數(shù)據(jù)類型有:NSNumber、NSString、NSDate、NSArray、NSDictionary、BOOL.
    NSInteger、float、double
  • 調(diào)用 synchronize 立刻同步數(shù)據(jù)到文件內(nèi)

使用

LTNSUserDefault工具類

@interface LTNSUserDefault : NSObject


+ (void)addUserDefaultArrayFromStr:(NSString *)text;

+ (NSArray *)arrayForKey;

+ (void)addUserDefaultObject:(id)object   key:(NSString *)key ;

+ (void)removeUserDefaultObjectFromKey:(NSString *)key;

+ (nullable id)objectForKey:(NSString *)key;

+ (void)removeAllArray;

@end


#import "LTNSUserDefault.h"

static NSString *const kArrayKey = @"array";

@implementation LTNSUserDefault

+ (void)addUserDefaultArrayFromStr:(NSString *)text {
    
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    
    NSArray *array = [userDefaults arrayForKey:kArrayKey];

    if (array.count > 0) {
        
    } else {
        array = [NSArray array];
    }
    
    NSMutableArray *mutableArray = [array mutableCopy];
    [mutableArray addObject:text];
    if (mutableArray.count > 4) {
        [mutableArray removeObjectAtIndex:0];
    }
    
    [userDefaults setObject:mutableArray forKey:kArrayKey];
    [userDefaults synchronize];
}

+ (NSArray *)arrayForKey {
    NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
    
    
    return [userDefault arrayForKey:kArrayKey];
    
}

+ (void)removeAllArray {
    NSUserDefaults * userDefault = [NSUserDefaults standardUserDefaults];
    [userDefault removeObjectForKey:kArrayKey];
    [userDefault synchronize];
}


+ (void)removeUserDefaultObjectFromKey:(NSString *)key {
    NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
    
    [userDefault removeObjectForKey:key];
    
    [userDefault synchronize];
}

+ (id)objectForKey:(NSString *)key {
    NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
    
    return  [userDefault objectForKey:key];
    
}

+ (void)addUserDefaultObject:(id)object key:(NSString *)key {
    
    NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
    
    [userDefault setObject:object forKey:key];
    
    [userDefault synchronize];
}

NSUserDefaultExample:

#import <UIKit/UIKit.h>

@interface NSUserDefaultExample : UIViewController
@property (weak, nonatomic) IBOutlet UIButton *addBtn;
@property (weak, nonatomic) IBOutlet UIButton *deleteBtn;
@property (weak, nonatomic) IBOutlet UITextView *detailTextView;

@end




#import "NSUserDefaultExample.h"
#import "LTNSUserDefault.h"



static NSString *const kAccount = @"Account";
static NSString *const kStrName = @"Name";
static NSString *const kFloatValue = @"Float";
static NSString *const kDoubleValue = @"Double";
static NSString *const kDictionaryValue = @"Dictionary";
static NSString *const kArrayValue = @"NSArray";
static NSString *const kArray = @"array";


@interface NSUserDefaultExample ()

@end

@implementation NSUserDefaultExample

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
}

- (IBAction)addClick:(id)sender {
    
    [LTNSUserDefault addUserDefaultArrayFromStr:@"1800"];
    [LTNSUserDefault addUserDefaultArrayFromStr:@"1200"];
    [LTNSUserDefault addUserDefaultArrayFromStr:@"1803"];
    [LTNSUserDefault addUserDefaultArrayFromStr:@"1801"];
    [LTNSUserDefault addUserDefaultArrayFromStr:@"1000"];
    
    
    
    [LTNSUserDefault addUserDefaultObject:@"linmingjun" key:kAccount];
    [LTNSUserDefault addUserDefaultObject:@"1.0" key:kFloatValue];
    [LTNSUserDefault addUserDefaultObject:@"2.00" key:kDoubleValue];
    [LTNSUserDefault addUserDefaultObject:@{ @"Name" : @"linmingjun",
                                            @"blogs" : @"http://www.lxweimin.com"} key:kDictionaryValue];
    [LTNSUserDefault addUserDefaultObject:@"linmingjun" key:kAccount];
    
    
    NSMutableString *str = @"".mutableCopy;
    [str appendString:@"LTNSUserDefault:{\n"];
    
    [str appendFormat:@"addUserDefaultArrayFromStr: %@    \n",[LTNSUserDefault arrayForKey]];
    
    
    [str appendFormat:@"addUserDefaultObject: %@    \n",[LTNSUserDefault objectForKey:@"kAccount"]];
    
    [str appendString:@"}"];
    
    
    _detailTextView.text = str;
    
}


- (IBAction)deleteClick:(id)sender {
    
    
    NSMutableString *str = @"".mutableCopy;
    [str appendString:@"LTNSUserDefault:{\n"];

    [str appendFormat:@"account: %@    \n",[LTNSUserDefault objectForKey:kAccount]];
    
    
    [str appendString:@"}"];
    
    _detailTextView.text = str;
}

演示圖

程序運(yùn)行界面

Core Data

Reference: Core Data蘋果官方文檔
Core Data蘋果官方文檔

基本概念

Core Data is a framework that you use to manage the model layer objects in your application. It provides generalized and automated solutions to common tasks associated with object life cycle and object graph management, including persistence.

Core Data typically decreases by 50 to 70 percent the amount of code you write to support the model layer. This is primarily due to the following built-in features that you do not have to implement, test, or optimize:

Core Data是一個(gè)框架,用于在應(yīng)用程序中管理對(duì)象模型層。 它提供了廣義和自動(dòng)化來(lái)解決常見的任務(wù)與對(duì)象生命周期和有關(guān)包括持久性對(duì)象圖管理。

Core Data通常減少50 - 70%編寫的代碼來(lái)支持模型層。 這主要是由于以下的內(nèi)置功能,你不需要實(shí)現(xiàn),測(cè)試,或優(yōu)化:

功能

  • 更改跟蹤和內(nèi)置管理之外的撤銷和重做基本的文本編輯。
  • 維護(hù)變化的傳播,包括維護(hù)對(duì)象之間的一致性關(guān)系。
  • 對(duì)象延遲加載,部分物化期貨(斷層),即寫即拷實(shí)現(xiàn)數(shù)據(jù)共享,減少開銷。
  • 自動(dòng)驗(yàn)證的屬性值。 管理對(duì)象擴(kuò)展標(biāo)準(zhǔn)鍵值編碼驗(yàn)證方法,以確保在可接受范圍內(nèi)的單個(gè)值都位于可接受的范圍內(nèi),從而使值的組合有意義。
  • 模式遷移工具,簡(jiǎn)化模式變化和允許您執(zhí)行高效的就地模式遷移。
  • 可選的集成與應(yīng)用程序的控制器層支持用戶界面同步。
  • 分組、過(guò)濾和組織內(nèi)存中的數(shù)據(jù)和用戶界面。
  • 支持自動(dòng)將對(duì)象存儲(chǔ)在外部數(shù)據(jù)存儲(chǔ)庫(kù)。
  • 復(fù)雜的查詢編譯。 而不是編寫SQL,您可以創(chuàng)建復(fù)雜的查詢與獲* 取請(qǐng)求一個(gè)NSPredicate對(duì)象相關(guān)聯(lián)。
  • 跟蹤和樂(lè)觀鎖定支持自動(dòng)multiwriter解決沖突的版本。
  • 與OS X和iOS工具鏈的有效整合。

Core Data 具體的設(shè)計(jì)

Core Data核心類關(guān)系圖

在 CoreData 中,有四大核心類:

  • Persistent Store Coordinator:持久化存儲(chǔ)協(xié)調(diào)器;被管理對(duì)象模型和實(shí)體類之間的轉(zhuǎn)換協(xié)調(diào)器,可以為不同的被管理對(duì)象模型創(chuàng)建各自的持久化存儲(chǔ)協(xié)調(diào)器,一般情況下會(huì)合并多個(gè)被管理對(duì)象模型,然后創(chuàng)建一個(gè)持久化存儲(chǔ)協(xié)調(diào)器統(tǒng)一來(lái)管理

  • Managed Object Model:對(duì)象模型;對(duì)應(yīng) Xcode 中創(chuàng)建的 .xcdatamodeld 對(duì)象模型文件

  • Persistent Object Store:存儲(chǔ)持久對(duì)象的數(shù)據(jù)庫(kù),CoreData 提供數(shù)據(jù)存儲(chǔ)類型有四種,分別為 NSSQLiteStoreType (SQLite 數(shù)據(jù)庫(kù))NSXMLStoreType(XML 文件)、NSBinaryStoreType(二進(jìn)制文件)、NSInMemoryStoreType(內(nèi)存中),一般使用 NSSQLiteStoreType(SQLite數(shù)據(jù)庫(kù)存儲(chǔ))

  • Managed Object Context:管理對(duì)象上下文;負(fù)責(zé)實(shí)體對(duì)象與數(shù)據(jù)庫(kù)的數(shù)據(jù)交互

[創(chuàng)建一個(gè)托管對(duì)象模型]

創(chuàng)建一個(gè)托管對(duì)象模型和一個(gè)實(shí)體及其屬性,關(guān)鍵步驟如成績(jī)管理數(shù)據(jù)表關(guān)系圖所示

Students (學(xué)生表)
Scores (分?jǐn)?shù)表)
Courses (課程表)
Classes (班級(jí)表)

表設(shè)計(jì)

模型創(chuàng)建的過(guò)程中需要注意:

  • 所有的屬性應(yīng)該指定具體類型(盡管在SQLite中可以不指定),因?yàn)閷?shí)體對(duì)象會(huì)對(duì)應(yīng)生成ObjC模型類。
  • 實(shí)體對(duì)象中其他實(shí)體對(duì)象類型的屬性應(yīng)該通過(guò)Relationships建立,并且注意實(shí)體之間的對(duì)應(yīng)關(guān)系。

** Student與Course 之間多對(duì)多的“選修”聯(lián)系,即一個(gè)學(xué)生可以修選多門課程(一對(duì)多),一個(gè)課程提供多個(gè)同學(xué)選修(一對(duì)多) **

** Classes與Student存在一對(duì)多的“歸屬”關(guān)系,即一個(gè)班由多個(gè)學(xué)生組成,一個(gè)學(xué)生只能歸屬某個(gè)班級(jí) **

** 同一個(gè)學(xué)生在同一個(gè)學(xué)期下不允許修讀同一門課程(Score與 Course、Student分別都是一對(duì)一的關(guān)系 )**


成績(jī)管理數(shù)據(jù)表關(guān)系圖
  • NSSet 是一個(gè)無(wú)序的集合 ,可以使用NSSet 與NSArray根據(jù)需要轉(zhuǎn)換使用,其他使用方法 同單表使用的增刪查改,只是訪問(wèn)時(shí)多層訪問(wèn)
    .
  • @synthesize :沒(méi)有自定義存取方法時(shí), 編譯器系統(tǒng)自動(dòng)生成 getter/setter方法
  • @dynamic :不自動(dòng)生成getter/setter方法,由自己實(shí)現(xiàn)存取方法 或 在運(yùn)行時(shí)動(dòng)態(tài)創(chuàng)建綁定 ,如coreData在實(shí)現(xiàn)NSManagedObject子類時(shí)使用,由Core Data在運(yùn)行時(shí)動(dòng)態(tài)生成

Delete Rule

  • No Action:當(dāng)A被刪除時(shí),B對(duì)象不變,但會(huì)指向一個(gè)不存在的對(duì)象,一般不建議使用;

  • Nullify(作廢):當(dāng)A對(duì)象被刪除時(shí),B對(duì)象指向的A對(duì)象會(huì)置為空,如果A與B的關(guān)系式一對(duì)多,則是A對(duì)象從B容器中移除

  • Cascade(級(jí)聯(lián)):當(dāng)A對(duì)象被刪除時(shí),A對(duì)象指向的B對(duì)象也會(huì)被刪除;

  • Deny(拒絕):當(dāng)刪除指向?qū)ο驜存在的A對(duì)象時(shí),操作將會(huì)被拒絕;

初始化核心數(shù)據(jù)堆棧

Core Data堆棧框架對(duì)象的集合,被評(píng)估為Core Data的初始化的一部分,并在你的應(yīng)用程序和外部數(shù)據(jù)存儲(chǔ)對(duì)象之間的調(diào)解。核心數(shù)據(jù)棧處理所有與外部數(shù)據(jù)存儲(chǔ)區(qū)的交互,以便您的應(yīng)用程序可以集中于它的業(yè)務(wù)邏輯。堆棧包含三個(gè)主要目標(biāo):

  • 管理對(duì)象上下文(NSManagedObjectContext):就是你的應(yīng)用程序?qū)⒁宰罨?dòng)的對(duì)象

  • 持久性存儲(chǔ)協(xié)調(diào)員(NSPersistentStoreCoordinator):坐在中間的Core Data堆棧, 協(xié)調(diào)員負(fù)責(zé)實(shí)現(xiàn)實(shí)例的實(shí)體定義模型的內(nèi)部。 創(chuàng)建新實(shí)例的實(shí)體模型,從持久存儲(chǔ)和檢索現(xiàn)有實(shí)例,可以在磁盤或持久化存儲(chǔ)在內(nèi)存中。

  • 管理對(duì)象模型(NSManagedObjectModel):
    實(shí)例描述了數(shù)據(jù)訪問(wèn)的就是Core Data堆棧。 在Core Data的創(chuàng)建堆棧,NSManagedObjectModel通常被稱為“mom”,加載到內(nèi)存的堆棧的創(chuàng)建的第一步

在初始化Core Data堆棧訪問(wèn)你的應(yīng)用程序數(shù)據(jù)之前。堆的初始化準(zhǔn)備Core Data為數(shù)據(jù)請(qǐng)求和數(shù)據(jù)的創(chuàng)建。下面是如何創(chuàng)建一個(gè)Core Data堆棧的例子。

CoreDataManager:

重點(diǎn)

As part of the initialization of Core Data, assign the adding of the persistent store (NSPersistentStore
) to the persistent store coordinator (NSPersistentStoreCoordinator
) to a background queue. That action can take an unknown amount of time, and performing it on the main queue can block the user interface, possibly causing the application to terminate.

NSPersistentStore *store = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error];

蘋果官方文檔有提到指定添加持久性存儲(chǔ)(NSPersistentStore)到持久存儲(chǔ)協(xié)調(diào)員(NSPersistentStoreCoordinator)這個(gè)動(dòng)作要加入到后臺(tái)隊(duì)列。在主隊(duì)列執(zhí)行可能阻止用戶界面,可能會(huì)導(dǎo)致應(yīng)用程序終止。

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

@interface CoreDataManager : NSObject


@property (nonatomic, strong) NSManagedObjectContext *context;

- (NSManagedObjectContext *)initalizeCoreData;

+ (CoreDataManager *)sharedManager;

@end



#import "CoreDataManager.h"

@implementation CoreDataManager

- (instancetype)init {
    
    if (!(self = [super init])) {
        return nil;
    }
    
    _context =  [self initalizeCoreData];
    
    return self;
}

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

- (NSManagedObjectContext *)initalizeCoreData {
    /**
     創(chuàng)建模型比較簡(jiǎn)單,我們只需要增加一個(gè)新文件到我們的項(xiàng)目,在 Core Data 選項(xiàng)中選擇 Data Model template。這個(gè)模型文件將會(huì)被編譯成后綴名為 .momd 類型的文件,我們將會(huì)在運(yùn)行時(shí)加載這個(gè)文件來(lái)為持久化存儲(chǔ)創(chuàng)建需要用的 NSManagedObjectModel,模型的源碼是簡(jiǎn)單的 XML
     */
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Workspace" withExtension:@"momd"];
    
    // 指定本地資源文件路徑,打開「被管理對(duì)象模型」文件
    NSManagedObjectModel *mom = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    
    /** 第二種方式打開對(duì)象模型文件
     // 打開「被管理對(duì)象模型」文件,參數(shù)為 nil 則打開包中所有模型文件并合并成一個(gè)
     NSManagedObjectModel *model=[NSManagedObjectModel mergedModelFromBundles:nil];
     */
    
    NSAssert(mom != nil, @"Error initializing Managed Object Model");
    
    // 創(chuàng)建 持久化存儲(chǔ)協(xié)調(diào)器
    NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
    
    // 創(chuàng)建被管理對(duì)象上下文,參數(shù):NSMainQueueConcurrencyType 指定上下文將與主隊(duì)列有關(guān)。
    NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    // 設(shè)置被管理對(duì)象上下文的持久化存儲(chǔ)協(xié)調(diào)器
    [moc setPersistentStoreCoordinator:psc];
    [self setContext:moc];
    
    // 獲取數(shù)據(jù)庫(kù)保存路徑,通常保存沙盒 Documents 目錄下
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSURL *documentURL = [[fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
    // SQLite 在應(yīng)用程序的文件目錄
    NSURL *storeURL = [documentURL URLByAppendingPathComponent:@"DataModel.sqlite"];
    
    // 將持久化存儲(chǔ)協(xié)調(diào)器設(shè)置到被管理對(duì)象上下文后,開啟線程異步將Block作為一個(gè)任務(wù)加入到全局并發(fā)隊(duì)列執(zhí)行(設(shè)置隊(duì)列優(yōu)先級(jí)為默認(rèn))
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSError *error = nil;
        // 獲取被管理對(duì)象上下文的持久化存儲(chǔ)協(xié)調(diào)器
        NSPersistentStoreCoordinator *psc = [[self context] persistentStoreCoordinator];
        // 通過(guò)獲取持久化存儲(chǔ)協(xié)調(diào)器,添加SQLite 類型的持久化存儲(chǔ)
        // 指定添加持久性存儲(chǔ)(NSPersistentStore)到持久存儲(chǔ)協(xié)調(diào)員(NSPersistentStoreCoordinator)這個(gè)動(dòng)作要加入到后臺(tái)隊(duì)列。在主隊(duì)列執(zhí)行可以阻止用戶界面,可能會(huì)導(dǎo)致應(yīng)用程序終止。
        NSPersistentStore *store = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error];
        NSAssert(store != nil, @"Error initializing PSC: %@\n%@", [error localizedDescription], [error userInfo]);
    });
    
    
    return moc;
}

/*
 NSPersistentStoreCoordinator.h
 // Persistent store types supported by Core Data: 持久化存儲(chǔ)支持core data的類型
 COREDATA_EXTERN NSString * const NSSQLiteStoreType NS_AVAILABLE(10_4, 3_0);
 COREDATA_EXTERN NSString * const NSXMLStoreType NS_AVAILABLE(10_4, NA);
 COREDATA_EXTERN NSString * const NSBinaryStoreType NS_AVAILABLE(10_4, 3_0);
 COREDATA_EXTERN NSString * const NSInMemoryStoreType NS_AVAILABLE(10_4, 3_0);
 
 */


@end





#import "CoreDataWorkspaceService.h"

#import "CoreDataManager.h"

static NSString *const kStudents = @"Students";

@implementation CoreDataWorkspaceService

+ (CoreDataWorkspaceService *)sharedService {
    
    static CoreDataWorkspaceService *service;
    
    static dispatch_once_t onceToken;
    
    dispatch_once(&onceToken,^{
        service = [CoreDataWorkspaceService new];
        service.context = [CoreDataManager sharedManager].context;
        
        NSLog(@"沙盒路徑:%@",NSHomeDirectory());
    });
    
    return service;
}

@end


@implementation StudentsSerivce

+ (StudentsSerivce *)sharedService {
    
    static StudentsSerivce *service;
    
    static dispatch_once_t onceToken;
    
    dispatch_once(&onceToken,^{
        service = [StudentsSerivce new];
        service.context = [CoreDataManager sharedManager].context;
        
        NSLog(@"沙盒路徑:%@",NSHomeDirectory());
    });
    
    return service;
}

- (NSManagedObjectContext *)context {
    return [CoreDataManager sharedManager].context;
}



/**
 @property (nullable, nonatomic, retain) NSString *studentDes;
 @property (nullable, nonatomic, retain) NSString *studentName;
 @property (nullable, nonatomic, retain) NSString *studentPwd;
 @property (nullable, nonatomic, retain) Classes *student_class;
 @property (nullable, nonatomic, retain) NSSet<Courses *> *student_course;
 @property (nullable, nonatomic, retain) Scores *student_score;
 */
- (BOOL)addStudents:(Students *)students {
    BOOL isSuccess = NO;
    Students *s = [NSEntityDescription insertNewObjectForEntityForName:@"Students" inManagedObjectContext:[self context]];
    
    s.studentId = students.studentId;
    s.studentName = students.studentName;
    s.studentPwd = students.studentPwd;
    s.student_class = students.student_class;
    s.student_course = students.student_course;
    s.student_score = students.student_score;
    NSError *error = nil;
    isSuccess = [[self context] save:&error];
    NSAssert(isSuccess, @"Error info : %@\n",  [error userInfo]);
    return isSuccess;
}

- (BOOL)addStudentWithName:(NSString *)studentId studentName:(NSString *)studentName studentDesc:(NSString *)studentDesc studentPwd:(NSString *)studentPwd{
    BOOL isSuccess = NO;
    Students *s = [NSEntityDescription insertNewObjectForEntityForName:@"Students" inManagedObjectContext:[self context]];
    
    s.studentId = studentId;
    s.studentName = studentName;
    s.studentDes = studentDesc;
    s.studentPwd = studentPwd;
//    s.student_class = students.student_class;
//    s.student_course = students.student_course;
//    s.student_score = students.student_score;
    NSError *error = nil;
    isSuccess = [[self context] save:&error];
    NSAssert(isSuccess, @"Error info : %@\n",  [error userInfo]);
    return isSuccess;
}

- (BOOL)removeStudentsByID:(NSString *)studentId {
    BOOL isSuccess = NO;
    
    Students *s = [self getStudentsByID:studentId];
    
    if (s) {
        NSError *error = nil;
//        [[self context] deletedObjects:s];
        [[self context] deleteObject:s];
        isSuccess = [[self context] save:&error];
        NSAssert(isSuccess, @"delete Error info : %@\n",  [error localizedDescription]);
    }
    
    
    return isSuccess;
}

- (void)removeStudentsForID:(NSString *)studentId {
    Students *s = [self getStudentsByID:studentId];
    
    if (s) {
        NSError *error = nil;
//                [[self context] deletedObjects:s];
        [[self context] deleteObject:s];
        BOOL isSuccess =  [[self context] save:&error];
    }

}

/**
 @property (nullable, nonatomic, retain) NSString *studentDes;
 @property (nullable, nonatomic, retain) NSString *studentName;
 @property (nullable, nonatomic, retain) NSString *studentPwd;
 @property (nullable, nonatomic, retain) Classes *student_class;
 @property (nullable, nonatomic, retain) NSSet<Courses *> *student_course;
 @property (nullable, nonatomic, retain) Scores *student_score;
 */
- (BOOL)updateStudent:(Students *)student {
    BOOL isSuccess = NO;
    
    Students *s = [self getStudentsByID:student.studentId];
    
    if (s) {
        NSError *error = nil;
        
        s.studentDes = student.studentDes;
        s.studentName = student.studentName;
        s.studentPwd = student.studentPwd;
        s.student_class = student.student_class;
        s.student_course = student.student_course;
        s.student_score = student.student_score;
        
        
        [[self context] deleteObject:s];
        isSuccess = [[self context] save:&error];
        NSAssert(isSuccess, @"delete Error info : %@\n",  [error localizedDescription]);
    }
    
    
    return isSuccess;

}

- (NSMutableArray *)getAllStudents {
    NSMutableArray *arrayRuture = @[].mutableCopy;
    
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:kStudents];
    
    NSError *error = nil;
    
    NSArray *array = [[self context] executeFetchRequest:request error:&error];
    
    if (!error) {
        for (Students *s in array) {
            [arrayRuture addObject:s];
        }
    }
    
    NSAssert(arrayRuture.count, @"Error fetching Students objects:Error Info:%@ \n",  [error localizedDescription]);
    
    return arrayRuture;
}

- (Students *)getStudentsByName:(NSString *)name {
    Students *studentsInfo;
    // 實(shí)例化查詢
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:kStudents];
    // 使用謂詞查詢是基于 Keypath 查詢的,如果鍵是一個(gè)變量,格式化字符串時(shí)需要使用 %K 而不是 %@
    request.predicate = [NSPredicate predicateWithFormat:@"%K = %@","studentName",name];
    NSError *error = nil;
    // 執(zhí)行查詢
    NSArray *arrayResult = [self.context executeFetchRequest:request error:&error];
    
    if (!error) {
        studentsInfo = [arrayResult firstObject];
    } else {
        NSLog(@"select data Error,Error Info:%@", error.localizedDescription);
    }
    
    return studentsInfo;

}

- (Students *)getStudentsByID:(NSString *)studentId {
    
    Students *studentsInfo;
    // 實(shí)例化查詢
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:kStudents];
    // 使用謂詞查詢是基于 Keypath 查詢的,如果鍵是一個(gè)變量,格式化字符串時(shí)需要使用 %K 而不是 %@

    request.predicate = [NSPredicate predicateWithFormat:@"%K=%@",@"studentId",studentId];
    NSError *error = nil;
    // 執(zhí)行查詢
    NSArray *arrayResult = [[self context] executeFetchRequest:request error:&error];
    
    if (!error) {
        studentsInfo = [arrayResult firstObject];
    } else {
        NSLog(@"select data Error,Error Info:%@", error.localizedDescription);
    }

    return studentsInfo;
    
}
@end


- (Students *)getStudentsByName:(NSString *)name;



@end

生成的數(shù)據(jù)庫(kù)相關(guān)文件

Reference:http://www.sqlite.org/fileformat2.html#walindexformat
databaseName.db:數(shù)據(jù)庫(kù)文件
databaseName.db-shm Shared Memory:數(shù)據(jù)庫(kù)預(yù)寫式日志索引文件
databaseName.db-wal Write-Ahead Log:數(shù)據(jù)庫(kù)預(yù)寫式日志文件

具體操作界面圖

生成數(shù)據(jù)庫(kù)表字段

Z_PK 是表的主鍵,從1開始遞增,唯一值
Z_ENT 表在xcdatamodel 中的索引值,創(chuàng)建了4個(gè)表,Z_ENT的區(qū)間就是[1,4 ]
Z_OPT 表示的是每條數(shù)據(jù)被操作的次數(shù),初始化值為1,只要是增刪改查都會(huì)加1

打印SQL語(yǔ)句

在 Produc-Scheme-Edit Scheme-Run菜單下的Arguments中的Arguments Passed On Launch下按先后順序新增兩個(gè)項(xiàng),內(nèi)容分別為-com.apple.CoreData.SQLDebug和1


D29FC78D-2738-476D-898D-A752B69C56FA.png
打印SQL

CoreData + UITableView的使用

如下圖效果圖,具體實(shí)現(xiàn)詳見下載源碼


效果圖

NSFetchedResultsController+CoreData + UITableView的使用

CoreData為UITableView提供數(shù)據(jù)的時(shí)候,使用NSFetchedReslutsController讀取數(shù)據(jù),能最大效率的讀取數(shù)據(jù)庫(kù)并且提供高效的查詢分類功能,數(shù)據(jù)以分組的形式 展示在UITableView中.

特點(diǎn):

  • 檢測(cè)數(shù)據(jù)變化
  • 當(dāng)數(shù)據(jù)發(fā)生變化時(shí),點(diǎn)對(duì)點(diǎn)的更新TableView,大大的提高了更新效率
  • 點(diǎn)對(duì)點(diǎn)的更新分組

使用

sectionNameKeyPath: 獲取的對(duì)象進(jìn)行分組的參數(shù).
cacheName:返回使用給定參數(shù)初始化的獲取請(qǐng)求控制器,應(yīng)用是用來(lái)處理重復(fù)的任務(wù),比如說(shuō)設(shè)置分組或者排列數(shù)據(jù)等

創(chuàng)建一個(gè)NSFetchedResultsController控制器設(shè)置分組、排序、緩存:
- (void)initializeFetchedResultsController {
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:kStudents];

    NSSortDescriptor *lastNameSort = [NSSortDescriptor sortDescriptorWithKey:@"studentId" ascending:YES];
    
    
    [request setSortDescriptors:@[lastNameSort]];
    
    CoreDataManager *manager = [CoreDataManager sharedManager];
    
    NSManagedObjectContext *moc = manager.context;
    
    
    NSFetchedResultsController * resultController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:moc sectionNameKeyPath:@"student_class" cacheName:@"rootCache"];
    resultController.delegate = self;
    
    self.fetchedResultsController = resultController;
    
    
    NSError *error = nil;
    if (![[self fetchedResultsController] performFetch:&error]) {
        NSLog(@"Failed to initialize FetchedResultsController: %@\n%@", [error localizedDescription], [error userInfo]);
        abort();
    }

}

當(dāng)數(shù)據(jù)發(fā)生變化時(shí),點(diǎn)對(duì)點(diǎn)的更新tableview

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath
{
    switch(type) {
        case NSFetchedResultsChangeInsert:
            [_contentView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
        case NSFetchedResultsChangeDelete:
            [_contentView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
        case NSFetchedResultsChangeUpdate:
            [self configureCell:[self.contentView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            break;
        case NSFetchedResultsChangeMove:
            [_contentView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
            [_contentView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}

點(diǎn)對(duì)點(diǎn)的更新section

- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
    switch(type) {
        case NSFetchedResultsChangeInsert:
            [_contentView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;
        case NSFetchedResultsChangeDelete:
            [_contentView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;
        case NSFetchedResultsChangeMove:
        case NSFetchedResultsChangeUpdate:
            break;
    }
}

顯示分組
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
id< NSFetchedResultsSectionInfo> sectionInfo = [[self fetchedResultsController] sections][section];
NSArray *array = [sectionInfo objects];

    Students *s = [array firstObject];
    
    Classes *c = s.student_class;
    
    return c.classNames;
}

將NSFetchedResultsController與UITableView數(shù)據(jù)源相結(jié)合
#pragma mark - Table view data source

- (void)configureCell:(LTTableViewCell *)cell atIndexPath:(NSIndexPath*)indexPath
{
    
    if (!cell) {
//        cell = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([LTTableViewCell class]) owner:nil options:nil] lastObject];
        cell = [[LTTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellReuseIdentifier];
    }
    Students *student = [[self fetchedResultsController] objectAtIndexPath:indexPath];
    cell.tag = [student.studentId integerValue]; // 存儲(chǔ) ID 用于「修改」和「刪除」記錄操作
    cell.model = student;

}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 80.0;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    LTTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellReuseIdentifier];
    [self configureCell:cell atIndexPath:indexPath];
    cell.rightUtilityButtons = [self rightButtons];
    cell.delegate = self;
    
    return cell;
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    NSLog(@"[[[self fetchedResultsController] sections] count]---%ld",[[[self fetchedResultsController] sections] count]);
    return [[[self fetchedResultsController] sections] count];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    
    id< NSFetchedResultsSectionInfo> sectionInfo = [[self fetchedResultsController] sections][section];
    return [sectionInfo numberOfObjects];
}

效果圖

程序運(yùn)行界面

未完待續(xù)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容