iOS程序设计
-数据持久化
        讲师: 谭奇宇
Email&MSN: qiyutan@hotmail.com
数据持久化的定义
二进制文件
 C语言函数库: fwrite, fread


 iOS平台:NSFileManger, NSData
属性列表
 定义
 具有层次结构的对象列表,可以通过
 NSPropertyListSerialization实现持久化。

 存储格式
 1. 标准XML
 2. 二进制
 3. 兹容旧格式(只读)
属性列表
 支持数据类型
属性列表
 写数据
 NSMutableDictionary *info = [NSMutableDictionary dictionary];

 [info setObject:@"Tom" forKey:@"Player"];
 [info setObject:[NSNumber numberWithInt:3000] forKey:@"Score"];

 [info writeToFile:fileName atomically:YES];
 或者
 NSData *data = [NSPropertyListSerialization dataFromPropertyList:info
 format:NSPropertyListXMLFormat_v1_0 errorDescription:nil];
 [data writeToFile:fileName atomically:YES];

 注意:NSDictionary的key对象必须实现NSCopying协议。若作为
 属性列表,必须得是NSString,即使NSNumber都不可以, 因为key
 值在XML中的元素类型只有1个<key>,无法区分类型。
属性列表
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
         <key>Player</key>
         <string>Tom</string>
         <key>Score</key>
         <integer>3000</integer>
</dict>
</plist>
属性列表
 读数据
 NSDictionary *info = [NSDictionary
 dictionaryWithContentsOfFile:fileName];
 或者
 NSData *data = [NSData dataWithContentsOfFile:fileName];
 NSDictionary *info = [NSPropertyListSerialization
                           propertyListFromData:data
                           mutabilityOption:NSPropertyListImmutable
                              format:&format
                           errorDescription:&error];
属性列表
@interface Player : NSObject
@property(retain) NSString *name;
@property(assign) int score;
@end

Player *p = [[[Player alloc] init] autorelease];
p.name = @"Tom";
p.score = 3000;
NSMutableDictionary *info = [NSMutableDictionary dictionary];
[info setObject:p forKey:@"Player"];
[info writeToFile:fileName atomically:YES]; //写操作失败
NSUserDefaults
 以plist格式存储<key, value>配置信息

 存储位置

$HOME/Library/Preferences/<ApplicationBundleIdentifer>.plist
NSUserDefaults
 初始化配置信息

if(![NSStandardUserDefaults objectForKey:@"DefaultLevel"]){
      [NSStandardUserDefaults setObject:[NSNumber
numberWithInt:1] forKey:@"DefaultLevel"];
}
…

NSDictionary *defaultSettings = [NSDictionary
dictionaryWithContentsOfFile:plistPathInBundle];
[NSStandardUserDefaults registerDefaults:defaultSettings];
NSUserDefaults
 不宜存放以下信息
 1. 敏感数据
   比如:帐号,密码
 2. 大数据量信息

 思考
 如何保证程序的配置信息在越狱iOS上不会被恶意修改?
 大数据量该如何存取?
NSJSONSerialization
 iOS 5.0版本引入,替代JSONKit等开源工具

 将NSArray, NSDictionary转化成JSON数据格式

 NSArray, NSDictionary中的元素必须是NSString,
   NSNumber, NSArray, NSDictionary, 或 NSNull

 NSDictionary的key必须是NSString

 用法
  NSArray *players = …;
  [NSJSONSerialization dataWithJSONObject:players
  options:NSJSONWritingPrettyPrinted error:&error];
  …
  players = [NSJSONSerialization JSONObjectWithData:data
  options:NSJSONReadingMutableContainers error:&error];
对象归档
 将对象及对象关系序列化为二进制流

 一种对象持久化通用解决方案
对象归档
 NSCoder
  1. 序列形式
    NSArchiver, NSUnarchiver (iOS不支持)
  2. Key-Value形式
    NSKeyedArchiver, NSKeyedUnarchiver
