欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

iOS開(kāi)發(fā)系列--詳細(xì)介紹數(shù)據(jù)存取

 更新時(shí)間:2016年11月16日 11:18:28   作者:KenshinCui  
本篇文章主要介紹了iOS開(kāi)發(fā)系列--詳細(xì)介紹數(shù)據(jù)存取,詳細(xì)介紹了IOS數(shù)據(jù)的存儲(chǔ)問(wèn)題,具有一定的參考價(jià)值,有興趣的同學(xué)可以了解一下。

概覽

在iOS開(kāi)發(fā)中數(shù)據(jù)存儲(chǔ)的方式可以歸納為兩類:一類是存儲(chǔ)為文件,另一類是存儲(chǔ)到數(shù)據(jù)庫(kù)。例如前面IOS開(kāi)發(fā)系列—Objective-C之Foundation框架的文章中提到歸檔、plist文件存儲(chǔ),包括偏好設(shè)置其本質(zhì)都是存儲(chǔ)為文件,只是說(shuō)歸檔或者plist文件存儲(chǔ)可以選擇保存到沙盒中,而偏好設(shè)置系統(tǒng)已經(jīng)規(guī)定只能保存到沙盒的Library/Preferences目錄。當(dāng)然,文件存儲(chǔ)并不作為本文的重點(diǎn)內(nèi)容。本文重點(diǎn)還是說(shuō)數(shù)據(jù)庫(kù)存儲(chǔ),做過(guò)數(shù)據(jù)庫(kù)開(kāi)發(fā)的朋友應(yīng)該知道,可以通過(guò)SQL直接訪問(wèn)數(shù)據(jù)庫(kù),也可以通過(guò)ORM進(jìn)行對(duì)象關(guān)系映射訪問(wèn)數(shù)據(jù)庫(kù)。這兩種方式恰恰對(duì)應(yīng)iOS中SQLite和Core Data的內(nèi)容,在此將重點(diǎn)進(jìn)行分析:

  • SQLite
  • Core Data
  • FMDB

SQLite

SQLite是目前主流的嵌入式關(guān)系型數(shù)據(jù)庫(kù),其最主要的特點(diǎn)就是輕量級(jí)、跨平臺(tái),當(dāng)前很多嵌入式操作系統(tǒng)都將其作為數(shù)據(jù)庫(kù)首選。雖然SQLite是一款輕型數(shù)據(jù)庫(kù),但是其功能也絕不亞于很多大型關(guān)系數(shù)據(jù)庫(kù)。學(xué)習(xí)數(shù)據(jù)庫(kù)就要學(xué)習(xí)其相關(guān)的定義、操作、查詢語(yǔ)言,也就是大家日常說(shuō)得SQL語(yǔ)句。和其他數(shù)據(jù)庫(kù)相比,SQLite中的SQL語(yǔ)法并沒(méi)有太大的差別,因此這里對(duì)于SQL語(yǔ)句的內(nèi)容不會(huì)過(guò)多贅述,大家可以參考SQLite中其他SQL相關(guān)的內(nèi)容,這里還是重點(diǎn)講解iOS中如何使用SQLite構(gòu)建應(yīng)用程序。先看一下SQLite數(shù)據(jù)庫(kù)的幾個(gè)特點(diǎn):

  • 基于C語(yǔ)言開(kāi)發(fā)的輕型數(shù)據(jù)庫(kù)
  • 在iOS中需要使用C語(yǔ)言語(yǔ)法進(jìn)行數(shù)據(jù)庫(kù)操作、訪問(wèn)(無(wú)法使用ObjC直接訪問(wèn),因?yàn)閘ibsqlite3框架基于C語(yǔ)言編寫(xiě))
  • SQLite中采用的是動(dòng)態(tài)數(shù)據(jù)類型,即使創(chuàng)建時(shí)定義了一種類型,在實(shí)際操作時(shí)也可以存儲(chǔ)其他類型,但是推薦建庫(kù)時(shí)使用合適的類型(特別是應(yīng)用需要考慮跨平臺(tái)的情況時(shí))
  • 建立連接后通常不需要關(guān)閉連接(盡管可以手動(dòng)關(guān)閉)

要使用SQLite很簡(jiǎn)單,如果在Mac OSX上使用可以考慮到SQLite網(wǎng)站下載命令行工具,也可以使用類似于SQLiteManager、Navicat for SQLite等工具。為了方便大家開(kāi)發(fā)調(diào)試,建議在開(kāi)發(fā)環(huán)境中安裝上述工具。

在iOS中操作SQLite數(shù)據(jù)庫(kù)可以分為以下幾步(注意先在項(xiàng)目中導(dǎo)入libsqlite3框架):

  1. 打開(kāi)數(shù)據(jù)庫(kù),利用sqlite3_open()打開(kāi)數(shù)據(jù)庫(kù)會(huì)指定一個(gè)數(shù)據(jù)庫(kù)文件保存路徑,如果文件存在則直接打開(kāi),否則創(chuàng)建并打開(kāi)。打開(kāi)數(shù)據(jù)庫(kù)會(huì)得到一個(gè)sqlite3類型的對(duì)象,后面需要借助這個(gè)對(duì)象進(jìn)行其他操作。
  2. 執(zhí)行SQL語(yǔ)句,執(zhí)行SQL語(yǔ)句又包括有返回值的語(yǔ)句和無(wú)返回值語(yǔ)句。
  3. 對(duì)于無(wú)返回值的語(yǔ)句(如增加、刪除、修改等)直接通過(guò)sqlite3_exec()函數(shù)執(zhí)行;
  4. 對(duì)于有返回值的語(yǔ)句則首先通過(guò)sqlite3_prepare_v2()進(jìn)行sql語(yǔ)句評(píng)估(語(yǔ)法檢測(cè)),然后通過(guò)sqlite3_step()依次取出查詢結(jié)果的每一行數(shù)據(jù),對(duì)于每行數(shù)據(jù)都可以通過(guò)對(duì)應(yīng)的sqlite3_column_類型()方法獲得對(duì)應(yīng)列的數(shù)據(jù),如此反復(fù)循環(huán)直到遍歷完成。當(dāng)然,最后需要釋放句柄。

在整個(gè)操作過(guò)程中無(wú)需管理數(shù)據(jù)庫(kù)連接,對(duì)于嵌入式SQLite操作是持久連接(盡管可以通過(guò)sqlite3_close()關(guān)閉),不需要開(kāi)發(fā)人員自己釋放連接。縱觀整個(gè)操作過(guò)程,其實(shí)與其他平臺(tái)的開(kāi)發(fā)沒(méi)有明顯的區(qū)別,較為麻煩的就是數(shù)據(jù)讀取,在iOS平臺(tái)中使用C進(jìn)行數(shù)據(jù)讀取采用了游標(biāo)的形式,每次只能讀取一行數(shù)據(jù),較為麻煩。因此實(shí)際開(kāi)發(fā)中不妨對(duì)這些操作進(jìn)行封裝:

KCDbManager.h

//
// DbManager.h
// DataAccess
//
// Created by Kenshin Cui on 14-3-29.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <sqlite3.h>
#import "KCSingleton.h"

@interface KCDbManager : NSObject

singleton_interface(KCDbManager);

#pragma mark - 屬性
#pragma mark 數(shù)據(jù)庫(kù)引用,使用它進(jìn)行數(shù)據(jù)庫(kù)操作
@property (nonatomic) sqlite3 *database;


#pragma mark - 共有方法
/**
 * 打開(kāi)數(shù)據(jù)庫(kù)
 *
 * @param dbname 數(shù)據(jù)庫(kù)名稱
 */
-(void)openDb:(NSString *)dbname;

/**
 * 執(zhí)行無(wú)返回值的sql
 *
 * @param sql sql語(yǔ)句
 */
-(void)executeNonQuery:(NSString *)sql;

/**
 * 執(zhí)行有返回值的sql
 *
 * @param sql sql語(yǔ)句
 *
 * @return 查詢結(jié)果
 */
-(NSArray *)executeQuery:(NSString *)sql;
@end

KCDbManager.m

