Part 10:iOS的数据持久化(1),文件,归档

本章中主要学习数据持久化与各种持久化方式的实现方法

沙箱目录 

沙箱目录设计的原理就是只能允许自己的应用访问目录,而不允许其他的应用访问。

1. Documents 目录 大量的数据,经常变化,最重要的是这个目录中数据,iCLoud 和 iTunes 备份。

2. Library 目录

使用偏好(系统设置)、缓存数据,不进行 iCLoud 和 iTunes 备份。

3. tmp 目录

临时数据,不进行 iCLoud 和 iTunes 备份。经常会被清除的。

沙箱目录与资源目录的区别: 资源目录中的内容是只 读的,沙箱目录是放置那些可以读写的数据的。

持久化方式 

1.属性列表文件,集合对象可以读写到属性列表文件中。

2.对象归档,一个对象状态和被保持到归档文件中。

3.SQLite 数据库,SQLite 是个开源嵌入式关系型数据库。 

4.CoreData,本质上也是通过 SQLite 存储的,它是一种对象关系映射技术(ORM)。

以下为本章中实现的Demo代码

展示层:View/MarterViewController.swift、DetailViewController.swift、AddViewController.swift

MarterViewController.swift

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

import UIKit

//表视频主页面

class MarterViewController: UITableViewController {

    var objects = NSMutableArray()

     

    let Controller:NoteController = NoteController()

     

    override func viewDidLoad() {

        super.viewDidLoad()

        //代码实现左侧按钮

        self.navigationItem.leftBarButtonItem = self.editButtonItem

         

        self.objects = self.Controller.findAll()

         

        //(接收通知)监听通知事件RegisterCompletionNotification,交给registerCompletion函数处理

        NotificationCenter.default.addObserver(self, selector: #selector(CreateNoteList(notification:)), name: NSNotification.Name.init(rawValue: "CreateNoteList"), object: nil)// . object: nil 可以发送通知的视图接收过来

         

    }

    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

        // Dispose of any resources that can be recreated.

    }

     

    //TableViewDataSource 协议方法实现

    //返回表视图节的个数

    override func numberOfSections(in tableView: UITableView) -> Int {

        // #warning Incomplete implementation, return the number of sections

        return 1

    }

     

    //返回表视图每个节中的行数

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        // #warning Incomplete implementation, return the number of rows

        return self.objects.count

    }

     

    //返回每一个 自定义cell 的内容

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        //请求可重用单元格,需要一个标识,CustomCellTableViewCell为自定义单元格类

        let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier"for: indexPath)

         

        let object = self.objects[indexPath.row] as! Note//查询数组中的Note出来

        cell.textLabel!.text = object.Content as String//取Note的Content的属性

        return cell

    }

     

    //点击Cell会触发视图控制器的Segue方法

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

        //判断是跳转的哪个segue,showtow为segue在故事板中定义的identifier

        if segue.identifier == "toDetail" {

            //得到二级(Detail控制器)

            let datailVC = segue.destination as! DetailViewController

            //当前表视图被选择单元格

            let indexPath = self.tableView.indexPathForSelectedRow

            //当前单元格所对应的Note对象

            let object = self.objects[(indexPath?.row)!] as! Note

            //将信息传给二级的listData

            datailVC.listData = object.Content as String

            //设置二级表视图名称

            datailVC.title = "Detail"

        }

    }

     

    override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {

        // 如果不希望指定项可编辑,则返回false

        return true

    }

     

    //进行操作,当前主要处理delete操作

    override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {

        if editingStyle == .delete {

            let removeNote = self.objects[indexPath.row] as? Note

            self.objects = self.Controller.remove(model: removeNote!)

            tableView.deleteRows(at: [indexPath], with: .left)

        else if editingStyle == .insert {

            // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.

        }

    }

     

    //实现通知监听方法

    func CreateNoteList(notification : NSNotification) {

        let allData = notification.object as! NSMutableArray//取到投送过来对像

        self.objects = allData//拿到的数据给当前视图的变量

        self.tableView.reloadData()//重新加载当前表视图

         

    }

}

