iOS经典讲解之利用单例类封装对SQLite数据库增、删、查、改的操作

iOS开发中SQLite数据库集成与封装实战 随着移动应用功能的日益复杂,高效、可靠的数据存储机制成为iOS应用开发的核心环节之一。数据库作为数据持久化的重要手段,在本地数据管理中扮演着不可或缺的角色。iOS平台原生支持SQLite这一轻量级嵌入式数据库,使其成为移动开发中最广泛使用的数据存储方案之一。本章将引导读者理解为何在iOS开发中选择数据库、数据库在应用架构中的位置,以及SQLite为何成为首选。同时,还将简要介绍本书的整体结构与学习路径,帮助读者建立系统性的认知框架,为后续深入学习打下坚实基础。 阅读详情
#import <Foundation/Foundation.h>
#import <sqlite3.h>
#import "LanOuStudent.h"
@interface SQLiteManager : NSObject
// 创建单例类 进行对数据库操作方法的封装
+ (SQLiteManager *)shareManager;
// 如果你要使用数据库的话 要引入一个框架 libsqllite3.0.dylib 框架
// 使用的话需要引入头文件
// 打开数据库 sqlite3 *表示数据库 返回打开的数据库
- (sqlite3 *)openDB;
// 关闭数据库
- (void)closeDB;
// 创建一个表
- (void)createTable;
// 插入数据
// 插入的不是model而是model的值
- (void)insertWith:(LanOuStudent *)student;
// 删除数据
- (void)deleteStudentWithAge:(NSInteger)age;
//更新数据 根据名字修改年龄
- (void)updateStudentName:(NSString *)name setAge:(NSInteger)age;
- (LanOuStudent *)queryStudentWithName:(NSString *)name andAge:(NSInteger)age;
// 查询所有的学生信息
- (NSArray *)queryAllStudents;
@end
</pre><pre name="code" class="objc"><pre name="code" class="objc">
#import "SQLiteManager.h"

@implementation SQLiteManager

// 初始化方法

+ (SQLiteManager *)shareManager
{
    static SQLiteManager *manager = nil;
    if (manager == nil) {
        manager = [[SQLiteManager alloc] init];
    }
    return manager;
}

// 定义一个静态指针 负责连接数据库
// 保证数据库直到程序结束才会释放
static sqlite3 *db = nil;
// 打开数据库 sqlite3 *表示数据库 返回打开的数据库
- (sqlite3 *)openDB
{
    // 判断
    // 如果数据库不为空 就说明数据库存在 直接返回
    if (db != nil) {
        return db;
    }
    //不存在就创建一个
    // 获取文件路径
    NSString *documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
    // 拼接路径
    // 数据库的名称 Student.sqlite
    NSString *dbPath = [documentsPath stringByAppendingPathComponent:@"Student.sqlite"];
    // 创建数据库
    // 参数1 文件的路径 需要将OC的字符串 转化一下类型 dbPath.UTF8String
    // 参数2 数据库的地址
    // sqlite3_open()该函数 如果存在数据库就打开没有就创建
    int result = sqlite3_open(dbPath.UTF8String, &db);
    // 查看命令返回结果
    //SQLITE_OK 表示成功
    if (result == SQLITE_OK) {
        NSLog(@"创建成功");
        NSLog(@"%@", dbPath);
    } else {
        NSLog(@"创建失败 或者 打开失败");
    }
   
    return db;
   
}
// 关闭数据库
- (void)closeDB
{
    // 关闭数据库
    int result = sqlite3_close(db);
    if (result == SQLITE_OK) {
        // 如果成功了把数据库置空
        db = nil;
        NSLog(@"关闭成功");
    } else {
        NSLog(@"关闭数据库失败");
    }
}