//
// DbManager.m
// DataAccess
//
// Created by Kenshin Cui on 14-3-29.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import "KCDbManager.h"
#import <sqlite3.h>
#import "KCSingleton.h"
#import "KCAppConfig.h"

#ifndef kDatabaseName

#define kDatabaseName @"myDatabase.db"

#endif

@interface KCDbManager()
@end

@implementation KCDbManager

singleton_implementation(KCDbManager)

#pragma mark 重寫(xiě)初始化方法
-(instancetype)init{
  KCDbManager *manager;
  if((manager=[super init]))
  {
    [manager openDb:kDatabaseName];
  }
  return manager;
}

-(void)openDb:(NSString *)dbname{
  //取得數(shù)據(jù)庫(kù)保存路徑,通常保存沙盒Documents目錄
  NSString *directory=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
  NSLog(@"%@",directory);
  NSString *filePath=[directory stringByAppendingPathComponent:dbname];
  //如果有數(shù)據(jù)庫(kù)則直接打開(kāi),否則創(chuàng)建并打開(kāi)(注意filePath是ObjC中的字符串,需要轉(zhuǎn)化為C語(yǔ)言字符串類型)
  if (SQLITE_OK ==sqlite3_open(filePath.UTF8String, &_database)) {
    NSLog(@"數(shù)據(jù)庫(kù)打開(kāi)成功!");
  }else{
    NSLog(@"數(shù)據(jù)庫(kù)打開(kāi)失敗!");
  }
}

-(void)executeNonQuery:(NSString *)sql{
  char *error;
  //單步執(zhí)行sql語(yǔ)句,用于插入、修改、刪除
  if (SQLITE_OK!=sqlite3_exec(_database, sql.UTF8String, NULL, NULL,&error)) {
    NSLog(@"執(zhí)行SQL語(yǔ)句過(guò)程中發(fā)生錯(cuò)誤!錯(cuò)誤信息:%s",error);
  }
}

-(NSArray *)executeQuery:(NSString *)sql{
  NSMutableArray *rows=[NSMutableArray array];//數(shù)據(jù)行
  
  //評(píng)估語(yǔ)法正確性
  sqlite3_stmt *stmt;
  //檢查語(yǔ)法正確性
  if (SQLITE_OK==sqlite3_prepare_v2(_database, sql.UTF8String, -1, &stmt, NULL)) {
    //單步執(zhí)行sql語(yǔ)句
    while (SQLITE_ROW==sqlite3_step(stmt)) {
      int columnCount= sqlite3_column_count(stmt);
      NSMutableDictionary *dic=[NSMutableDictionary dictionary];
      for (int i=0; i<columnCount; i++) {
        const char *name= sqlite3_column_name(stmt, i);//取得列名
        const unsigned char *value= sqlite3_column_text(stmt, i);//取得某列的值
        dic[[NSString stringWithUTF8String:name]]=[NSString stringWithUTF8String:(const char *)value];
      }
      [rows addObject:dic];
    }
  }
  
  //釋放句柄
  sqlite3_finalize(stmt);
  
  return rows;
}
@end

在上面的類中對(duì)于數(shù)據(jù)庫(kù)操作進(jìn)行了封裝,封裝之后數(shù)據(jù)操作更加方便,同時(shí)所有的語(yǔ)法都由C轉(zhuǎn)換成了ObjC。

下面仍然以微博查看為例進(jìn)行SQLite演示。當(dāng)然實(shí)際開(kāi)發(fā)中微博數(shù)據(jù)是從網(wǎng)絡(luò)讀取的,但是考慮到緩存問(wèn)題,通常會(huì)選擇將微博數(shù)據(jù)保存到本地,下面的Demo演示了將數(shù)據(jù)存放到本地?cái)?shù)據(jù)庫(kù)以及數(shù)據(jù)讀取的過(guò)程。當(dāng)然,實(shí)際開(kāi)發(fā)中并不會(huì)在視圖控制器中直接調(diào)用數(shù)據(jù)庫(kù)操作方法,在這里通常會(huì)引入兩個(gè)概念Model和Service。Model自不必多說(shuō),就是MVC中的模型。而Service指的是操作數(shù)據(jù)庫(kù)的服務(wù)層,它封裝了對(duì)于Model的基本操作方法,實(shí)現(xiàn)具體的業(yè)務(wù)邏輯。為了解耦,在控制器中是不會(huì)直接接觸數(shù)據(jù)庫(kù)的,控制器中只和模型(模型是領(lǐng)域的抽象)、服務(wù)對(duì)象有關(guān)系,借助服務(wù)層對(duì)模型進(jìn)行各類操作,模型的操作反應(yīng)到數(shù)據(jù)庫(kù)中就是對(duì)表中數(shù)據(jù)的操作。具體關(guān)系如下:
要完成上述功能,首先定義一個(gè)應(yīng)用程序全局對(duì)象進(jìn)行數(shù)據(jù)庫(kù)、表的創(chuàng)建。為了避免每次都創(chuàng)建數(shù)據(jù)庫(kù)和表出錯(cuò),這里利用了偏好設(shè)置進(jìn)行保存當(dāng)前創(chuàng)建狀態(tài)(其實(shí)這也是數(shù)據(jù)存儲(chǔ)的一部分),如果創(chuàng)建過(guò)了數(shù)據(jù)庫(kù)則不再創(chuàng)建,否則創(chuàng)建數(shù)據(jù)庫(kù)和表。

KCDatabaseCreator.m

//
// KCDatabaseCreator.m
// DataAccess
//
// Created by Kenshin Cui on 14-3-29.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import "KCDatabaseCreator.h"
#import "KCDbManager.h"

@implementation KCDatabaseCreator

+(void)initDatabase{
  NSString *key=@"IsCreatedDb";
  NSUserDefaults *defaults=[[NSUserDefaults alloc]init];
  if ([[defaults valueForKey:key] intValue]!=1) {
    [self createUserTable];
    [self createStatusTable];
    [defaults setValue:@1 forKey:key];
  }
}

+(void)createUserTable{
  NSString *sql=@"CREATE TABLE User (Id integer PRIMARY KEY AUTOINCREMENT,name text,screenName text, profileImageUrl text,mbtype text,city text)";
  [[KCDbManager sharedKCDbManager] executeNonQuery:sql];
}

+(void)createStatusTable{
  NSString *sql=@"CREATE TABLE Status (Id integer PRIMARY KEY AUTOINCREMENT,source text,createdAt date,\"text\" text,user integer REFERENCES User (Id))";
  [[KCDbManager sharedKCDbManager] executeNonQuery:sql];
}
@end

其次,定義數(shù)據(jù)模型,這里定義用戶User和微博Status兩個(gè)數(shù)據(jù)模型類。注意模型應(yīng)該盡量保持其單純性,僅僅是簡(jiǎn)單的POCO,不要引入視圖、控制器等相關(guān)內(nèi)容。

KCUser.h

//
// KCUser.h
// UrlConnection
//
// Created by Kenshin Cui on 14-3-22.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface KCUser : NSObject

#pragma mark 編號(hào)
@property (nonatomic,strong) NSNumber *Id;

#pragma mark 用戶名
@property (nonatomic,copy) NSString *name;

#pragma mark 用戶昵稱
@property (nonatomic,copy) NSString *screenName;

#pragma mark 頭像
@property (nonatomic,copy) NSString *profileImageUrl;

#pragma mark 會(huì)員類型
@property (nonatomic,copy) NSString *mbtype;

#pragma mark 城市
@property (nonatomic,copy) NSString *city;

#pragma mark - 動(dòng)態(tài)方法

/**
 * 初始化用戶
 *
 * @param name 用戶名
 * @param city 所在城市
 *
 * @return 用戶對(duì)象
 */
-(KCUser *)initWithName:(NSString *)name screenName:(NSString *)screenName profileImageUrl:(NSString *)profileImageUrl mbtype:(NSString *)mbtype city:(NSString *)city;

/**
 * 使用字典初始化用戶對(duì)象
 *
 * @param dic 用戶數(shù)據(jù)
 *
 * @return 用戶對(duì)象
 */
-(KCUser *)initWithDictionary:(NSDictionary *)dic;