DetailViewController.swift

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

import UIKit

//点击表视频图后进入的详细页面

class DetailViewController: UIViewController {

     

    @IBOutlet weak var DetailLebel: UILabel!

     

    var listData:String!

    override func viewDidLoad() {

        super.viewDidLoad()

         

        self.DetailLebel.text = self.listData

         

    }

    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

        // Dispose of any resources that can be recreated.

    }   

}

AddViewController.swift

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

import UIKit

//添加备忘录

class AddViewController: UIViewController {

    @IBOutlet weak var textField: UITextField!

     

    let Controller:NoteController = NoteController()

     

     

    @IBAction func onDone(_ sender: UIBarButtonItem) {

        self.textField.resignFirstResponder()//username放弃第一响应者

        self.dismiss(animated: true, completion: nil)//关闭模态

    }

     

    @IBAction func onSave(_ sender: UIBarButtonItem) {

         

        //实例化一个新的Note

        let note = Note(date: NSDate(), content: self.textField.text! as NSString)//NSDate(),为当前日期

        //调用业务逻辑层的createNote方法,会返回添加后的所有数据

        let objs = Controller.createNote(model: note)

        //注册通知,传输数据 . object: objs 把返回的所有数据投送出去

        NotificationCenter.default.post(name: NSNotification.Name(rawValue: "CreateNoteList"), object: objs, userInfo: nil)

         

        self.dismiss(animated: true, completion: nil)//关闭模态

        self.textField.resignFirstResponder()//username放弃第一响应者

    }

     

    override func viewDidLoad() {

        super.viewDidLoad()

        //注册点击事件

        self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTap)))

    }

    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

        // Dispose of any resources that can be recreated.

    }

     

    //点击空白处关闭键盘方法

    func handleTap(sender: UITapGestureRecognizer) {

        if sender.state == .ended {

            self.textField.resignFirstResponder()//username放弃第一响应者

        }

        sender.cancelsTouchesInView = false

    }

}

业务逻辑层:Controller/NoteController.swift

NoteController.swift,等等四种持久化方式都用一个业务逻辑类

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

import Foundation

//业务逻辑层,Note为plist,NoteCoding为归档,,等等四种持久化方式都用一个业务逻辑类

class NoteController {

    

    //查询所用数据方法

    func findAll() -> NSMutableArray {

        let dao:NoteModelSQlite = NoteModelSQlite.sharedFoo

        return dao.findAll()

    }

     

    //插入Note方法

    func createNote(model: Note) -> NSMutableArray {

        let dao:NoteModelSQlite = NoteModelSQlite.sharedFoo

        dao.create(model: model) //会有警告,因为create方法有返回值但在这里并没有使用

        return dao.findAll()

    }

     

    //删除Note方法

    func remove(model: Note) -> NSMutableArray {

        let dao:NoteModelSQlite = NoteModelSQlite.sharedFoo

        dao.remove(model: model)

        return dao.findAll()

    }

}

数据持久层:Model/Note.swift、NoteModel.swift、NoteCoding.swift、NoteModelCoding.swift、NoteModelSQlite.swift、DemoApp-10-Bridging-Header.h(OC的头文件)

业务领域对像类:Note.swift,等等四种持久化方式都用一个业务领域对象类

1

2

3

4

5

6

7

8

9

10

11

12

13

14

import Foundation

//业务领域对象类

class Note {

     

    let Date: NSDate

    var Content: NSString

     

    init(date:NSDate,content:NSString) {

        self.Date date

        self.Content = content

    }

     

}

plist文件方式:NoteModel.swift

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

import Foundation

//plist文件方式

class NoteModel {

     

    let dateFormatter : DateFormatter = DateFormatter()

     

    private static let sharedInstance = NoteModel() //单例的实例保存这个属性中

    class var sharedFoo: NoteModel { //swift中的静态计算属性

         

        //初始化 拷贝文件到沙箱

        sharedInstance.plistCopyDocument()

         

        return sharedInstance

    }

     

     

    //修改Note方法