// 创建一个表
- (void)createTable
{
    // 1、打开数据库
    db = [self openDB];
    // 2、写创建表的SQL语句
    NSString *sql = @"create table if not exists lanOuStudent(number integer primary key not NULL, name text nut NULL, age integer not NULL, gender text not NULL)";
    // 3、执行这个语句
    //char **errmsg 错误信息
    char *error = nil;
    int result = sqlite3_exec(db, sql.UTF8String, NULL, NULL, &error);
    // 4、判断是否成功
    if (result == SQLITE_OK) {
        NSLog(@"创建表成功");
    } else {
        NSLog(@"创建失败");
    }
    // 5、关闭数据库
    [self closeDB];
}

// 插入数据
// 插入的不是model而是model的值
- (void)insertWith:(LanOuStudent *)student
{
    //1、打开数据库
   db = [self openDB];
    //2、写SQL语句
    NSString *sql =[NSString stringWithFormat:@"insert into lanOuStudent(name, age, gender, number) values('%@', '%ld', '%@', '%ld')", student.name, student.age, student.gender, student.number];
    //3、执行SQL语句
    int result = sqlite3_exec(db, sql.UTF8String, NULL, NULL, nil);
    //4、判断是否成功
    if (result == SQLITE_OK) {
        NSLog(@"插入成功");
    } else {
        NSLog(@"插入失败");
    }
    //5、关闭数据库
    [self closeDB];
}

// 删除数据
- (void)deleteStudentWithAge:(NSInteger)age
{
    //1、打开数据库
    db = [self openDB];
    // 2、写sql语句
    NSString *sql = [NSString stringWithFormat:@"delete from lanOuStudent where age > '%ld'", age];
    // 3、执行语句
    int result = sqlite3_exec(db, sql.UTF8String, NULL, NULL, nil);
    //4、判断是否成功
    if (result == SQLITE_OK) {
        NSLog(@"删除成功");
    } else {
        NSLog(@"删除失败");
    }
    // 关闭数据库
    [self closeDB];
}

//更新数据 根据名字修改年龄
- (void)updateStudentName:(NSString *)name setAge:(NSInteger)age
{
    // 1、打开数据库
    db = [self openDB];
    //2、写sql语句
    NSString *sql = [NSString stringWithFormat:@"update lanOuStudent set age = '%ld' where name = '%@'", age, name];
    //3、执行sql语句
    int result = sqlite3_exec(db, sql.UTF8String, NULL, NULL, nil);
    //4、判断是否执行成功
    if (result == SQLITE_OK) {
        NSLog(@"更新成功");
    } else {
        NSLog(@"更新失败");
    }
    // 5、关闭数据库
    [self closeDB];
}

// 查询数据
// 根据名字和年龄查询学生
- (LanOuStudent *)queryStudentWithName:(NSString *)name andAge:(NSInteger)age
{
    //1、打开数据库
    db = [self openDB];
    //2、写sql语句
    NSString *sql = [NSString stringWithFormat:@"select * from lanOuStudent where name = '%@' and age = '%ld'", name, age];
    // 3、创建跟随指针 保存sql语句
    sqlite3_stmt *stmt = nil;
    //4、执行语句
    //<#int nByte#>表示sql语句的长度 -1代表无限长
    //<#const char **pzTail#> 截取未执行的部分的语句
    int result = sqlite3_prepare_v2(db, sql.UTF8String, -1, &stmt, NULL);
    //5、判断是否执行成功
    if (result == SQLITE_OK) {
        NSLog(@"查询执行成功");
       
        //6、绑定查询信息 绑定查询条件
        // 查询的条件是啥类型的 就选bind_类型
        // 参数2 查询条件的顺序 从1开始
        //<#const char *#>参数三 填查询条件 字段名
        //<#int n#>参数四 长度
        //<#void (*)(void *)#>参数5绑定后执行的函数
        sqlite3_bind_text(stmt, 1, name.UTF8String, -1, NULL); // 绑定的条件1
        sqlite3_bind_int(stmt, 2, -1); // 绑定条件2
       
    // 7、开始查询
        //sqlite3_step(stmt) == SQLITE_ROW如果这条语句可以被找到 就返回SQLITE_ROW
        while (sqlite3_step(stmt) == SQLITE_ROW) {
           
            //8、获取数据
            // 参数2<#int iCol#>在数据库的表中的列数 列数从0开始 需要打开数据库看一下在第几列
           
            char *name = (char *)sqlite3_column_text(stmt, 1);
            char *gender = (char *)sqlite3_column_text(stmt, 3);
            int age = sqlite3_column_int(stmt, 2);
            int number = sqlite3_column_int(stmt, 0);
           
        // 9、给对象赋值
            LanOuStudent *student = [[LanOuStudent alloc] init];
            //把c语言字符串转会OC字符串
            student.name = [NSString stringWithUTF8String:name];
            student.age = age;
            student.gender = [NSString stringWithUTF8String:gender];
            student.number = number;
           
        // 10、释放跟随指针
            sqlite3_finalize(stmt);
           
            // 关闭数据库
            [self closeDB];
            // 11、返回数据 并释放对象
            return [student autorelease];
        }
       
    } else {
        NSLog(@"查询执行失败");
    }
   
    //12、关闭数据库
    [self closeDB];
    return nil;
}