#pragma mark - 靜態(tài)方法
+(KCUser *)userWithName:(NSString *)name screenName:(NSString *)screenName profileImageUrl:(NSString *)profileImageUrl mbtype:(NSString *)mbtype city:(NSString *)city;
@end
KCUser.m

//
// KCUser.m
// UrlConnection
//
// Created by Kenshin Cui on 14-3-22.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import "KCUser.h"

@implementation KCUser

-(KCUser *)initWithName:(NSString *)name screenName:(NSString *)screenName profileImageUrl:(NSString *)profileImageUrl mbtype:(NSString *)mbtype city:(NSString *)city{
  if (self=[super init]) {
    self.name=name;
    self.screenName=screenName;
    self.profileImageUrl=profileImageUrl;
    self.mbtype=mbtype;
    self.city=city;
  }
  return self;
}


-(KCUser *)initWithDictionary:(NSDictionary *)dic{
  if (self=[super init]) {
    [self setValuesForKeysWithDictionary:dic];
  }
  return self;
}

+(KCUser *)userWithName:(NSString *)name screenName:(NSString *)screenName profileImageUrl:(NSString *)profileImageUrl mbtype:(NSString *)mbtype city:(NSString *)city{
  KCUser *user=[[KCUser alloc]initWithName:name screenName:screenName profileImageUrl:profileImageUrl mbtype:mbtype city:city];
  return user;
}

@end

KCStatus.h

//
// KCStatus.h
// UITableView
//
// Created by Kenshin Cui on 14-3-1.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "KCUser.h"

@interface KCStatus : NSObject

#pragma mark - 屬性
@property (nonatomic,strong) NSNumber *Id;//微博id
@property (nonatomic,strong) KCUser *user;//發(fā)送用戶
@property (nonatomic,copy) NSString *createdAt;//創(chuàng)建時(shí)間
@property (nonatomic,copy) NSString *source;//設(shè)備來(lái)源
@property (nonatomic,copy) NSString *text;//微博內(nèi)容

#pragma mark - 動(dòng)態(tài)方法

/**
 * 初始化微博數(shù)據(jù)
 *
 * @param createAt    創(chuàng)建日期
 * @param source     來(lái)源
 * @param text      微博內(nèi)容
 * @param user      發(fā)送用戶
 *
 * @return 微博對(duì)象
 */
-(KCStatus *)initWithCreateAt:(NSString *)createAt source:(NSString *)source text:(NSString *)text user:(KCUser *)user;

/**
 * 初始化微博數(shù)據(jù)
 *
 * @param profileImageUrl 用戶頭像
 * @param mbtype     會(huì)員類型
 * @param createAt    創(chuàng)建日期
 * @param source     來(lái)源
 * @param text      微博內(nèi)容
 * @param userId     用戶編號(hào)
 *
 * @return 微博對(duì)象
 */
-(KCStatus *)initWithCreateAt:(NSString *)createAt source:(NSString *)source text:(NSString *)text userId:(int)userId;
/**
 * 使用字典初始化微博對(duì)象
 *
 * @param dic 字典數(shù)據(jù)
 *
 * @return 微博對(duì)象
 */
-(KCStatus *)initWithDictionary:(NSDictionary *)dic;

#pragma mark - 靜態(tài)方法
/**
 * 初始化微博數(shù)據(jù)
 *
 * @param createAt    創(chuàng)建日期
 * @param source     來(lái)源
 * @param text      微博內(nèi)容
 * @param user      發(fā)送用戶
 *
 * @return 微博對(duì)象
 */
+(KCStatus *)statusWithCreateAt:(NSString *)createAt source:(NSString *)source text:(NSString *)text user:(KCUser *)user;
/**
 * 初始化微博數(shù)據(jù)
 *
 * @param profileImageUrl 用戶頭像
 * @param mbtype     會(huì)員類型
 * @param createAt    創(chuàng)建日期
 * @param source     來(lái)源
 * @param text      微博內(nèi)容
 * @param userId     用戶編號(hào)
 *
 * @return 微博對(duì)象
 */
+(KCStatus *)statusWithCreateAt:(NSString *)createAt source:(NSString *)source text:(NSString *)text userId:(int)userId;

@end

KCStatus.m

//
// KCStatus.m
// UITableView
//
// Created by Kenshin Cui on 14-3-1.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import "KCStatus.h"

@implementation KCStatus

-(KCStatus *)initWithDictionary:(NSDictionary *)dic{
  if (self=[super init]) {
    [self setValuesForKeysWithDictionary:dic];
    self.user=[[KCUser alloc]init];
    self.user.Id=dic[@"user"];
  }
  return self;
}

-(KCStatus *)initWithCreateAt:(NSString *)createAt source:(NSString *)source text:(NSString *)text user:(KCUser *)user{
  if (self=[super init]) {
    self.createdAt=createAt;
    self.source=source;
    self.text=text;
    self.user=user;
  }
  return self;
}

-(KCStatus *)initWithCreateAt:(NSString *)createAt source:(NSString *)source text:(NSString *)text userId:(int)userId{
  if (self=[super init]) {
    self.createdAt=createAt;
    self.source=source;
    self.text=text;
    KCUser *user=[[KCUser alloc]init];
    user.Id=[NSNumber numberWithInt:userId];
    self.user=user;
  }
  return self;
}

-(NSString *)source{
  return [NSString stringWithFormat:@"來(lái)自 %@",_source];
}

+(KCStatus *)statusWithCreateAt:(NSString *)createAt source:(NSString *)source text:(NSString *)text user:(KCUser *)user{
  KCStatus *status=[[KCStatus alloc]initWithCreateAt:createAt source:source text:text user:user];
  return status;
}

+(KCStatus *)statusWithCreateAt:(NSString *)createAt source:(NSString *)source text:(NSString *)text userId:(int)userId{
  KCStatus *status=[[KCStatus alloc]initWithCreateAt:createAt source:source text:text userId:userId];
  return status;
}
@end

然后,編寫(xiě)服務(wù)類,進(jìn)行數(shù)據(jù)的增、刪、改、查操作,由于服務(wù)類方法同樣不需要過(guò)多的配置,因此定義為單例,保證程序中只有一個(gè)實(shí)例即可。服務(wù)類中調(diào)用前面封裝的數(shù)據(jù)庫(kù)方法將對(duì)數(shù)據(jù)庫(kù)的操作轉(zhuǎn)換為對(duì)模型的操作。

KCUserService.h

//
// KCUserService.h
// DataAccess
//
// Created by Kenshin Cui on 14-3-29.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "KCUser.h"
#import "KCSingleton.h"

@interface KCUserService : NSObject
singleton_interface(KCUserService)

/**
 * 添加用戶信息
 *
 * @param user 用戶對(duì)象
 */
-(void)addUser:(KCUser *)user;

/**
 * 刪除用戶
 *
 * @param user 用戶對(duì)象
 */
-(void)removeUser:(KCUser *)user;

/**
 * 根據(jù)用戶名刪除用戶
 *
 * @param name 用戶名
 */
-(void)removeUserByName:(NSString *)name;

/**
 * 修改用戶內(nèi)容
 *
 * @param user 用戶對(duì)象
 */
-(void)modifyUser:(KCUser *)user;

/**
 * 根據(jù)用戶編號(hào)取得用戶
 *
 * @param Id 用戶編號(hào)
 *
 * @return 用戶對(duì)象
 */
-(KCUser *)getUserById:(int)Id;

/**
 * 根據(jù)用戶名取得用戶
 *
 * @param name 用戶名
 *
 * @return 用戶對(duì)象
 */
-(KCUser *)getUserByName:(NSString *)name;

@end

KCUserService.m

//
// KCUserService.m
// DataAccess
//
// Created by Kenshin Cui on 14-3-29.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import "KCUserService.h"
#import "KCUser.h"
#import "KCDbManager.h"

@implementation KCUserService
singleton_implementation(KCUserService)