    public func modify(model: Note) -> Int {

         

        let path = self.getDocumentPath()

        let array = NSMutableArray(contentsOfFile: path)

         

        //业务逻辑层的数据的数组中套Note。因此我们需要转换。

        for item in array! {

            let dict = item as! NSDictionary

             

            let strDate = dict["date"as! String

            let date = dateFormatter.date(from: strDate)!

             

            //比较日期主键是否相等

            if date == model.Date as Date {

                //修改字典中的某个对应content key的值

                dict.setValue(model.Content, forKey: "content")

                //在次写入文件

                array?.write(toFile: path, atomically: true)

            }

        }

         

        return 0

    }

     

     

    //插入Note方法

    public func create(model: Note) {

         

        let path = self.getDocumentPath()

        let array = NSMutableArray(contentsOfFile: path)

         

        //将日期转为字符串

        let strDate = dateFormatter.string(from: model.Date as Date)

        //组成一个字典元素

        let dict = NSDictionary(objects: [model.Content,strDate], forKeys: ["content" as NSCopying,"date" as NSCopying])

        //把字典元素添加到数组中

        array?.add(dict)

        //插入到文件

        array?.write(toFile: path, atomically: true)

    }

     

    //删除Note方法

    public func remove(model: Note) {

         

        let path = self.getDocumentPath()

        let array = NSMutableArray(contentsOfFile: path)

         

        //业务逻辑层的数据的数组中套Note。因此我们需要转换。

        for item in array! {

            let dict = item as! NSDictionary

             

            let strDate = dict["date"as! String

            let date = dateFormatter.date(from: strDate)!

             

            //比较日期主键是否相等

            if date == model.Date as Date {

                array?.remove(dict)//删除数组元素中的对应字典

                //写入文件

                array?.write(toFile: path, atomically: true)

            }

        }

         

    }

     

    //根据主键查询一条数据

    func findByid(model:Note) -> Note? {

        //实例时间对象于格式化

         

        self.dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"

         

        let SXplist = self.getDocumentPath()

        let array = NSArray(contentsOfFile: SXplist)

         

        //业务逻辑层的数据的数组中套Note。因此我们需要转换。

        for item in array! {

            let dict = item as! NSDictionary//取到数组中的每一个字典

            let strDate = dict["date"as! String//将字典中key的date取出

            let date = dateFormatter.date(from: strDate)!

            if date == model.Date as Date {

                let content = dict["content"as! String//将字典中的content取出

                let note = Note(datedate as NSDate, content: content as NSString)

                return note

            }

        }

        return nil

    }

     

     

    //查询所有数据方法

    func findAll() -> NSMutableArray {

         

        //实例时间对象于格式化

        self.dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"

         

        //得到已拷贝到沙箱目录的plist文件

        let SXplist = self.getDocumentPath()

        //将文件读到变量中,array中是数组套字典的格式

        let array = NSArray(contentsOfFile: SXplist)

        //字义可变数组对象

        let listData  = NSMutableArray()

        //业务逻辑层的数据的数组中套Note。因此我们需要转换。

        for item in array! {

            let dict = item as! NSDictionary//取到数组中的每一个字典

            let strDate = dict["date"as! String//将字典中key的date取出

            let date = dateFormatter.date(from: strDate)!

            let content = dict["content"as! String//将字典中的content取出

            let note = Note(datedate as NSDate, content: content as NSString)//实例化note对象,返回一个note对象

            listData.add(note)//添加到可变数组中

        }

        return listData

    }

     

    //将plist文件拷贝到沙箱Document目录

    func plistCopyDocument() {

        //获得FileManager的单例

        let fileManager = FileManager.default

         

        //获得资源目录

        let defaultDBPath = Bundle.main.resourcePath as String!

        //在defaultDBPath字符串的基础上累加另一个字符串NotesList.plist,目录+文件路径

        let dbFile = defaultDBPath?.appending("/NotesList.plist")

         

        //目录+文件路径

        let writablepaht = self.getDocumentPath()

         

        //判断文件是否存在,如果存在则不重复拷贝

        let dbexits = fileManager.fileExists(atPath: writablepaht)

        if dbexits != true {

            do {//当代码提示后有throws时, 需要 try一下 有抛出异常的操作

                //拷贝资源目录的文件 to 沙箱目录中

                try fileManager.copyItem(atPath: dbFile!, toPath: writablepaht)

            }catch {

                //使用断言方法,如果bool为false,会打印message字符串

                //assert(bool, message)

                print("出现异常")

            }

        }

         

        //print(writablepaht)

    }

     

    //获取系统沙箱Document目录方法

    func getDocumentPath() -> String {

        let documentDirectory: NSArray = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) as NSArray

        let myDocPath = documentDirectory[0] as! NSString

        //在myDocPath字符串的基础上累加另一个字符串NotesList.plist,目录+文件路径

        let wtFile = myDocPath.appending("/NotesList.plist"as String

        return wtFile

    }

     

}

归档方式:NoteCoding.swift,NoteModelCoding.swift

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

import Foundation

//归档与反归档,被归档条件,1必须是NSObject对象,2必须实现NSCoding这个协议

class NoteCoding: NSObject, NSCoding {

     

    let Date: NSDate

    var Content: NSString

     

    init(date:NSDate,content:NSString) {

        self.Date date

        self.Content = content

    }

     

    //NSCoding要求实现的 “解码” 在反归档时候调用的,必须要求实现的,“应该” 内部调用

    public required init?(coder aDecoder: NSCoder) {

        self.Date = aDecoder.decodeObject(forKey: "Date"as! NSDate

        self.Content = aDecoder.decodeObject(forKey: "Content"as! NSString

    }

     

    //NSCoding要求实现的 “编码” 在归档时候调用的,必须要求实现的,“应该” 内部调用

    public func encode(with aCoder: NSCoder) {

        aCoder.encode(self.Date, forKey: "Date")

        aCoder.encode(self.Content, forKey: "Content")

    }

     

}

=============================

import Foundation

//归档与反归档

class NoteModelCoding: NSObject {

     

    //保存数据列表

    var listData: NSMutableArray!

    //归档与返归档用到的KEY

    let ARCH_KEY = "ackksy"

     

    let dateFormatter : DateFormatter = DateFormatter()

     

    private static let sharedInstance = NoteModelCoding() //单例的实例保存这个属性中

    class var sharedFoo: NoteModelCoding { //swift中的静态计算属性

         

        //初始化 创建归档文件

        sharedInstance.plistCopyDocument()

         

        return sharedInstance

    }

     

     

    //修改Note方法

    public func modify(model: NoteCoding) -> Int {

         

        let path = self.getDocumentPath()

        //获得所有数据

        let array = self.findAll()

         

        //业务逻辑层的数据的数组中套Note。因此我们需要转换。

        for item in array {

            let note = item as! NoteCoding

             

            //比较日期主键是否相等

            if note.Date == model.Date {

                note.Content = model.Content//修改数据,待测试...

                 

                //array进行归档

                let data = NSMutableData()

                let archiver = NSKeyedArchiver(forWritingWith: data)

                archiver.encode(array, forKey: ARCH_KEY)

                archiver.finishEncoding()

                data.write(toFile: path, atomically: true)

                break

            }

        }

         

        return 0

    }

     

     

    //插入Note方法

    public func create(model: NoteCoding) {

         

        let path = self.getDocumentPath()

        let array = self.findAll()

        array.add(model)

         

        //array进行归档

        let data = NSMutableData()

        let archiver = NSKeyedArchiver(forWritingWith: data)

        archiver.encode(array, forKey: ARCH_KEY)

        archiver.finishEncoding()

        data.write(toFile: path, atomically: true)

         

    }

     

    //删除Note方法

    public func remove(model: NoteCoding) {

         

        let path = self.getDocumentPath()

        //获得所有数据

        let array = self.findAll()

         

        //业务逻辑层的数据的数组中套Note。因此我们需要转换。

        for item in array {

            let note = item as! NoteCoding

             

            //比较日期主键是否相等

            if note.Date == model.Date {

                array.remove(note)//删除当前匹配出的Note

                 

                //array进行归档

                let data = NSMutableData()

                let archiver = NSKeyedArchiver(forWritingWith: data)

                archiver.encode(array, forKey: ARCH_KEY)

                archiver.finishEncoding()

                data.write(toFile: path, atomically: true)

                break

            }

        }

         

    }

     

    //根据主键查询一条数据

    func findByid(model:NoteCoding) -> NoteCoding? {

        //得到已拷贝到沙箱目录的plist文件

        let SXplist = self.getDocumentPath()

        //将归档文件读取到Data中

        let data = NSData(contentsOfFile: SXplist)!

         

        if data.length > 0 {//判断文件是否读取成功

            //开始反归档

            //定义反归档对象,与data关联

            let unarchiver = NSKeyedUnarchiver(forReadingWith: data as Data)

            //解码ARCH_KEY的归档文件 并 转为可变数组赋值给listData

            self.listData = unarchiver.decodeObject(forKey: ARCH_KEY) as! NSMutableArray

            //结束反归档

            unarchiver.finishDecoding()

             

            //业务逻辑层的数据的数组中套Note。因此我们需要转换。

            for item in self.listData {

                let note = item as! NoteCoding

                //比较日期主键是否相等

                if note.Date == model.Date {

                    return note

                }

            }

        }

        return nil

    }

     

     

    //查询所有数据方法

    func findAll() -> NSMutableArray {

         

        //得到已拷贝到沙箱目录的plist文件

        let SXplist = self.getDocumentPath()

        //将归档文件读取到Data中

        let data = NSData(contentsOfFile: SXplist)!

         

        if data.length > 0 {//判断文件是否读取成功

            //开始反归档

            //定义反归档对象,与data关联

            let unarchiver = NSKeyedUnarchiver(forReadingWith: data as Data)

            //解码ARCH_KEY的归档文件 并 转为可变数组赋值给listData

            self.listData = unarchiver.decodeObject(forKey: ARCH_KEY) as! NSMutableArray

            //结束反归档

            unarchiver.finishDecoding()

        }

        return self.listData

    }

     

    //将plist文件拷贝到沙箱Document目录

    func plistCopyDocument() {

        //获得FileManager的单例

        let fileManager = FileManager.default

        //目录+文件路径

        let writablepaht = self.getDocumentPath()

        //判断文件是否存在,如果存在则不重复拷贝

        let dbexits = fileManager.fileExists(atPath: writablepaht)

         

        if dbexits != true {

            //添加一些测试数据

            self.dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"

            let date1: NSDate = self.dateFormatter.date(from: "2015-01-01 16:01:03")! as NSDate

            let note1: NoteCoding = NoteCoding(date:date1, content: "Welcome to MyNote.")

             

            let date2: NSDate = self.dateFormatter.date(from: "2015-01-02 8:01:03")! as NSDate

            let note2: NoteCoding = NoteCoding(date:date2, content: "欢迎使用MyNote。")

             

            //定义一个listData:NSMutableArray常量后,需要在实例化NSMutableArray()

            self.listData = NSMutableArray()

            self.listData.add(note1)

            self.listData.add(note2)

             

            //进行归档

            let data = NSMutableData()//归档需要可变的Data实例

            let archiver = NSKeyedArchiver(forWritingWith: data)//定义归档对象,与data关联

            archiver.encode(self.listData, forKey: self.ARCH_KEY)//编码listdata数组到ARCH_KEY中

            archiver.finishEncoding()//完成编码

            data.write(toFile: writablepaht, atomically: true)//写入归档文件

        }

         

        //print(writablepaht)

    }

     

    //获取系统沙箱Document目录方法

    func getDocumentPath() -> String {

        let documentDirectory: NSArray = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) as NSArray

        let myDocPath = documentDirectory[0] as! NSString

        //在myDocPath字符串的基础上累加另一个字符串NotesList.plist,目录+文件路径

        let wtFile = myDocPath.appending("/NotesList.fo"as String

        return wtFile

    }

     

}


此处接 Part 10:iOS的数据持久化(2),Sqlite,CoreData

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程工人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值