美文网首页swift 文章收集小斑IOS三人行
Swift3.0 SQLite使用、性能优化、线程安全

Swift3.0 SQLite使用、性能优化、线程安全

作者: Codepgq | 来源:发表于2016-12-12 19:47 被阅读1693次

    效果图附上:
    <br />
    插入数据


    insert.gif

    <br />
    删除数据


    delete.gif

    <br />
    更新数据


    update.gif

    <br />
    查找数据


    select.gif

    <br />


    排序数据

    <br />
    分页检索数据


    limit.gif

    <br />
    Other数据- 这里时间限制,不演示全部


    other.gif
    • 前言

    上一篇简单的介绍了SQLite的基本使用之后,现在我们开始在代码中去完成对数据库的操作。
    内容点:
    PS:使用C函数完成,不使用第三方框架

    • 集成SQLite
    • 使用SQLite完成增删改查
    • 子线程操作数据库
    • 数据库的优化

    <br />

    • 1. 集成

    • 1.1先创建一个Swift 3.0工程,名字随意,创建完成后点击项目工程名

    添加类库

    <br />

    • 1.2创建一个桥接文件

    command + n


    桥接文件1
    桥接文件2
    桥接文件3

    <br />

    • 1.3导入头文件

    输入 #import <sqlite3.h>

    导入头文件
    <br />
    • 1.4 然后在ViewController中输入sqlite3,有提示表示集成成功.

    如图,类似就行

    <br />

    • 在开始之前先说明一下:由于代码的注释都写的很详细了,所以不会逐行解释。

    <br />

    • 2. 使用SQLite完成增删改查

    要想完成对数据的增删改查,得有一个文件吧,和上一篇类似,我们应该先创建数据库文件,然后创建表,才能进行增删改查,所以我们第一步是创建一个数据库文件、然后是创建表。然后我们创建一个单例类来专门管理数据库。

    • 2.1 创建单例类

    使用 command + n 创建一个类SQLManager,实现单例

    /// 单例
        static let manager : SQLManager = SQLManager()
        class func shareInstance() -> SQLManager{
            return manager
        }
    

    <br />

    • 2.2写一个方法创建数据库文件

    创建数据库文件,首先需要一个文件名,然后通过文件名得到地址,再通过地址去创建数据库文件
     /// 打开数据库
        func openDB(DBName: String){
            
            //1、拿到数据库路径
            let path = DBName.documentDir()
            //打印路径,以便拿到数据文件
            print(path)
            
            //2、转化为c字符串
            let cPath = path.cString(using: String.Encoding.utf8)
            /*
             参数一:c字符串,文件路径
             参数二:OpaquePointer 一个数据库对象的地址
             
             注意Open方法的特性:如果指定的文件路径已有对应的数据库文件会直接打开,如果没有则会创建在打开
             使用Sqlite_OK判断
             sqlite3_open(cPath, &dbBase)
             */
            /*
             #define SQLITE_OK           0   /* Successful result */
             */
            if sqlite3_open(cPath, &dbBase) != SQLITE_OK{
                print("数据库打开失败")
                return
            }
        }
    

    <br />

    • 2.3如果你运行没有打印错误提示,那么就是创建成功了,这个时候我们就创建一个表,表的设计如下:

    表设计如图
    有了 表的设计,我们就可以编写SQL语句了,写一个方法去完成表的创建,在创建表之前有一点我觉得有必要提一下:
    在SQLite中除查询,其他的都是使用exec去执行,所以先把exec封装一下
     func execSQL(sql : String) -> Bool {
            // 1、先把OC字符串转化为C字符串
            let cSQL = sql.cString(using: String.Encoding.utf8)
            
            // 2、执行语句
            /// 在SQLite3中,除了查询以外(创建/删除/更新)都是用同一个函数
            /*
             1. 已经打开的数据库对象
             2. 需要执行的SQL语句,c字符串
             3. 执行SQL语句之后的回调,一般写nil
             4. 是第三个参数的第一个参数,一般传nil
             5. 错误信息,一般传nil
             
             SQLITE_API int SQLITE_STDCALL sqlite3_exec(
             sqlite3*,                                  /* An open database */
             const char *sql,                           /* SQL to be evaluated */
             int (*callback)(void*,int,char**,char**),  /* Callback function */
             void *,                                    /* 1st argument to callback */
             char **errmsg                              /* Error msg written here */
             );
             */
            
            if sqlite3_exec(dbBase, cSQL, nil, nil, nil) != SQLITE_OK {
                return false
            }
            return true
        }
    

    创建表

    @discardableResult func createTab() -> Bool{
            
            //1、编写SQL语句
            let sql = "CREATE TABLE IF NOT EXISTS T_Person\n" +
                "(\n" +
                "id INTEGER PRIMARY KEY AUTOINCREMENT,\n" +
                "name TEXT NOT NULL,\n" +
                "age INTEGER, \n" +
                "money REAL DEFAULT 100.0\n" +
            ");"
            print(sql)
            
            let flag = execSQL(sql: sql)
            if !flag {
                print("创建表失败")
            }
            return flag
        }
    这里要注明一下,为什么SQL语句是这样子组成的,看了打印信息就明白了
    CREATE TABLE IF NOT EXISTS T_Person
    (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    age INTEGER, 
    money REAL DEFAULT 100.0
    );
    

    <br />

    • 2.4 增

    刚才我们设计了一个表包括id、name、age、money,然后我们要进行插入数据,但是如果所有的数据都用单例来管理,如果我有5个表,并且每个月都有增删改查,那么我的单例会很臃肿,秉着睡的事情谁干的原则,这个时候我们在创建一个类:Person,这个类自己去管理增删改查。

     /// 插入
        func insertSQL() -> Bool{
            
            //断言
            assert(name != nil, "姓名不能为空")
            
            //1、编写SQL语句
            //如果插入的是可选的,那么你会发现你插入的前面有一个Optional,这是很尴尬的事情,
            //可以通过 ! 解决
            
            var sql : String = ""
            if id == -1 {
                sql = "INSERT INTO T_Person (name,age,money) VALUES('\(name!)',\(age == -1 ? 0 : age),\(money == -1 ? 100.0 : money));"
            }
            else{
                sql = "INSERT INTO T_Person (id,name,age,money) VALUES(\(id),'\(name!)',\(age == -1 ? 0 : age),\(money == -1 ? 100.0 : money));"
            }
            //3、执行语句
            return SQLManager.shareInstance().execSQL(sql: sql)
            
        }
    

    <br />

    • 2.5 删

    /// 删除
        func deleteSQL() -> Bool{
            
            //1、编写SQL语句
            let sql = sqlWithType(sql: "DELETE FROM T_Person", contactStr: "AND")
            print("del - \(sql)")
            
            let flag = SQLManager.shareInstance().execSQL(sql: sql)
            
            return false
        }
    

    <br />

    • 2.6 改

    /// 更新
        func updateSQL() -> Bool{
            //断言
            assert(name != nil, "姓名不能为空")
            
            //1、编写SQL语句
            let sql = "UPDATE T_Person \n" +
                        "SET name='\(name!)',age=\(age),money=\(money) \n" +
                        "WHERE id=\(id);";
            
            //3、执行语句
            return SQLManager.shareInstance().execSQL(sql: sql)
        }
    

    <br />

    • 2.7 查

    class func selectSQL(sql : String) -> [Person]{
            //2、获取查询的数据
            let dicts = SQLManager.shareInstance().selectSQL(sql: sql)
            //3、创建一个数组用于保存模型
            var datas = [Person]()
            //4、遍历字典数组,生成模型数组
            for dict in dicts{
                datas.append(Person(dict: dict))
            }
            //5、返回模型数组
            return datas
        }
    

    <br />

    3、完成界面搭建:

    项目结构图

    图片.png

    作为主入口ViewController包括了一个Toolbar、一个TableView、一个右按钮
    其中toolBar又连接着增、删、改、查、排序、分页等界面的入口,在其他中则是一些数据库的优化(插入大量数据的时候如何优化)。

    ViewController

    <br />

    3.1 增

    界面包括四个textfiled,一个button,一个label,一个navigation item

    • textfiled用来给用户输入数据
    • button 用来执行插入操作
    • label用来显示插入状态
    • navigation item用于快速插入数据,便于测试
    增加

    插入按钮代码如下:
    这里需要注意的是:类型的判断,ID,age必须是Int类型,name 必须是String、而money必须是浮点型 Double,所以在取值的时候需要进行判断

     @IBAction func insertData(_ sender: UIButton) {
            //名字为空 不能插入
            if nameTextfield.text?.characters.count == 0 {
                status.text = statuText + "姓名不能为空"
                return
            }
    
            //通过textField创建对象
            var dict = [String : Any]()
            //如果id输入了,就插入ID,否则自增长
            if idTextfield.text?.characters.count != 0{
                if idTextfield.text!.isAllNum() { //判断是不是纯数字
                    dict["id"] = (idTextfield.text! as NSString).intValue
                }
            }
            
            //名字直接添加
            dict["name"] = nameTextfield.text!
            
            //如果输入了年龄并且是纯数字才添加
            if ageTextfield.text?.characters.count != 0 {
                if ageTextfield.text!.isAllNum() { //判断是不是纯数字
                    dict["age"] = (ageTextfield.text! as NSString).intValue
                }
            }
            
            
            if moneyTextfield.text?.characters.count != 0 {
                let double : Double = (moneyTextfield.text! as NSString).doubleValue
                if double > 0{
                    dict["money"] = double
                }
            }
            
            let p = Person(dict: dict)
            if !p.insertSQL() {
                status.text = statuText + "插入失败"
            }else{
                status.text = statuText + "插入成功"
            }
        }
    

    <br />

    3.2 删

    界面包括四个textfiled,一个button,一个label,

    • textfiled用来给用户输入数据
    • button 用来执行插入操作
    • label用来显示插入状态
    删除

    删除按钮代码如下:
    这里需要注意的是条件的拼接、当没有输入条件的时候默认把整个表所有数据删除,这里我们自定义了一个字典,前面是一个结构体,后面是一个可选的值,因为用户可能输入

    @IBAction func deleteData(_ sender: UIButton) {
            
            
            var dict = [Person_Property : Any?]()
            dict[.id] = idTextfield.text
            dict[.name] = nameTextfield.text
            dict[.age] = ageTextfield.text
            dict[.money] = moneyTextfield.text
            
            let p = Person(myDict : dict)
            
            if p.deleteSQL(){
                statuLabel.text = statuText + "删除成功"
            }
            else{
                statuLabel.text = statuText + "删除失败"
            }
            
    
        }
    

    构造方法如下:

    init(myDict : [Person_Property : Any?]){
            super.init()
            
            var dict = [String : Any]()
            
            for (key,value) in myDict {
                if (value as! String).characters.count > 0 {
                    if key.rawValue as String == "id" {
                        if (value as! String).isAllNum() { //并且全是数字
                            dict[key.rawValue as String] = Int((value as! NSString).intValue)
                        }
                    }else if key.rawValue as String == "name" {
                        dict[key.rawValue as String] = value
                        
                    }else if key.rawValue as String == "age" {
                        if (value as! String).isAllNum() { // 并且全是数字
                            dict[key.rawValue as String] = Int((value as! NSString).intValue)
                        }
                    }else if key.rawValue as String == "money" { //
                        if (value as! String).isFloatValue() { //并且是浮点型
                            dict[key.rawValue as String] = (value as! NSString).doubleValue
                        }
                    }
                }
            }
            
            setValuesForKeys(dict)
            
        }
    

    <br />

    3.3 改

    界面包括一个现实所有数据的tableView和一个二级页面,点击cell进入二级页面,修改特定的数据

    页面一
    这里我并没有把ID也列进来,因为当数据量达到一定量的时候如果你贸然去修改ID,并且这个ID是主键,
    很有可能会造成主键冲突,所以ID就一般原则而言要唯一,且不修改
    
    

    这里包括一个关闭按钮、界面采用present的方式显示,一个当前用户信息的label以及三个提供给用户输入的textfield,一个button以及一个更新状态的显示

    页面二

    <br />

    3.4 查 、3.5 排序

    由于这两个非常类似,界面布局都一致,所以就都拿到一起了
    包括一个label、一个textView、一个button、一个tableView

    <br />


    查找、排序

    查找Code

     class func queryPersons(condition : String) -> [Person]{
            //如果输入的为空,就全部加载
            if condition == ""{
                return loadPersons()
            }
            
            //1、编写SQL语句
            let sql = "SELECT * FROM T_Person WHERE \(condition);"
            
           return selectSQL(sql: sql)
            
        }
    

    排序Code

         /// 排序
        ///
        /// - Parameter sort: 字段
        /// - Returns: 数组
        class func querySortPersons(sort : String) -> [Person]{
            //如果输入的为空,就全部加载
            if sort == "" {
                return loadPersons()
            }
            
            //1、编写SQL语句
            let sql = "SELECT * FROM T_Person ORDER BY \(sort);"
            
            return selectSQL(sql: sql)
        }
    

    <br />

    3.6 分页检索数据

    这个界面也很简单,主要包括一个textField、三个Button、一个TableView

    • textField 用于输入每页显示的条目数
    • 三个按钮分别作用于:开始所有、上/下一页
    • tableView主要用于显示数据

    <br />


    LIMIT

    检索代码,通过传入的m,n进行搜索,如果还不是很清楚的可以去我的上一篇简书文章查看。

    class func queryLimitPerson(m : Int32, n : Int32) -> [Person]{
            if n == 0 {
                return loadPersons()
            }
            
            let sql = "SELECT * FROM T_Person LIMIT \(m),\(n)"
            
            return selectSQL(sql: sql)
        }
    

    三个按钮code

     private func limitDatas(m : Int32, n:Int32){
            let pdatas = Person.queryLimitPerson(m : m , n: n)
            if pdatas.count == 0{
                showErrorText()
                return
            }
            
            datas = pdatas
        }
        
        @IBAction func startLimitBtn(_ sender: UIButton) {
            index = 0
            limitDatas(m: Int32(index) * (textField.text! as NSString).intValue, n: (textField.text! as NSString).intValue)
        }
        
        @IBAction func nextBtn(_ sender: Any) {
            index += 1
            limitDatas(m: Int32(index) * (textField.text! as NSString).intValue, n: (textField.text! as NSString).intValue)
        }
        
        @IBAction func preBtn(_ sender: Any) {
            index = (index - 1) < 0 ? 0 : (index - 1)
            limitDatas(m: Int32(index) * (textField.text! as NSString).intValue, n: (textField.text! as NSString).intValue)
        }
    

    4、事务,预编译、线程安全

    4.1 事务:

    事务(Transaction)是一个对数据库执行工作单元。事务(Transaction)是以逻辑顺序完成的工作单位或序列,可以是由用户手动操作完成,也可以是由某种数据库程序自动完成。

    事务(Transaction)是指一个或多个更改数据库的扩展。例如,如果您正在创建一个记录或者更新一个记录或者从表中删除一个记录,那么您正在该表上执行事务。重要的是要控制事务以确保数据的完整性和处理数据库错误。

    使用下面的命令来控制事务:
    BEGIN TRANSACTION:开始事务处理。

    COMMIT:保存更改,或者可以使用 END TRANSACTION 命令。

    ROLLBACK:回滚所做的更改。

    事务控制命令只与 DML 命令 INSERT、UPDATE 和 DELETE 一起使用。他们不能在创建表或删除表时使用,因为这些操作在数据库中是自动提交的。

    <br />

    • 然后我们在SQLManager中添加事务代码
        /// 开启事务
        func beginTransaction(){
            execSQL(sql: "BEGIN TRANSACTION")
        }
        
        /// 提交事务
        func commitTransaction(){
            execSQL(sql: "COMMIT TRANSACTION")
        }
        
        /// 回滚
        func rollbackTransaction(){
            execSQL(sql: "ROLLBACK TRANSACTION")
        }
    

    <br />

    4.2 预编译

    关于预编译的意思,网上很多种,我这里打一个比喻:
    你是一个司机,今天下午要去仓库A里面去那2000件货物。
    预编译:你打电话给仓库,让仓库管理员给你准备好货物,然后你准时到达,装货走人。
    不是预编译:你下午三点到了仓库,再让仓库管理员给你准备货物,清点完成后再装货,在走人。
    所以预编译是可以提高效率的。

    预编译执行语句:

        @discardableResult func batchExecSQL(sql : String , args: CVarArg...) -> Bool{
            //1转化为C字符串
            let cSql = sql.cString(using: String.Encoding.utf8)!
            
            //2、执行预编译
            var stmt : OpaquePointer? = nil
            if  sqlite3_prepare_v2(dbBase, cSql, -1, &stmt, nil) != SQLITE_OK {
                print("预编译失败")
                sqlite3_finalize(stmt)
                return false
            }
            
            //3、进行数据绑定
            /*
             这里要注意,下标从1开始。
             */
            var index : Int32 = 1
            /*
             sqlite3_bind_XX(句柄, 下标(从1开始), 值)
             */
            for objc in args{
                if objc is Int{
                    sqlite3_bind_int64(stmt, index, sqlite3_int64(objc as! Int))
                }else if objc is Double{
                    sqlite3_bind_double(stmt, index, objc as! Double)
                }else if objc is String{
                    //得到字符串
                    let text = objc as! String
                    //得到C字符串
                    let cText = text.cString(using: String.Encoding.utf8)!
                    /*
                     sqlite3_bind_text(句柄, 下标, 字符串, 字符串长度 -1 表示系统自己计算, OC传入nil,SWIFT不行)
                     1 句柄
                     2 下标
                     3 C字符串
                     4 C字符串长度 -1 自动计算
                     5 OC 传入nil 但是SWIFT不行,因为对象提前释放掉了,会导致插入的数据不对
                        typedef void (*sqlite3_destructor_type)(void*);
                     
                        #define SQLITE_STATIC      ((sqlite3_destructor_type)0)
                        #define SQLITE_TRANSIENT   ((sqlite3_destructor_type)-1)
                     
                        第五个参数如果传入SQLITE_STATIC/nil, 那么系统不会保存需要绑定的数据, 如果需要绑定的数据提前释放了, 那么系统就随便绑定一个值
                        第五个参数如果传入SQLITE_TRANSIENT, 那么系统会对需要绑定的值进行一次copy, 直到绑定成功之后再释放
                     
                     但是Swift中并不能直接写 SQLITE_TRANSIENT 或者 -1,需要自定义一个SQLITE_TRANSIENT,来覆盖系统的
                     在 124 行中
                     */
                    sqlite3_bind_text(stmt, index, cText, -1, SQLITE_TRANSIENT)
                }
                index += 1
            }
            
            
            //4、执行SQL语句
            if sqlite3_step(stmt) != SQLITE_DONE {
                print("执行SQL语句失败")
                return false
            }
            
            //5、重置STMT
            if sqlite3_reset(stmt) != SQLITE_OK{
                print("重置句柄失败")
                return false
            }
            
            //6、关闭STMT
            sqlite3_finalize(stmt)
            
            return true
        }
    

    <br />

    4.2 线程安全

    在多线程中操作数据库是有安全隐患的,可能会发生这里在执行插入、另外又在执行删除、更新或者其他的指令,所以多线程操作数据库一定要保证线程安全。
    如何保证呢?

    • 通过创建一个创行队列

    多线程执行操作模式

    //    MARK - Child Thread
        /*
         1 一个唯一的对列名
         2 优先级
         3 队列类型
         4 
         */
        private let dbQueue = DispatchQueue(label: "com.codepgq.github", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.inherit, target: nil)
        //DispatchQueue(label:"com.appcoda.queue2", qos:DispatchQoS.userInitiated)
        func execQueueSQL(action : @escaping (_ manager : SQLManager) ->()){
            //开一个子线程
            DispatchQueue.global().async { 
                action(self)
            }
        }
    

    或者通过这种方式创建串行队列

    // 创建一个串行队列
        fileprivate let dbQueue = DispatchQueue(label: "com.codepgq.github", attributes: [])
    

    4.4OK,了解了上面的知识,我们就搭建最后一个页面:

    界面很简单,就不做介绍了,主要是通过判断打开了哪些switch来进行芳芳的选择。

    其他

    <br />
    所有的方法如下:

        @IBAction func startInsert(_ sender: Any) {
            
            if isSerting {
                showErrorText(message : "正在插入")
                return
            }
            isSerting = true
            
            //计算值
            let value : Int8 = isOnValue(sw: openTrans) * 100 + 10 * isOnValue(sw: openThread) + isOnValue(sw: openPrepare)
            
            print(NSString.init(format: "value - %03d", value))
            /*
             001 010 100 110 101 011 000 111
             */
            switch value {
            case 001:
                //开启了预编译
                insertDatas(true)
            case 010:
                //开启了子线程
                openChildThread()
            case 100:
                //开启了事务
                openTransaction()
            case 011:
                //开启了子线程 预编译
                openTheadAndPrepare()
            case 110:
                //开启了事务 子线程
                openTransAndTheard()
            case 101:
                //开启了事务 预编译
                openTransAndPrepare()
            case 111:
                //开启了事务 子线程 预编译
                openAll()
            default:
                //啥都没开
                insertDatas(false)
            }
            
        }
        
        /// 开启事务和预编译
        func openTransAndPrepare(){
            SQLManager.shareInstance().beginTransaction()
            insertDatas(true)
            SQLManager.shareInstance().commitTransaction()
        }
        
        /// 开启事务和线程
        func openTransAndTheard(){
            SQLManager.shareInstance().execQueueSQL { (manager) in
                self.insertDatas(false)
            }
        }
        
        //开启线程和预编译
        func openTheadAndPrepare(){
            SQLManager.shareInstance().execQueueSQL { (manager) in
                self.insertDatas(true)
            }
        }
        
        //全部打开
        func openAll(){
            SQLManager.shareInstance().execQueueSQL { (manager) in
                manager.beginTransaction()
                self.insertDatas(true)
                manager.commitTransaction()
            }
        }
        
        //开启子线程
        func openChildThread(){
            SQLManager.shareInstance().execQueueSQL { (manager) in
                self.insertDatas(false)
            }
        }
        
        //开启事务
        func openTransaction(){
            //获取数据库对象
            let manager = SQLManager.shareInstance()
            //开始事务
            manager.beginTransaction()
            //插入数据
            insertDatas(false)
            //提交事务
            manager.commitTransaction()
        }
        
        //插入数据
        private func insertDatas(_ prepare : Bool) {
            //得到开始时间
            let start = CFAbsoluteTimeGetCurrent()
            startLabel.text = "开始时间:\(start)"
            
            print(#function,"\(prepare ? "预编译" : "未开启预编译" )")
            
            //开始插入
            
            for index in 0..<insertCount {
                let name = "rand\(index)"
                let age = Int(arc4random() % 100 + 1)
                let money = Double(arc4random() % 10000) + Double(arc4random() % 100) * 0.01
                if prepare {
                    //预编译
                    let sql = "INSERT INTO T_Person (name,age,money) VALUES(?,?,?);"
                    SQLManager.shareInstance().batchExecsql(sql, args: name,age,money)
                }
                else{
                    //直接插入
                    let sql = "INSERT INTO T_Person (name,age,money) VALUES('\(name)',\(age),\(money));"
                    SQLManager.shareInstance().execSQL(sql: sql)
                }
                
            }
            
            
            //得到结束时间
            let end = CFAbsoluteTimeGetCurrent()
            endLabel.text = "结束时间:\(end)"
            
            //得出耗时
            timeLabel.text = "耗时:\(end - start)"
            
            isSerting = false
            
        }
        
        //计算当前是不是开启状态
        private func isOnValue(sw : UISwitch) -> Int8{
            return sw.isOn ? 1 : 0
        }
    

    <br />
    什么都没开启的情况下插入10000条数据:


    什么都没开启

    <br />
    开启了预编译的情况下插入10000条数据:


    开了预编译

    <br />
    开启了事务的情况下插入10000条数据:


    开启了事务

    <br />
    开启了预编译和事务的情况下插入10000条数据:


    开启了事务和预编译

    开启了线程的时候就不会阻塞UI,可自行测试。

    Demo传送门

    写的不好见谅,但是如果对你有帮助,那么我就心满意足了。
    码字不易、喜欢点一下。

    相关文章

      网友评论

      • 4b5f77ed3b5a:楼主你好:有几个问题想请教下:
        第一:swift4.0版本中没有:
        let path = DBName.documentDir()这个方法
        我用这个方法代替可以不
        let path = NSHomeDirectory()+"/Documents/BLOB.sqlite"
        第二,我看了您的demo不知道是从哪里获得的数据库对象,我断点调试了一下,发现在
        override func viewDidLoad() {
        super.viewDidLoad()
        tableView.dataSource = self
        tableView.register(UINib(nibName: "TableViewCell", bundle: nil), forCellReuseIdentifier: CellIdentifier)
        }
        之前就已经获取到了。有店不明白,您能给知道下吗。谢谢谢谢。
        4b5f77ed3b5a:@布吉岛_acd7 在我啃的书里:程序启动后都值先执行ViewController里的 override func viewDidLoad() 这个方法,但是我在这个方法的最开始打断点结果已经是执行了SQLManager里的opendb方法。这个是什么原理呀。 我理解的就是viewDidLoad() 相当于程序的入口,类似于java的main方法,但是现在看的你demo好好像跳过了程序的入口直接就执行了。。
        4b5f77ed3b5a:初学swift ;
        在我啃的书里:程序启动后都值先执行ViewController里的 override func viewDidLoad() 这个方法,但是我在这个方法的最开始打断点结果已经是执行了SQLManager里的opendb方法。这个是什么原理呀。
        Codepgq:第一:是我自己写的extension,可以在demo中的String-DirCategory.swift 中找到
        第二:可以的
        第三:数据库对象?在AppDelegate中就已经打开了表
        //第一步打开数据库
        SQLManager.shareInstance().openDB(DBName: "BLOB.sqlite")
        而数据源是在viewWillAppear中赋值
        datas = Person.loadPersons()
      • 好名字猪娶了:写的不错,学习了,有没有对线程安全的管理呢?你现在介绍的只是对数据多的耗时判断,如果需要管理多个耗时的存储呢?还有数据库除了事务,预编译,线程还有什么能影响它的效率?
      • 旧事年华:不错学习了
      • 春宵一刻值千金:你好,请问你的Demo里的SQLite数据库是放在哪里的,我找不到,看了源码也不明白数据被存到哪里去了
        Codepgq:如果你是模拟器的话你可以打印沙盒路径,进去看下,具体是那个文件夹我也不记得了,应该在Doucuments下面或者是library

      本文标题:Swift3.0 SQLite使用、性能优化、线程安全

      本文链接:https://www.haomeiwen.com/subject/laojmttx.html