-(void)addUser:(KCUser *)user{
  NSString *sql=[NSString stringWithFormat:@"INSERT INTO User (name,screenName, profileImageUrl,mbtype,city) VALUES('%@','%@','%@','%@','%@')",user.name,user.screenName, user.profileImageUrl,user.mbtype,user.city];
  [[KCDbManager sharedKCDbManager] executeNonQuery:sql];
}

-(void)removeUser:(KCUser *)user{
  NSString *sql=[NSString stringWithFormat:@"DELETE FROM User WHERE Id='%@'",user.Id];
  [[KCDbManager sharedKCDbManager] executeNonQuery:sql];
}

-(void)removeUserByName:(NSString *)name{
  NSString *sql=[NSString stringWithFormat:@"DELETE FROM User WHERE name='%@'",name];
  [[KCDbManager sharedKCDbManager] executeNonQuery:sql];
}

-(void)modifyUser:(KCUser *)user{
  NSString *sql=[NSString stringWithFormat:@"UPDATE User SET name='%@',screenName='%@',profileImageUrl='%@',mbtype='%@',city='%@' WHERE Id='%@'",user.name,user.screenName,user.profileImageUrl,user.mbtype,user.city,user.Id];
  [[KCDbManager sharedKCDbManager] executeNonQuery:sql];
}

-(KCUser *)getUserById:(int)Id{
  KCUser *user=[[KCUser alloc]init];
  NSString *sql=[NSString stringWithFormat:@"SELECT name,screenName,profileImageUrl,mbtype,city FROM User WHERE Id='%i'", Id];
  NSArray *rows= [[KCDbManager sharedKCDbManager] executeQuery:sql];
  if (rows&&rows.count>0) {
    [user setValuesForKeysWithDictionary:rows[0]];
  }
  return user;
}

-(KCUser *)getUserByName:(NSString *)name{
  KCUser *user=[[KCUser alloc]init];
  NSString *sql=[NSString stringWithFormat:@"SELECT Id, name,screenName,profileImageUrl,mbtype,city FROM User WHERE name='%@'", name];
  NSArray *rows= [[KCDbManager sharedKCDbManager] executeQuery:sql];
  if (rows&&rows.count>0) {
    [user setValuesForKeysWithDictionary:rows[0]];
  }
  return user;
}
@end

KCStatusService.h

//
// KCStatusService.h
// DataAccess
//
// Created by Kenshin Cui on 14-3-29.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "KCSingleton.h"
@class KCStatus;

@interface KCStatusService : NSObject
singleton_interface(KCStatusService)

/**
 * 添加微博信息
 *
 * @param status 微博對(duì)象
 */
-(void)addStatus:(KCStatus *)status;

/**
 * 刪除微博
 *
 * @param status 微博對(duì)象
 */
-(void)removeStatus:(KCStatus *)status;

/**
 * 修改微博內(nèi)容
 *
 * @param status 微博對(duì)象
 */
-(void)modifyStatus:(KCStatus *)status;

/**
 * 根據(jù)編號(hào)取得微博
 *
 * @param Id 微博編號(hào)
 *
 * @return 微博對(duì)象
 */
-(KCStatus *)getStatusById:(int)Id;

/**
 * 取得所有微博對(duì)象
 *
 * @return 所有微博對(duì)象
 */
-(NSArray *)getAllStatus;
@end

KCStatusService.m

//
// KCStatusService.m
// DataAccess
//
// Created by Kenshin Cui on 14-3-29.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import "KCStatusService.h"
#import "KCDbManager.h"
#import "KCStatus.h"
#import "KCUserService.h"
#import "KCSingleton.h"

@interface KCStatusService(){
  
}

@end

@implementation KCStatusService
singleton_implementation(KCStatusService)


-(void)addStatus:(KCStatus *)status{
  NSString *sql=[NSString stringWithFormat:@"INSERT INTO Status (source,createdAt,\"text\" ,user) VALUES('%@','%@','%@','%@')",status.source,status.createdAt,status.text,status.user.Id];
  [[KCDbManager sharedKCDbManager] executeNonQuery:sql];
}

-(void)removeStatus:(KCStatus *)status{
  NSString *sql=[NSString stringWithFormat:@"DELETE FROM Status WHERE Id='%@'",status.Id];
  [[KCDbManager sharedKCDbManager] executeNonQuery:sql];
}

-(void)modifyStatus:(KCStatus *)status{
  NSString *sql=[NSString stringWithFormat:@"UPDATE Status SET source='%@',createdAt='%@',\"text\"='%@' ,user='%@' WHERE Id='%@'",status.source,status.createdAt,status.text,status.user, status.Id];
  [[KCDbManager sharedKCDbManager] executeNonQuery:sql];
}

-(KCStatus *)getStatusById:(int)Id{
  KCStatus *status=[[KCStatus alloc]init];
  NSString *sql=[NSString stringWithFormat:@"SELECT Id, source,createdAt,\"text\" ,user FROM Status WHERE Id='%i'", Id];
  NSArray *rows= [[KCDbManager sharedKCDbManager] executeQuery:sql];
  if (rows&&rows.count>0) {
    [status setValuesForKeysWithDictionary:rows[0]];
    status.user=[[KCUserService sharedKCUserService] getUserById:[(NSNumber *)rows[0][@"user"] intValue]] ;
  }
  return status;
}

-(NSArray *)getAllStatus{
  NSMutableArray *array=[NSMutableArray array];
  NSString *sql=@"SELECT Id, source,createdAt,\"text\" ,user FROM Status ORDER BY Id";
  NSArray *rows= [[KCDbManager sharedKCDbManager] executeQuery:sql];
  for (NSDictionary *dic in rows) {
    KCStatus *status=[self getStatusById:[(NSNumber *)dic[@"Id"] intValue]];
    [array addObject:status];
  }
  return array;
}
@end

最后,在視圖控制器中調(diào)用相應(yīng)的服務(wù)層進(jìn)行各類數(shù)據(jù)操作,在下面的代碼中分別演示了增、刪、改、查四類操作。

KCMainViewController.m

//
// KCMainTableViewController.m
// DataAccess
//
// Created by Kenshin Cui on 14-3-29.
// Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//

#import "KCMainTableViewController.h"
#import "KCDbManager.h"
#import "KCDatabaseCreator.h"
#import "KCUser.h"
#import "KCStatus.h"
#import "KCUserService.h"
#import "KCStatusService.h"
#import "KCStatusTableViewCell.h"

@interface KCMainTableViewController (){
  NSArray *_status;
  NSMutableArray *_statusCells;
}

@end

@implementation KCMainTableViewController

- (void)viewDidLoad {
  [super viewDidLoad];
  [KCDatabaseCreator initDatabase];
  
//  [self addUsers];
//  [self removeUser];
//  [self modifyUserInfo];
  
//  [self addStatus];
  
  [self loadStatusData];
  
}

-(void)addUsers{
  KCUser *user1=[KCUser userWithName:@"Binger" screenName:@"冰兒" profileImageUrl:@"binger.jpg" mbtype:@"mbtype.png" city:@"北京"];
  [[KCUserService sharedKCUserService] addUser:user1];
  KCUser *user2=[KCUser userWithName:@"Xiaona" screenName:@"小娜" profileImageUrl:@"xiaona.jpg" mbtype:@"mbtype.png" city:@"北京"];
  [[KCUserService sharedKCUserService] addUser:user2];
  KCUser *user3=[KCUser userWithName:@"Lily" screenName:@"麗麗" profileImageUrl:@"lily.jpg" mbtype:@"mbtype.png" city:@"北京"];
  [[KCUserService sharedKCUserService] addUser:user3];
  KCUser *user4=[KCUser userWithName:@"Qianmo" screenName:@"阡陌" profileImageUrl:@"qianmo.jpg" mbtype:@"mbtype.png" city:@"北京"];
  [[KCUserService sharedKCUserService] addUser:user4];
  KCUser *user5=[KCUser userWithName:@"Yanyue" screenName:@"炎月" profileImageUrl:@"yanyue.jpg" mbtype:@"mbtype.png" city:@"北京"];
  [[KCUserService sharedKCUserService] addUser:user5];
}