对象归档
 归档(Archive)

  NSMutableData *data = [NSMutableData data];
  NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc]
  initForWritingWithMutableData:data];
  [archiver encodeObject:player forKey:@”Player"];
  //…
  [archiver finishEncoding];
  [data writeToFile:archivePath atomically:YES];


  [NSKeyedArchiver archiveRootObject:player
  toFile:archivePath];
对象归档
 解档(Unarchive)
  NSData *data = [NSData dataWithContentsOfFile:archivePath];
  unarchiver = [[NSKeyedUnarchiver alloc]
  initForReadingWithData:data];
  player = [unarchiver decodeObjectForKey:@”player"];
  [unarchiver finishDecoding];
  [unarchiver release];


  player = [NSKeyedUnarchiver
  unarchiveObjectWithFile:archivePath];
对象归档
 NSCoding协议

@interface Player : NSObject<NSCoding>
//…

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super init];
//如果父类就已经实现了NSCoding协议
    //self = [super initWithCoder:aDecoder];
    if(self){
        self.name = [aDecoder decodeObjectForKey:@"Name"];
        self.score = [aDecoder decodeIntForKey:@"Score"];
    }
    return self;
}
对象归档
 NSCoding协议


 - (void)encodeWithCoder:(NSCoder *)aCoder
 {
    //[super encodeWithCoder];
    [aCoder encodeObject:self.name forKey:@"Name"];
    [aCoder encodeInt:self.score forKey:@"Score"];
 }
对象归档
 NSNumber, NSString, NSDate等,以及NSArray,
  NSDictionary, NSSet集合类均实现了NSCoding协议


   NSMutableArray *players = [NSMutableArray array];
   [players addObject:player1];
   …
   //会调用数组中每个player对象的encoder
   [NSKeyedArchiver archiveRootObject:players
   toFile:archivePath];

   //会调用decoder生成每个player对象
   players = [NSKeyedUnarchiver
   unarchiveObjectWithFile:archivePath];
SQLite
 C语言接口,支持通用的SQL操作

 无服务器

 单文件

 内存占用小,速度快,比较可靠

 不适合存储多媒体数据:音视频、图片资源

 并发处理方面比较弱(对手机端影响不大)
SQLite
 接口举例

1. 打开数据库
   int sqlite3_open(constchar*filename,sqlite3**db);

1. 执行SQL语句
   int sqlite3_exec(sqlite3 *db, const char *sql, int (*callback)(void
   *, int, char **, char **), void *context, char **error);

2. 读取查询结果
   int mycallback(void*context,int count, char**values, char**cols);

3. 关闭数据库
   int sqlite3_close(sqlite3 *db);
Core Data
 为何需要使用Core Data
  1. 开发工作量和效率
  2. 后期数据维护和升级
  3. 性能优化
  4. 线程安全
  5. 更好地支持iCloud
Core Data
 数据管理框架,提供一套OC语法的接口

 不是通常意义上的数据库,存储模型可以是SQLite,
 XML等
Core Data
 实体-关系模型
                            NSEntityDescription




                                                   NSAttributeDescription




     NSRelationshipDescription


                                        NSManagedObjectModel
Core Data
 NSManagedObject-描述数据对象的类型
  1. 数据对象均为NSManagedObject类型或派生类型
  2. 利用Xcode可以自动生成表示Player, Team类代码
Core Data
 自动生成的Player类

 @interface Player : NSManagedObject

 @property (nonatomic, retain) NSNumber * name;
 @property (nonatomic, retain) NSString * score;
 @property (nonatomic, retain) NSManagedObject *team;

 @end
Core Data
 自动生成的Player类

 @interface Player : NSManagedObject

 @property (nonatomic, retain) NSNumber * name;
 @property (nonatomic, retain) NSString * score;
 @property (nonatomic, retain) NSManagedObject *team;

 @end

           @implementation Player
           //dynamic指示符用于告诉编译器运行时会生成对应的读取方法
           @dynamic name;
           @dynamic score;
           @dynamic team;

           @end
Core Data
 自动生成的Team类
@class Player;

@interface Team : NSManagedObject

@property (nonatomic, retain) NSString * name;
@property (nonatomic, retain) NSSet *players;
@end

@interface Team (CoreDataGeneratedAccessors)
//声明对应的添加和删除Player的方法,也是运行时才
实现
- (void)addPlayersObject:(Player *)value;    @implementation Team
- (void)removePlayersObject:(Player *)value;
- (void)addPlayers:(NSSet *)values;          @dynamic name;
- (void)removePlayers:(NSSet *)values;       @dynamic players;

@end                                             @end
Core Data
 NSManagedObject遵循KVC
  NSString *name = [managedObject valueForKey:@”name”];
  等价于
  NSString *name = player.name;

 什么情况下需要继承NSManagedContext?
  1. 代码更直观简洁
  2. 添加方法或属性
     比如,可以增加portrait方法,根据player.name属性,
     查询文件返回对应的UIImage对象