// 查询所有的学生信息
- (NSArray *)queryAllStudents
{
    //1、打开数据库
    db = [self openDB];
    // 2、写sql语句
    NSString *sql = @"select * from lanOuStudent";
    //3、创建一个跟随指针 来保存sql语句
    sqlite3_stmt *stmt = nil;
    //4、执行语句
    int result = sqlite3_prepare_v2(db, sql.UTF8String, -1, &stmt, NULL);
    //5、判断是否执行成功
    if (result == SQLITE_OK) {
       
        //6、不用绑定查询 创建一个数组保存查询好的model
        NSMutableArray *array = [NSMutableArray array];
        //7、开始查询
        while (sqlite3_step(stmt) == SQLITE_ROW) {
           
            // 8、获取数据
           
            char *name = (char *)sqlite3_column_text(stmt, 1);
            char *gender = (char *)sqlite3_column_text(stmt, 3);
            int age = sqlite3_column_int(stmt, 2);
            int number = sqlite3_column_int(stmt, 0);
            // 9、创建model并赋值
            LanOuStudent *student = [[LanOuStudent alloc] init];
            student.name = [NSString stringWithUTF8String:name];
            student.gender = [NSString stringWithUTF8String:gender];
            student.age = age;
            student.number = number;
        //10、把model装进数组中
            [array addObject:student];
            //11、释放学生对象
            [student release];
           
        }
       
        //12、释放跟随指针
        sqlite3_finalize(stmt);
        // 13、关闭数据库
        [self closeDB];
       
        // 返回数组
        return array;
       
    } else {
        NSLog(@"--查询失败");
    }
   
    [self closeDB];
    return nil;  
}
@end

#import <Foundation/Foundation.h>
@interface LanOuStudent : NSObject
@property (nonatomic, retain) NSString *name;
@property (nonatomic, retain) NSString *gender;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, assign) NSInteger number;
@end

对封装的单例类的应用:
// 创建一个manager对象
   SQLiteManager *manage = [SQLiteManager shareManager];
      // 打开数据库
    [manage openDB];
    // 关闭数据库
    [manage closeDB];
    // 创建一个表
    [manage createTable];
    // 插入数据
    LanOuStudent *studnt = [[LanOuStudent alloc] init];
    studnt.name = @"张三";
    studnt.age = 19;
    studnt.gender = @"男";
    studnt.number = 1;
    [manage insertWith:studnt];
    [studnt release];
    LanOuStudent *studnt1 = [[LanOuStudent alloc] init];
    studnt1.name = @"李四";
    studnt1.age = 19;
    studnt1.gender = @"男";
    studnt1.number = 2;
    [manage insertWith:studnt1];
    [studnt1 release];
// 删除数据
    [manage deleteStudentWithAge:19];
    // 更新数据
    [manage updateStudentName:@"张三" setAge:23];
    // 查询数据 返回一条
   LanOuStudent *students = [manage queryStudentWithName:@"李四" andAge:19];
// 查询数据 返回多条数组
  NSArray *arr = [manage queryAllStudents];