-(void)addStatus{
  KCStatus *status1=[KCStatus statusWithCreateAt:@"9:00" source:@"iPhone 6" text:@"一只雪猴在日本邊泡溫泉邊玩iPhone的照片,獲得了\"2014年野生動(dòng)物攝影師\"大賽特等獎(jiǎng)。一起來(lái)為猴子配個(gè)詞" userId:1];
  [[KCStatusService sharedKCStatusService] addStatus:status1];
  KCStatus *status2=[KCStatus statusWithCreateAt:@"9:00" source:@"iPhone 6" text:@"一只雪猴在日本邊泡溫泉邊玩iPhone的照片,獲得了\"2014年野生動(dòng)物攝影師\"大賽特等獎(jiǎng)。一起來(lái)為猴子配個(gè)詞" userId:1];
  [[KCStatusService sharedKCStatusService] addStatus:status2];
  KCStatus *status3=[KCStatus statusWithCreateAt:@"9:30" source:@"iPhone 6" text:@"【我們送iPhone6了 要求很簡(jiǎn)單】真心回饋粉絲,小編覺(jué)得現(xiàn)在最好的獎(jiǎng)品就是iPhone6了。今起到12月31日,關(guān)注我們,轉(zhuǎn)發(fā)微博,就有機(jī)會(huì)獲iPhone6(獎(jiǎng)品可能需要等待)!每月抽一臺(tái)[鼓掌]。不費(fèi)事,還是試試吧,萬(wàn)一中了呢" userId:2];
  [[KCStatusService sharedKCStatusService] addStatus:status3];
  KCStatus *status4=[KCStatus statusWithCreateAt:@"9:45" source:@"iPhone 6" text:@"重大新聞:蒂姆庫(kù)克宣布出柜后,ISIS戰(zhàn)士怒扔iPhone,沙特神職人員呼吁人們換回iPhone 4。[via Pan-Arabia Enquirer]" userId:3];
  [[KCStatusService sharedKCStatusService] addStatus:status4];
  KCStatus *status5=[KCStatus statusWithCreateAt:@"10:05" source:@"iPhone 6" text:@"小伙伴們,有誰(shuí)知道怎么往Iphone4S里倒東西?倒入的東西又該在哪里找?用了Iphone這么長(zhǎng)時(shí)間,還真的不知道怎么弄!有誰(shuí)知道???謝謝!" userId:4];
  [[KCStatusService sharedKCStatusService] addStatus:status5];
  KCStatus *status6=[KCStatus statusWithCreateAt:@"10:07" source:@"iPhone 6" text:@"在音悅臺(tái)iPhone客戶端里發(fā)現(xiàn)一個(gè)悅單《Infinite 金明洙》,推薦給大家! " userId:1];
  [[KCStatusService sharedKCStatusService] addStatus:status6];
  KCStatus *status7=[KCStatus statusWithCreateAt:@"11:20" source:@"iPhone 6" text:@"如果sony吧mp3播放器產(chǎn)品發(fā)展下去,不貪圖手頭節(jié)目源的現(xiàn)實(shí)利益,就木有蘋(píng)果的ipod,也就木有iphone。柯達(dá)類似的現(xiàn)實(shí)利益,不自我革命的案例也是一種巨頭的宿命。" userId:2];
  [[KCStatusService sharedKCStatusService] addStatus:status7];
  KCStatus *status8=[KCStatus statusWithCreateAt:@"13:00" source:@"iPhone 6" text:@"【iPhone 7 Plus】新買的iPhone 7 Plus ,如何?夠酷炫么?" userId:2];
  [[KCStatusService sharedKCStatusService] addStatus:status8];
  KCStatus *status9=[KCStatus statusWithCreateAt:@"13:24" source:@"iPhone 6" text:@"自拍神器#卡西歐TR500#,tr350S~價(jià)格美麗,行貨,全國(guó)聯(lián)?!玦Phone6 iPhone6Plus卡西歐TR150 TR200 TR350 TR350S全面到貨 招收各種代理![給力]微信:39017366" userId:3];
  [[KCStatusService sharedKCStatusService] addStatus:status9];
  KCStatus *status10=[KCStatus statusWithCreateAt:@"13:26" source:@"iPhone 6" text:@"猜到猴哥玩手機(jī)時(shí)所思所想者,再獎(jiǎng)iPhone一部。(獎(jiǎng)品由“2014年野生動(dòng)物攝影師”評(píng)委會(huì)頒發(fā))" userId:3];
  [[KCStatusService sharedKCStatusService] addStatus:status10];
}

-(void)removeUser{
  //注意在SQLite中區(qū)分大小寫(xiě)
  [[KCUserService sharedKCUserService] removeUserByName:@"Yanyue"];
}

-(void)modifyUserInfo{
  KCUser *user1= [[KCUserService sharedKCUserService]getUserByName:@"Xiaona"];
  user1.city=@"上海";
  [[KCUserService sharedKCUserService] modifyUser:user1];
  
  KCUser *user2= [[KCUserService sharedKCUserService]getUserByName:@"Lily"];
  user2.city=@"深圳";
  [[KCUserService sharedKCUserService] modifyUser:user2];
}

#pragma mark 加載數(shù)據(jù)
-(void)loadStatusData{
  _statusCells=[[NSMutableArray alloc]init];
  _status=[[KCStatusService sharedKCStatusService]getAllStatus];
  [_status enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    KCStatusTableViewCell *cell=[[KCStatusTableViewCell alloc]init];
    cell.status=(KCStatus *)obj;
    [_statusCells addObject:cell];
  }];
  NSLog(@"%@",[_status lastObject]);
}


#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
  return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  return _status.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  static NSString *identtityKey=@"myTableViewCellIdentityKey1";
  KCStatusTableViewCell *cell=[self.tableView dequeueReusableCellWithIdentifier:identtityKey];
  if(cell==nil){
    cell=[[KCStatusTableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:identtityKey];
  }
  cell.status=_status[indexPath.row];

  return cell;
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
  return ((KCStatusTableViewCell *)_statusCells[indexPath.row]).height;
}

-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
  return 20.0f;
}
@end

項(xiàng)目目錄結(jié)構(gòu):

運(yùn)行效果:

Core Data

基本概念

當(dāng)前,各類應(yīng)用開(kāi)發(fā)中只要牽扯到數(shù)據(jù)庫(kù)操作通常都會(huì)用到一個(gè)概念“對(duì)象關(guān)系映射(ORM)”。例如在Java平臺(tái)使用Hibernate,在.NET平臺(tái)使用Entity Framework、Linq、NHibernate等。在iOS中也不例外,iOS中ORM框架首選Core Data,這是官方推薦的,不需要借助第三方框架。無(wú)論是哪種平臺(tái)、哪種技術(shù),ORM框架的作用都是相同的,那就是將關(guān)系數(shù)據(jù)庫(kù)中的表(準(zhǔn)確的說(shuō)是實(shí)體)轉(zhuǎn)換為程序中的對(duì)象,其本質(zhì)還是對(duì)數(shù)據(jù)庫(kù)的操作(例如Core Data中如果存儲(chǔ)類型配置為SQLite則本質(zhì)還是操作的SQLite數(shù)據(jù)庫(kù))。細(xì)心的朋友應(yīng)該已經(jīng)注意到,在上面的SQLite中其實(shí)我們?cè)贙CMainViewController中進(jìn)行的數(shù)據(jù)庫(kù)操作已經(jīng)轉(zhuǎn)換為了對(duì)象操作,服務(wù)層中的方法中已經(jīng)將對(duì)數(shù)據(jù)庫(kù)的操作封裝起來(lái),轉(zhuǎn)換為了對(duì)Model的操作,這種方式已經(jīng)是面向?qū)ο蟮?。上述通過(guò)將對(duì)象映射到實(shí)體的過(guò)程完全是手動(dòng)完成的,相對(duì)來(lái)說(shuō)操作比較復(fù)雜,就拿對(duì)KCStatus對(duì)象的操作來(lái)說(shuō):首先要手動(dòng)創(chuàng)建數(shù)據(jù)庫(kù)(Status表),其次手動(dòng)創(chuàng)建模型KCStatus,接著創(chuàng)建服務(wù)層KCStatusService。Core Data正是為了解決這個(gè)問(wèn)題而產(chǎn)生的,它將數(shù)據(jù)庫(kù)的創(chuàng)建、表的創(chuàng)建、對(duì)象和表的轉(zhuǎn)換等操作封裝起來(lái),簡(jiǎn)化了我們的操作(注意Core Data只是將對(duì)象關(guān)系的映射簡(jiǎn)化了,并不是把服務(wù)層替代了,這一點(diǎn)大家需要明白)。