Core Data
      读取与存储数据
                        数据对象
     NSManagedObject      …          NSManagedObject


数据对象管理器:增,                                         NSManagedObjectContex
删,改,查,撤销/重
           NSManagedObjectContext             …
                                                            t
做

 数据字典(一般加载合并
 Bundle中的所有model)                NSPersistentStoreCoordinator
 NSManagedObjectMode
          l


          数据文          NSPersistentStore    …      NSPersistentStore
          件
Core Data
 初始化工作

model = [NSManagedObjectModel mergedModelFromBundles:nil]; //nil
表示从mainbundle中加载合并所有model

coordinator = [[NSPersistentStoreCoordinator alloc]
initWithManagedObjectModel:model];
 [coordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil URL:storeUrl options:options error:&error];

context = [[NSManagedObjectContext alloc] init];
context.persistentStoreCoordinator = coordinator;

以上操作,iOS5.0下,等价于
UIManagedDocument *document = [[UIManagedDocument alloc]
initWithFileURL:storeUrl];
Core Data
 增加记录

  player = [NSEntityDescription
            insertNewObjectForEntityForName:@"Player”
            inManagedObjectContext:context];
  team = [NSEntityDescription
            insertNewObjectForEntityForName:@"Team”
            inManagedObjectContext:context];
  team.name = @"TheBest";
  player.name = @"Tom";
  player.score = [NSNumber numberWithInt:3000];
  player.team = team;
  if(![context save:&error]){
            NSLog(@"%@", error);
  }
Core Data
 删除记录
 [context deleteObject:player];

 查询记录
 NSFetchRequest *request = [NSFetchRequest
 fetchRequestWithEntityName:@"Player"];
 request.predicate = [NSPredicate
 predicateWithFormat:@"score > 2000"];
 NSSet *players = [context executeFetchRequest:request
 error:&error];

 修改记录
 player.score = [NSNumber numberWithInt:3500];
Core Data
 更多内容
 1. 版本升级 NSMappingModel
 2. NSManagedObjectContext与多线程
 3. NSManagedObject的生命周期
 4. iOS5.0, UIManagedDocument, iCloud
 5. NSFetchedResultsController与UITableView
 6. 内存使用与性能优化
 7. Cocoa Binding (Mac OS X)
数据持久化
 持久化方案的选择
  数据类型
  读写频率
  数据大小
  缓存机制
  安全性
  可扩展性
  开发效率
谢谢聆听!