FMDatabase:iOS和macOS SQLite数据库管理实战 在本章中,我们将介绍FMDatabase框架的核心概念和用途。FMDatabase是由Jay Satterfield开发的一个Objective-C封装库,它简化了SQLite数据库操作,使开发者能够更便捷地执行SQL语句,管理数据库连接,以及处理查询结果。 阅读详情

相关推荐

iOS中SQLite的封装(来源于Facebook开源框架)

基于iOS系统的SQLite数据库的封装,源码来源于Facebook,希望能帮到你,谢谢!

iOS sqlite 简单封装使用

欢迎大家关注我的公众号,我会定期分享一些我在项目中遇到问题的解决办法和一些iOS实用的技巧,现阶段主要是整理出一些基础的知识记录下来 文章也会同步更新到我的博客:ppsheep.com sqlite几乎在每个APP编码都会涉及到,它是一种轻量级的数据库,在手机端使用起来极其方便,因为SQLite原生的接口都是使用C写的,不是特别友好,所有有一个强大库就出现了FMDB,今天我们基于FMDB来进行二次...

weixin_34248705的博客 220

iOS 数据库封装SKYdata

在FMDatabase的基础上面扩展了一个利用反射机制来新建数据库字段,无需写任何SQL语句,创建表不需要写,增删改查也不需要,只要继承接口然后给个主键及表就可以进行数据库操作,十分方便。 作者注:里面我有写了一个工厂,这个工厂是为了以后加新的MODEL,你以后只需要专注于新建MODEL,而不需要再关键数据库当中有木有这张表,你只要添加一下就OK了。还有一个对沙盒操作的封装。 小编注:感谢作者@傻傻傻傻傻啊嘴 分享原创代码于Code4App。

基于 iOS SQLite 增删改查基本方法的封装---通用式数据库访问类

1.本文使用iOS 对sqlite的基本操作进行封装,可以自动生成拼接SQL语句,自动绑定数据并执行,简化了数据库的四项主要操作,减少代码量。 首先看下使用样例,新建一个单视图工程,引入所需头文件"WDGDatabaseTool.h"(所需文件见附件),注意添加libsqlite3.0.tbd到工程中。 // // ViewController.m // WDGSqliteTool // //

wdgfnhui的博客 1607

LKDBHelper数据库的使用

iOS研发中数据缓存LKDBHelper的使用

ios-数据库增删改查操作的封装.zip

1.我这份代码是参照JKDBModel完成的,我只是加上了一些注释,删除了UI部分代码。目的只是为了让想学习FMDB的人能省去百度的时间(反正我很多不懂的代码都是靠百度) 2.Tool_FMDBGetDatabase这个类作用主要有2点:定义数据库路径,创建一个多线程安全的数据库 3.Tool_FMDBModel是所有模型的父类,也就是说:如果创建的模型对象想用这个FMDB封装来做数据库缓存,模型对象必须继承自Tool_FMDBModel 4.代码中User这个模型,就是一个示例模型;具体的增删改查示例代码,在viewController.m中

iOS之Sqlite3封装

一、代码下载代码下载地址二、实例效果展示 三、实例项目简介这个实例主要是封装sqlite3数据库工具,并用这个工具实现记录、删除、修改、查询学生信息的功能,另外一个附属的功能就是根据学生的姓名首字母分组并添加索引。对于数据库,我做了两层封装。第一层是对数据库的基本操作进行抽象,第二层是对学生数据的操作进行抽象。这种分层的思想不仅能使处于底层的功能得到多次重用,调高效率;而且这样做逻辑清晰,易于

jiuchabaikaishui的专栏 3783

ios简单数据库封装

面向对象封装ios数据库,代码实现增,删,改,查功能

SQLite数据库增删改查操作

一、使用嵌入式关系型SQLite数据库存储数据 在Android平台上,集成了一个嵌入式关系型数据库——SQLite,SQLite3支持NULL、INTEGER、REAL(浮点数字)、 TEXT(字符串文本)和BLOB(二进制对象)数据类型,虽然它支持的类型只有五种,但实际上sqlite3也接受varchar(n)、 char(n)、decimal(p,s) 等数据类型,只不过在运算或保存时会转