使用Core Data進(jìn)行數(shù)據(jù)庫(kù)存取并不需要手動(dòng)創(chuàng)建數(shù)據(jù)庫(kù),這個(gè)過(guò)程完全由Core Data框架完成,開(kāi)發(fā)人員面對(duì)的是模型,主要的工作就是把模型創(chuàng)建起來(lái),具體數(shù)據(jù)庫(kù)如何創(chuàng)建則不用管。在iOS項(xiàng)目中添加“Data Model”文件。然后在其中創(chuàng)建實(shí)體和關(guān)系:

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

  • 實(shí)體對(duì)象不需要?jiǎng)?chuàng)建ID主鍵,Attributes中應(yīng)該是有意義屬性(創(chuàng)建過(guò)程中應(yīng)該考慮對(duì)象的屬性而不是數(shù)據(jù)庫(kù)中表有幾個(gè)字段,盡管多數(shù)屬性會(huì)對(duì)應(yīng)表的字段)。
  • 所有的屬性應(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)系(例如一個(gè)用戶有多條微博,而一條微博則只屬于一個(gè)用戶,用戶和微博形成一對(duì)多的關(guān)系)。

以上模型創(chuàng)建后,接下來(lái)就是根據(jù)上面的模型文件(.xcdatamodeld文件)生成具體的實(shí)體類。在Xcode中添加“NSManagedObject Subclass”文件,按照步驟選擇創(chuàng)建的模型及實(shí)體,Xcode就會(huì)根據(jù)所創(chuàng)建模型生成具體的實(shí)體類。

User.h

//
// User.h
// CoreData
//
// Created by Kenshin Cui on 14/03/27.
// Copyright (c) 2014年 cmjstudio. All rights reserved.
//

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

@class Status;

@interface User : NSManagedObject

@property (nonatomic, retain) NSString * city;
@property (nonatomic, retain) NSString * mbtype;
@property (nonatomic, retain) NSString * name;
@property (nonatomic, retain) NSString * profileImageUrl;
@property (nonatomic, retain) NSString * screenName;
@property (nonatomic, retain) NSSet *statuses;
@end

@interface User (CoreDataGeneratedAccessors)

- (void)addStatusesObject:(Status *)value;
- (void)removeStatusesObject:(Status *)value;
- (void)addStatuses:(NSSet *)values;
- (void)removeStatuses:(NSSet *)values;

@end

User.m

//
// User.m
// CoreData
//
// Created by Kenshin Cui on 14/03/27.
// Copyright (c) 2014年 cmjstudio. All rights reserved.
//

#import "User.h"
#import "Status.h"


@implementation User

@dynamic city;
@dynamic mbtype;
@dynamic name;
@dynamic profileImageUrl;
@dynamic screenName;
@dynamic statuses;

@end

Status.h

//
// Status.h
// CoreData
//
// Created by Kenshin Cui on 14/03/27.
// Copyright (c) 2014年 cmjstudio. All rights reserved.
//

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


@interface Status : NSManagedObject

@property (nonatomic, retain) NSDate * createdAt;
@property (nonatomic, retain) NSString * source;
@property (nonatomic, retain) NSString * text;
@property (nonatomic, retain) NSManagedObject *user;

@end

Status.m

//
// Status.m
// CoreData
//
// Created by Kenshin Cui on 14/03/27.
// Copyright (c) 2014年 cmjstudio. All rights reserved.
//

#import "Status.h"


@implementation Status

@dynamic createdAt;
@dynamic source;
@dynamic text;
@dynamic user;

@end

很顯然,通過(guò)模型生成類的過(guò)程相當(dāng)簡(jiǎn)單,通常這些類也不需要手動(dòng)維護(hù),如果模型發(fā)生的變化只要重新生成即可。有幾點(diǎn)需要注意:

  • 所有的實(shí)體類型都繼承于NSManagedObject,每個(gè)NSManagedObject對(duì)象對(duì)應(yīng)著數(shù)據(jù)庫(kù)中一條記錄。
  • 集合屬性(例如User中的status)生成了訪問(wèn)此屬性的分類方法。
  • 使用@dynamic代表具體屬性實(shí)現(xiàn),具體實(shí)現(xiàn)細(xì)節(jié)不需要開(kāi)發(fā)人員關(guān)心。

當(dāng)然,了解了這些還不足以完成數(shù)據(jù)的操作。究竟Core Data具體的設(shè)計(jì)如何,要完成數(shù)據(jù)的存取我們還需要了解一下Core Data幾個(gè)核心的類。

  • Persistent Object Store:可以理解為存儲(chǔ)持久對(duì)象的數(shù)據(jù)庫(kù)(例如SQLite,注意Core Data也支持其他類型的數(shù)據(jù)存儲(chǔ),例如xml、二進(jìn)制數(shù)據(jù)等)。
  • Managed Object Model:對(duì)象模型,對(duì)應(yīng)Xcode中創(chuàng)建的模型文件。
  • Persistent Store Coordinator:對(duì)象模型和實(shí)體類之間的轉(zhuǎn)換協(xié)調(diào)器,用于管理不同存儲(chǔ)對(duì)象的上下文。
  • Managed Object Context:對(duì)象管理上下文,負(fù)責(zé)實(shí)體對(duì)象和數(shù)據(jù)庫(kù)之間的交互。

Core Data使用

Core Data使用起來(lái)相對(duì)直接使用SQLite3的API而言更加的面向?qū)ο?,操作過(guò)程通常分為以下幾個(gè)步驟:

1.創(chuàng)建管理上下文

創(chuàng)建管理上下可以細(xì)分為:加載模型文件->指定數(shù)據(jù)存儲(chǔ)路徑->創(chuàng)建對(duì)應(yīng)數(shù)據(jù)類型的存儲(chǔ)->創(chuàng)建管理對(duì)象上下方并指定存儲(chǔ)。

經(jīng)過(guò)這幾個(gè)步驟之后可以得到管理對(duì)象上下文NSManagedObjectContext,以后所有的數(shù)據(jù)操作都由此對(duì)象負(fù)責(zé)。同時(shí)如果是第一次創(chuàng)建上下文,Core Data會(huì)自動(dòng)創(chuàng)建存儲(chǔ)文件(例如這里使用SQLite3存儲(chǔ)),并且根據(jù)模型對(duì)象創(chuàng)建對(duì)應(yīng)的表結(jié)構(gòu)。下圖為第一次運(yùn)行生成的數(shù)據(jù)庫(kù)及相關(guān)映射文件:
為了方便后面使用,NSManagedObjectContext對(duì)象可以作為單例或靜態(tài)屬性來(lái)保存,下面是創(chuàng)建的管理對(duì)象上下文的主要代碼:

-(NSManagedObjectContext *)createDbContext{
  NSManagedObjectContext *context;
  //打開(kāi)模型文件,參數(shù)為nil則打開(kāi)包中所有模型文件并合并成一個(gè)
  NSManagedObjectModel *model=[NSManagedObjectModel mergedModelFromBundles:nil];
  //創(chuàng)建解析器
  NSPersistentStoreCoordinator *storeCoordinator=[[NSPersistentStoreCoordinator alloc]initWithManagedObjectModel:model];
  //創(chuàng)建數(shù)據(jù)庫(kù)保存路徑
  NSString *dir=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
  NSLog(@"%@",dir);
  NSString *path=[dir stringByAppendingPathComponent:@"myDatabase.db"];
  NSURL *url=[NSURL fileURLWithPath:path];
  //添加SQLite持久存儲(chǔ)到解析器
  NSError *error;
  [storeCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:nil error:&error];
  if(error){
    NSLog(@"數(shù)據(jù)庫(kù)打開(kāi)失敗!錯(cuò)誤:%@",error.localizedDescription);
  }else{
    context=[[NSManagedObjectContext alloc]init];
    context.persistentStoreCoordinator=storeCoordinator;
    NSLog(@"數(shù)據(jù)庫(kù)打開(kāi)成功!");
  }
  return context;
}