iOS程序设计-数据持久化

  • 1.
    iOS程序设计 -数据持久化 讲师: 谭奇宇 Email&MSN: qiyutan@hotmail.com
  • 3.
  • 4.
    二进制文件  C语言函数库: fwrite,fread  iOS平台:NSFileManger, NSData
  • 5.
    属性列表  定义 具有层次结构的对象列表,可以通过 NSPropertyListSerialization实现持久化。  存储格式 1. 标准XML 2. 二进制 3. 兹容旧格式(只读)
  • 6.
  • 7.
    属性列表  写数据 NSMutableDictionary*info = [NSMutableDictionary dictionary]; [info setObject:@"Tom" forKey:@"Player"]; [info setObject:[NSNumber numberWithInt:3000] forKey:@"Score"]; [info writeToFile:fileName atomically:YES]; 或者 NSData *data = [NSPropertyListSerialization dataFromPropertyList:info format:NSPropertyListXMLFormat_v1_0 errorDescription:nil]; [data writeToFile:fileName atomically:YES]; 注意:NSDictionary的key对象必须实现NSCopying协议。若作为 属性列表,必须得是NSString,即使NSNumber都不可以, 因为key 值在XML中的元素类型只有1个<key>,无法区分类型。
  • 8.
    属性列表 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPEplist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Player</key> <string>Tom</string> <key>Score</key> <integer>3000</integer> </dict> </plist>
  • 9.
    属性列表  读数据 NSDictionary*info = [NSDictionary dictionaryWithContentsOfFile:fileName]; 或者 NSData *data = [NSData dataWithContentsOfFile:fileName]; NSDictionary *info = [NSPropertyListSerialization propertyListFromData:data mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&error];
  • 10.
    属性列表 @interface Player :NSObject @property(retain) NSString *name; @property(assign) int score; @end Player *p = [[[Player alloc] init] autorelease]; p.name = @"Tom"; p.score = 3000; NSMutableDictionary *info = [NSMutableDictionary dictionary]; [info setObject:p forKey:@"Player"]; [info writeToFile:fileName atomically:YES]; //写操作失败
  • 11.
    NSUserDefaults  以plist格式存储<key, value>配置信息 存储位置 $HOME/Library/Preferences/<ApplicationBundleIdentifer>.plist
  • 12.
    NSUserDefaults  初始化配置信息 if(![NSStandardUserDefaults objectForKey:@"DefaultLevel"]){ [NSStandardUserDefaults setObject:[NSNumber numberWithInt:1] forKey:@"DefaultLevel"]; } … NSDictionary *defaultSettings = [NSDictionary dictionaryWithContentsOfFile:plistPathInBundle]; [NSStandardUserDefaults registerDefaults:defaultSettings];
  • 13.
    NSUserDefaults  不宜存放以下信息 1.敏感数据 比如:帐号,密码 2. 大数据量信息  思考 如何保证程序的配置信息在越狱iOS上不会被恶意修改? 大数据量该如何存取?
  • 14.
    NSJSONSerialization  iOS 5.0版本引入,替代JSONKit等开源工具 将NSArray, NSDictionary转化成JSON数据格式  NSArray, NSDictionary中的元素必须是NSString, NSNumber, NSArray, NSDictionary, 或 NSNull  NSDictionary的key必须是NSString  用法 NSArray *players = …; [NSJSONSerialization dataWithJSONObject:players options:NSJSONWritingPrettyPrinted error:&error]; … players = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
  • 15.
  • 16.
    对象归档  NSCoder 1. 序列形式 NSArchiver, NSUnarchiver (iOS不支持) 2. Key-Value形式 NSKeyedArchiver, NSKeyedUnarchiver
  • 17.
    对象归档  归档(Archive) NSMutableData *data = [NSMutableData data]; NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; [archiver encodeObject:player forKey:@”Player"]; //… [archiver finishEncoding]; [data writeToFile:archivePath atomically:YES]; [NSKeyedArchiver archiveRootObject:player toFile:archivePath];
  • 18.
    对象归档  解档(Unarchive) NSData *data = [NSData dataWithContentsOfFile:archivePath]; unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; player = [unarchiver decodeObjectForKey:@”player"]; [unarchiver finishDecoding]; [unarchiver release]; player = [NSKeyedUnarchiver unarchiveObjectWithFile:archivePath];
  • 19.
    对象归档  NSCoding协议 @interface Player: NSObject<NSCoding> //… - (id)initWithCoder:(NSCoder *)aDecoder { self = [super init]; //如果父类就已经实现了NSCoding协议 //self = [super initWithCoder:aDecoder]; if(self){ self.name = [aDecoder decodeObjectForKey:@"Name"]; self.score = [aDecoder decodeIntForKey:@"Score"]; } return self; }
  • 20.
    对象归档  NSCoding协议 -(void)encodeWithCoder:(NSCoder *)aCoder { //[super encodeWithCoder]; [aCoder encodeObject:self.name forKey:@"Name"]; [aCoder encodeInt:self.score forKey:@"Score"]; }
  • 21.
    对象归档  NSNumber, NSString,NSDate等,以及NSArray, NSDictionary, NSSet集合类均实现了NSCoding协议 NSMutableArray *players = [NSMutableArray array]; [players addObject:player1]; … //会调用数组中每个player对象的encoder [NSKeyedArchiver archiveRootObject:players toFile:archivePath]; //会调用decoder生成每个player对象 players = [NSKeyedUnarchiver unarchiveObjectWithFile:archivePath];
  • 22.
    SQLite  C语言接口,支持通用的SQL操作  无服务器 单文件  内存占用小,速度快,比较可靠  不适合存储多媒体数据:音视频、图片资源  并发处理方面比较弱(对手机端影响不大)
  • 23.
    SQLite  接口举例 1. 打开数据库 int sqlite3_open(constchar*filename,sqlite3**db); 1. 执行SQL语句 int sqlite3_exec(sqlite3 *db, const char *sql, int (*callback)(void *, int, char **, char **), void *context, char **error); 2. 读取查询结果 int mycallback(void*context,int count, char**values, char**cols); 3. 关闭数据库 int sqlite3_close(sqlite3 *db);
  • 24.
    Core Data  为何需要使用CoreData 1. 开发工作量和效率 2. 后期数据维护和升级 3. 性能优化 4. 线程安全 5. 更好地支持iCloud
  • 25.
    Core Data  数据管理框架,提供一套OC语法的接口 不是通常意义上的数据库,存储模型可以是SQLite, XML等
  • 26.
    Core Data  实体-关系模型 NSEntityDescription NSAttributeDescription NSRelationshipDescription NSManagedObjectModel
  • 27.
    Core Data  NSManagedObject-描述数据对象的类型 1. 数据对象均为NSManagedObject类型或派生类型 2. 利用Xcode可以自动生成表示Player, Team类代码
  • 28.
    Core Data  自动生成的Player类 @interface Player : NSManagedObject @property (nonatomic, retain) NSNumber * name; @property (nonatomic, retain) NSString * score; @property (nonatomic, retain) NSManagedObject *team; @end
  • 29.
    Core Data  自动生成的Player类 @interface Player : NSManagedObject @property (nonatomic, retain) NSNumber * name; @property (nonatomic, retain) NSString * score; @property (nonatomic, retain) NSManagedObject *team; @end @implementation Player //dynamic指示符用于告诉编译器运行时会生成对应的读取方法 @dynamic name; @dynamic score; @dynamic team; @end
  • 30.
    Core Data  自动生成的Team类 @classPlayer; @interface Team : NSManagedObject @property (nonatomic, retain) NSString * name; @property (nonatomic, retain) NSSet *players; @end @interface Team (CoreDataGeneratedAccessors) //声明对应的添加和删除Player的方法,也是运行时才 实现 - (void)addPlayersObject:(Player *)value; @implementation Team - (void)removePlayersObject:(Player *)value; - (void)addPlayers:(NSSet *)values; @dynamic name; - (void)removePlayers:(NSSet *)values; @dynamic players; @end @end
  • 31.
    Core Data  NSManagedObject遵循KVC NSString *name = [managedObject valueForKey:@”name”]; 等价于 NSString *name = player.name;  什么情况下需要继承NSManagedContext? 1. 代码更直观简洁 2. 添加方法或属性 比如,可以增加portrait方法,根据player.name属性, 查询文件返回对应的UIImage对象
  • 32.
    Core Data  读取与存储数据 数据对象 NSManagedObject … NSManagedObject 数据对象管理器:增, NSManagedObjectContex 删,改,查,撤销/重 NSManagedObjectContext … t 做 数据字典(一般加载合并 Bundle中的所有model) NSPersistentStoreCoordinator NSManagedObjectMode l 数据文 NSPersistentStore … NSPersistentStore 件
  • 33.
    Core Data  初始化工作 model= [NSManagedObjectModel mergedModelFromBundles:nil]; //nil 表示从mainbundle中加载合并所有model coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; [coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error]; context = [[NSManagedObjectContext alloc] init]; context.persistentStoreCoordinator = coordinator; 以上操作,iOS5.0下,等价于 UIManagedDocument *document = [[UIManagedDocument alloc] initWithFileURL:storeUrl];
  • 34.
    Core Data  增加记录 player = [NSEntityDescription insertNewObjectForEntityForName:@"Player” inManagedObjectContext:context]; team = [NSEntityDescription insertNewObjectForEntityForName:@"Team” inManagedObjectContext:context]; team.name = @"TheBest"; player.name = @"Tom"; player.score = [NSNumber numberWithInt:3000]; player.team = team; if(![context save:&error]){ NSLog(@"%@", error); }
  • 35.
    Core Data  删除记录 [context deleteObject:player];  查询记录 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Player"]; request.predicate = [NSPredicate predicateWithFormat:@"score > 2000"]; NSSet *players = [context executeFetchRequest:request error:&error];  修改记录 player.score = [NSNumber numberWithInt:3500];
  • 36.
    Core Data  更多内容 1. 版本升级 NSMappingModel 2. NSManagedObjectContext与多线程 3. NSManagedObject的生命周期 4. iOS5.0, UIManagedDocument, iCloud 5. NSFetchedResultsController与UITableView 6. 内存使用与性能优化 7. Cocoa Binding (Mac OS X)
  • 37.
    数据持久化  持久化方案的选择 数据类型  读写频率  数据大小  缓存机制  安全性  可扩展性  开发效率
  • 38.