Little_Myna的专栏 970

ios-SQLite封装工具类.zip

SQLite封装工具类

STM32F103C8T6实战:从零搭建三相SPWM逆变器(附MATLAB仿真、PCB、源码)

本文详细介绍了如何使用STM32F103C8T6单片机从零搭建三相SPWM逆变器,涵盖硬件选型、PCB设计、SPWM算法实现及MATLAB仿真。项目提供完整的工程文件,包括源码、PCB设计和仿真模型,适合电子工程师和学生进行实践学习。通过本教程,读者可以掌握逆变器设计的核心技术和调试方法。

weixin_30458043的博客 453

SQLite

1.为什么要用SQLite 1.不需要一个单独的服务器进程或操作的系统(无服务器的)。 2.SQLite 不需要配置,这意味着不需要安装或管理。 3.一个完整的 SQLite 数据库是存储在一个单一的跨平台的磁盘文件。 4.SQLite 是非常小的,是轻量级的,完全配置时小于 400KiB,省略可选功能配置时小于250KiB。 5.SQLite 是自给自足的,这意味着不需要任何外部的依赖...

画心傻乎乎的博客 1万+

国产麒麟系统中的PaddleOCR离线绿色部署:高效安全的文字识别新纪元

摘要:本文详细介绍了PaddleOCR在银河麒麟V10SP1系统上的离线部署方案。通过Conda虚拟环境隔离依赖,采用离线包安装方式实现无网络部署,支持80+语言识别。重点讲解了模型量化剪枝优化、多线程处理等性能调优技巧,并提供了完整的OCR识别代码示例。该方案具有数据本地化处理的安全优势,适用于医疗票据数字化等对隐私要求严格的场景,同时解决了模型加载失败、内存不足等常见问题。

墨夶的博客 1228

sqlite 实例教程 IOS下用sqlite打造词典-IOS开发

声明 欢迎转载,但是请尊重作者劳动成果,转载请保留此框内声明,谢谢。 文章出处:http://blog.csdn.net/iukey sqlite 是个好东西,对于移动平台来说。一直想写有关sqlite的教程,但是不知道从何写起,考虑了很久,还是从一个小Demo 谈起吧。我写了一个精简版的词典,实现了增删查改的基本功能。 工程结构如下。最后效果图如下。 效果图中可以看到,

潇洒的程序员 1万+

达芬奇Pro开发板硬件验证全流程指南

FPGA开发板硬件验证是嵌入式系统可靠性的基石,其核心在于理解SoC启动时序、IO资源映射与高速接口物理层约束。基于Xilinx Zynq-7000异构架构的开发平台,需围绕电源完整性、时钟稳定性、信号完整性及软硬协同启动链路展开分层测试。技术价值体现在规避量产失效率、支撑高可靠工业控制与实时图像处理等场景。典型应用涵盖千兆以太网通信验证、DVP摄像头与RGB LCD显示链路调试、以及SFP+光纤接口PRBS误码率测试。本文聚焦达芬奇Pro系列开发板,深入解析JTAG下载、QSPI Flash重烧录、RGM

weixin_42453228的博客 123

2024年软考信息安全工程师备考历年真题汇总

信息安全工程师分属“信息系统”专业,位处中级资格,2016年下半年,第一次开考信息安全工程师(中级)考试。目前每年考试一次。已开考六次,2016年11月12日,2017年5月20日,2018年5月26日,2019年5月25日,2020年11月7日,2021年11月6日为第六次考试。 第五次和第六次考试,采用第二版教材。 2024年软考信息安全工程师备考历年真题汇总

月梦工作室 5104

SQLite 增、删、改、查

1.iOS中实现SQLite的增、删、改、查 http://www.jianshu.com/p/0b9b78e704a4。 2.用数据库实现收藏功能http://www.jianshu.com/p/ac719d33cb3b 3.SQLite数据库的使用 4.[iOS]数据库第三方框架FMDB详细讲解http://www.jianshu.com/p/e2dbd69b6db2 转载于:h...