2.查詢數(shù)據(jù)

對(duì)于有條件的查詢,在Core Data中是通過(guò)謂詞來(lái)實(shí)現(xiàn)的。首先創(chuàng)建一個(gè)請(qǐng)求,然后設(shè)置請(qǐng)求條件,最后調(diào)用上下文執(zhí)行請(qǐng)求的方法。

-(void)addUserWithName:(NSString *)name screenName:(NSString *)screenName profileImageUrl:(NSString *)profileImageUrl mbtype:(NSString *)mbtype city:(NSString *)city{
  //添加一個(gè)對(duì)象
  User *us= [NSEntityDescription insertNewObjectForEntityForName:@"User" inManagedObjectContext:self.context];
  us.name=name;
  us.screenName=screenName;
  us.profileImageUrl=profileImageUrl;
  us.mbtype=mbtype;
  us.city=city;
  NSError *error;
  //保存上下文
  if (![self.context save:&error]) {
    NSLog(@"添加過(guò)程中發(fā)生錯(cuò)誤,錯(cuò)誤信息:%@!",error.localizedDescription);
  }
}

如果有多個(gè)條件,只要使用謂詞組合即可,那么對(duì)于關(guān)聯(lián)對(duì)象條件怎么查詢呢?這里分為兩種情況進(jìn)行介紹:

a.查找一個(gè)對(duì)象只有唯一一個(gè)關(guān)聯(lián)對(duì)象的情況,例如查找用戶名為“Binger”的微博(一個(gè)微博只能屬于一個(gè)用戶),通過(guò)keypath查詢

-(NSArray *)getStatusesByUserName:(NSString *)name{
  NSFetchRequest *request=[NSFetchRequest fetchRequestWithEntityName:@"Status"];
  request.predicate=[NSPredicate predicateWithFormat:@"user.name=%@",name];
  NSArray *array=[self.context executeFetchRequest:request error:nil];
  return array;
}

此時(shí)如果跟蹤C(jī)ore Data生成的SQL語(yǔ)句會(huì)發(fā)現(xiàn)其實(shí)就是把Status表和User表進(jìn)行了關(guān)聯(lián)查詢(JOIN連接)。

b.查找一個(gè)對(duì)象有多個(gè)關(guān)聯(lián)對(duì)象的情況,例如查找發(fā)送微博內(nèi)容中包含“Watch”并且用戶昵稱為“小娜”的用戶(一個(gè)用戶有多條微博),此時(shí)可以充分利用謂詞進(jìn)行過(guò)濾。

 

-(NSArray *)getStatusesByUserName:(NSString *)name{
  NSFetchRequest *request=[NSFetchRequest fetchRequestWithEntityName:@"Status"];
  request.predicate=[NSPredicate predicateWithFormat:@"user.name=%@",name];
  NSArray *array=[self.context executeFetchRequest:request error:nil];
  return array;
}

注意如果單純查找微博中包含“Watch”的用戶,直接查出對(duì)應(yīng)的微博,然后通過(guò)每個(gè)微博的user屬性即可獲得用戶,此時(shí)就不用使用額外的謂詞過(guò)濾條件。

3.插入數(shù)據(jù)

插入數(shù)據(jù)需要調(diào)用實(shí)體描述對(duì)象NSEntityDescription返回一個(gè)實(shí)體對(duì)象,然后設(shè)置對(duì)象屬性,最后保存當(dāng)前上下文即可。這里需要注意,增、刪、改操作完最后必須調(diào)用管理對(duì)象上下文的保存方法,否則操作不會(huì)執(zhí)行。

-(void)addUserWithName:(NSString *)name screenName:(NSString *)screenName profileImageUrl:(NSString *)profileImageUrl mbtype:(NSString *)mbtype city:(NSString *)city{
  //添加一個(gè)對(duì)象
  User *us= [NSEntityDescription insertNewObjectForEntityForName:@"User" inManagedObjectContext:self.context];
  us.name=name;
  us.screenName=screenName;
  us.profileImageUrl=profileImageUrl;
  us.mbtype=mbtype;
  us.city=city;
  NSError *error;
  //保存上下文
  if (![self.context save:&error]) {
    NSLog(@"添加過(guò)程中發(fā)生錯(cuò)誤,錯(cuò)誤信息:%@!",error.localizedDescription);
  }
}

4.刪除數(shù)據(jù)

刪除數(shù)據(jù)可以直接調(diào)用管理對(duì)象上下文的deleteObject方法,刪除完保存上下文即可。注意,刪除數(shù)據(jù)前必須先查詢到對(duì)應(yīng)對(duì)象。

-(void)removeUser:(User *)user{
  [self.context deleteObject:user];
  NSError *error;
  if (![self.context save:&error]) {
    NSLog(@"刪除過(guò)程中發(fā)生錯(cuò)誤,錯(cuò)誤信息:%@!",error.localizedDescription);
  }
}

5.修改數(shù)據(jù)

修改數(shù)據(jù)首先也是取出對(duì)應(yīng)的實(shí)體對(duì)象,然后通過(guò)修改對(duì)象的屬性,最后保存上下文。

-(void)modifyUserWithName:(NSString *)name screenName:(NSString *)screenName profileImageUrl:(NSString *)profileImageUrl mbtype:(NSString *)mbtype city:(NSString *)city{
  User *us=[self getUserByName:name];
  us.screenName=screenName;
  us.profileImageUrl=profileImageUrl;
  us.mbtype=mbtype;
  us.city=city;
  NSError *error;
  if (![self.context save:&error]) {
    NSLog(@"修改過(guò)程中發(fā)生錯(cuò)誤,錯(cuò)誤信息:%@",error.localizedDescription);
  }

}

調(diào)試

雖然Core Data(如果使用SQLite數(shù)據(jù)庫(kù))操作最終轉(zhuǎn)換為SQL操作,但是調(diào)試起來(lái)卻不像操作SQL那么方便。特別是對(duì)于初學(xué)者而言經(jīng)常出現(xiàn)查詢報(bào)錯(cuò)的問(wèn)題,如果能看到最終生成的SQL語(yǔ)句自然對(duì)于調(diào)試很有幫助。事實(shí)上在Xcode中是支持Core Data調(diào)試的,具體操作:Product-Scheme-Edit Scheme-Run-Arguments中依次添加兩個(gè)參數(shù)(注意參數(shù)順序不能錯(cuò)):-com.apple.CoreData.SQLDebug、1。然后在運(yùn)行程序過(guò)程中如果操作了數(shù)據(jù)庫(kù)就會(huì)將SQL語(yǔ)句打印在輸出面板。
注意:如果模型發(fā)生了變化,此時(shí)可以重新生成實(shí)體類文件,但是所生成的數(shù)據(jù)庫(kù)并不會(huì)自動(dòng)更新,這時(shí)需要考慮重新生成數(shù)據(jù)庫(kù)并遷移原有的數(shù)據(jù)。

FMDB

基本使用

相比于SQLite3來(lái)說(shuō)Core Data存在著諸多優(yōu)勢(shì),它面向?qū)ο?,開(kāi)發(fā)人員不必過(guò)多的關(guān)心更多數(shù)據(jù)庫(kù)操作知識(shí),同時(shí)它基于ObjC操作,書(shū)寫(xiě)更加優(yōu)雅等。但是它本身也存在著一定的限制,例如如果考慮到跨平臺(tái),則只能選擇SQLite,因?yàn)闊o(wú)論是iOS還是Android都可以使用同一個(gè)數(shù)據(jù)庫(kù),降低了開(kāi)發(fā)成本和維護(hù)成本。其次是當(dāng)前多數(shù)ORM框架都存在的性能問(wèn)題,因?yàn)镺RM最終轉(zhuǎn)化為SQL操作,其中牽扯到模型數(shù)據(jù)轉(zhuǎn)化,其性能自然比不上直接使用SQL操作數(shù)據(jù)庫(kù)。那么有沒(méi)有更好的選擇呢?答案就是對(duì)SQLite進(jìn)行封裝。

其實(shí)通過(guò)前面對(duì)于SQLite的分析,大家應(yīng)該已經(jīng)看到KCDbManager就是對(duì)于SQLite封裝的結(jié)果,開(kāi)發(fā)人員面對(duì)的只有SQL和ObjC方法,不用過(guò)多l(xiāng)ibsqlite3的C語(yǔ)言API。但它畢竟只是一個(gè)簡(jiǎn)單的封裝,還有更多的細(xì)節(jié)沒(méi)有考慮,例如如何處理并發(fā)安全性,如何更好的處理事務(wù)等。因此,這里推薦使用第三方框架FMDB,整個(gè)框架非常輕量級(jí)但又不失靈活性,也是很多企業(yè)開(kāi)發(fā)的首選。

1.FMDB既然是對(duì)于libsqlite3框架的封裝,自然使用起來(lái)也是類似的,使用前也要打開(kāi)一個(gè)數(shù)據(jù)庫(kù),這個(gè)數(shù)據(jù)庫(kù)文件存在則直接打開(kāi)否則會(huì)創(chuàng)建并打開(kāi)。這里FMDB引入了一個(gè)FMDatabase對(duì)象來(lái)表示數(shù)據(jù)庫(kù),打開(kāi)數(shù)據(jù)庫(kù)和后面的數(shù)據(jù)庫(kù)操作全部依賴此對(duì)象。下面是打開(kāi)數(shù)據(jù)庫(kù)獲得FMDatabase對(duì)象的代碼:

-(void)openDb:(NSString *)dbname{
  //取得數(shù)據(jù)庫(kù)保存路徑,通常保存沙盒Documents目錄
  NSString *directory=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
  NSLog(@"%@",directory);
  NSString *filePath=[directory stringByAppendingPathComponent:dbname];
  //創(chuàng)建FMDatabase對(duì)象
  self.database=[FMDatabase databaseWithPath:filePath];
  //打開(kāi)數(shù)據(jù)上
  if ([self.database open]) {
    NSLog(@"數(shù)據(jù)庫(kù)打開(kāi)成功!");
  }else{
    NSLog(@"數(shù)據(jù)庫(kù)打開(kāi)失敗!");
  }
}

注意:dataWithPath中的路徑參數(shù)一般會(huì)選擇保存到沙箱中的Documents目錄中;如果這個(gè)參數(shù)設(shè)置為nil則數(shù)據(jù)庫(kù)會(huì)在內(nèi)存中創(chuàng)建;如果設(shè)置為@””則會(huì)在沙箱中的臨時(shí)目錄創(chuàng)建,應(yīng)用程序關(guān)閉則文件刪除。

2.對(duì)于數(shù)據(jù)庫(kù)的操作跟前面KCDbManager的封裝是類似的,在FMDB中FMDatabase類提供了兩個(gè)方法executeUpdate:和executeQuery:分別用于執(zhí)行無(wú)返回結(jié)果的查詢和有返回結(jié)果的查詢。當(dāng)然這兩個(gè)方法有很多的重載這里就不詳細(xì)解釋了。唯一需要指出的是,如果調(diào)用有格式化參數(shù)的sql語(yǔ)句時(shí),格式化符號(hào)使用“?”而不是“%@”、等。下面是兩種情況的代碼片段:

a.無(wú)返回結(jié)果

-(void)executeNonQuery:(NSString *)sql{
  //執(zhí)行更新sql語(yǔ)句,用于插入、修改、刪除
  if (![self.database executeUpdate:sql]) {
    NSLog(@"執(zhí)行SQL語(yǔ)句過(guò)程中發(fā)生錯(cuò)誤!");
  }
}

b.有返回結(jié)果

-(NSArray *)executeQuery:(NSString *)sql{
  NSMutableArray *array=[NSMutableArray array];
  //執(zhí)行查詢sql語(yǔ)句
  FMResultSet *result= [self.database executeQuery:sql];
  while (result.next) {
    NSMutableDictionary *dic=[NSMutableDictionary dictionary];
    for (int i=0; i<result.columnCount; ++i) {
      dic[[result columnNameForIndex:i]]=[result stringForColumnIndex:i];
    }
    [array addObject:dic];
  }
  return array;
}

對(duì)于有返回結(jié)果的查詢而言,查詢完返回一個(gè)游標(biāo)FMResultSet,通過(guò)遍歷游標(biāo)進(jìn)行查詢。而且FMDB中提供了大量intForColumn、stringForColumn等方法進(jìn)行取值。

并發(fā)和事務(wù)

我們知道直接使用libsqlite3進(jìn)行數(shù)據(jù)庫(kù)操作其實(shí)是線程不安全的,如果遇到多個(gè)線程同時(shí)操作一個(gè)表的時(shí)候可能會(huì)發(fā)生意想不到的結(jié)果。為了解決這個(gè)問(wèn)題建議在多線程中使用FMDatabaseQueue對(duì)象,相比FMDatabase而言,它是線程安全的。

創(chuàng)建FMDatabaseQueue的方法是類似的,調(diào)用databaseQueueWithPath:方法即可。注意這里不需要調(diào)用打開(kāi)操作。

-(void)openDb:(NSString *)dbname{
  //取得數(shù)據(jù)庫(kù)保存路徑,通常保存沙盒Documents目錄
  NSString *directory=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
  NSLog(@"%@",directory);
  NSString *filePath=[directory stringByAppendingPathComponent:dbname];
  //創(chuàng)建FMDatabaseQueue對(duì)象
  self.database=[FMDatabaseQueue databaseQueueWithPath:filePath];
}

然后所有的增刪改查操作調(diào)用FMDatabaseQueue的inDatabase:方法在block中執(zhí)行操作sql語(yǔ)句即可。

-(void)executeNonQuery:(NSString *)sql{
  //執(zhí)行更新sql語(yǔ)句,用于插入、修改、刪除
  [self.database inDatabase:^(FMDatabase *db) {
    [db executeUpdate:sql];
  }];
}
-(NSArray *)executeQuery:(NSString *)sql{
  NSMutableArray *array=[NSMutableArray array];
  [self.database inDatabase:^(FMDatabase *db) {
    //執(zhí)行查詢sql語(yǔ)句
    FMResultSet *result= [db executeQuery:sql];
    while (result.next) {
      NSMutableDictionary *dic=[NSMutableDictionary dictionary];
      for (int i=0; i<result.columnCount; ++i) {
        dic[[result columnNameForIndex:i]]=[result stringForColumnIndex:i];
      }
      [array addObject:dic];
    }
  }];
  return array;
}

之所以將事務(wù)放到FMDB中去說(shuō)并不是因?yàn)橹挥蠪MDB才支持事務(wù),而是因?yàn)镕MDB將其封裝成了幾個(gè)方法來(lái)調(diào)用,不用自己寫(xiě)對(duì)應(yīng)的sql而已。其實(shí)在在使用libsqlite3操作數(shù)據(jù)庫(kù)時(shí)也是原生支持事務(wù)的(因?yàn)檫@里的事務(wù)是基于數(shù)據(jù)庫(kù)的,F(xiàn)MDB還是使用的SQLite數(shù)據(jù)庫(kù)),只要在執(zhí)行sql語(yǔ)句前加上“begin transaction;”執(zhí)行完之后執(zhí)行“commit transaction;”或者“rollback transaction;”進(jìn)行提交或回滾即可。另外在Core Data中大家也可以發(fā)現(xiàn),所有的增、刪、改操作之后必須調(diào)用上下文的保存方法,其實(shí)本身就提供了事務(wù)的支持,只要不調(diào)用保存方法,之前所有的操作是不會(huì)提交的。在FMDB中FMDatabase有beginTransaction、commit、rollback三個(gè)方法進(jìn)行開(kāi)啟事務(wù)、提交事務(wù)和回滾事務(wù)。

原文鏈接:http://www.cnblogs.com/kenshincui/p/4077833.html

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論