weixin_30247159的博客 107

【AI工具】Browser use搭建及使用的保姆级教程:让AI帮你自动操作浏览器

【AI工具】Browser use搭建及使用的保姆级教程:让AI帮你自动操作浏览器

funfan的博客 2535

[iOS]数据库第三方框架FMDB详细讲解

[iOS]数据库第三方框架FMDB详细讲解 初识FMDB iOS中原生的SQLite API在进行数据存储的时候,需要使用C语言中的函数,操作比较麻烦。于是,就出现了一系列将SQLite API进行封装的库,例如FMDB、PlausibleDatabase、sqlitepersistentobjects等。 FMDB是一款简洁、易用的封装库。因此,在这里推荐使用第三方框架FMDB

江月夜 1418

Spring AI Alibaba 1.x 源码分析-架构设计

本文通过分析Spring AI Alibaba 框架的源码,分析出其原理和架构设计,无论是用于项目开发、技术评估还是团队培训都具有重要价值。

program哲学 490

【Autoware】2小时安装Autoware1.13(保姆级教程)

Autoware是一款“一体化”开源自动驾驶软件,能实现感知、决策、控制等功能,通过在Ubuntu中搭建Autoware开发环境和案例的运行,使大家对自动驾驶技术的实现有一个更清晰的认识。

DevFrank的博客 1万+

iOS 数据库增\删\改\查

本博文主要针对数据库增删改查进行讲解,希望大家喜欢

JustinACE的专栏 537

整车的智能配电系统

本文探讨了汽车电网安全的重要性,分析了传统汽车电网的现状,并重点介绍了半导体在汽车智能配电系统中的作用。半导体凭借其单向导电性和可控性,如二极管、MOSFET等,提供了更高效、安全的电力控制。通过对比传统保险丝与半导体器件,阐述了半导体在过流保护、电压监测和故障诊断方面的优势,强调其在减少安全隐患、提高能源管理和续航里程方面的贡献。极氪智能配电系统展示了如何运用半导体技术实现整车能源的精细化管理,降低维修成本,减少线束复杂性,提升安全性能。

让你爱上电路设计 1612

数据缓存——SQLite关系型数据库的使用

SQLite关系型数据库的使用   数据库(Database): 存放数据的仓库, 存放的是一张的表, 特别像Excel, Numbers, 都以表格的形式存放数据, 可以创建多张表。   常见的数据库: sqlite, MySQL, SQLServer, Oracle, Access。 使用数据库,主要是因为文件读写和归档读取数据需要一次把数据全部读出来, 占用内存开销大;其次是数据库

番薯大佬的专栏 6516

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

Flutter SqlLite数据库快速入门

一、前言 光阴荏苒,转眼间2019年快过去一大半了!Flutter也从年初的1.2正式版到现在的1.5正式版,并且4月底谷歌IO大会宣布Flutter支持WEB端开发嵌入式开发,至此F已经支持全端开发了,这足以看到谷歌对这框架的投入;是时候花时间去学习与归纳Flutter知识了! 二、sqflite插件 sqflite基于Sqlite开发的一款flutter插件,其支持iOS和A...

ChessLuo 3896

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

IOS sqlite简单使用 以及为什么sqlite3_open无法创建数据库的原因

一、IOS数据存储方式 1.IOS中数据的存储方式有: 1)plist(NSArray、NSDictionary) 2)Preference(偏好设置,NSUserDefaulters) 3)NSCoding(NSKeyedArchiver) 4)sqlite3(数据库) 5)Core Data 2.其中归档、偏好设置还有plist存储都无法存储大量的数据。故大批量数据通常使

smilefish12345的博客 3513

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

上一篇: iOS经典讲解之复杂对象的数据持久化-归档与反归档
下一篇: iOS经典讲解之iOS8新特性UIAlertController
刘新林
博客等级 码龄11年 72粉丝 61原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值