IOS数据存储之Sqlite数据库
前言:
之前学习了数据存储的NSUserDefaults,归档和解档,沙盒文件存储,但是对于数据量比较大,需要频繁查询,删除,更新等操作的时候无论从效率上还是性能上,上述三种明显不能满足我们的日常开发需要了。这个时候我们必须借助数据库,做为Android开发的都知道采用的是一种轻量级数据库Sqlite。其实它广泛用于包括浏览器、IOS,Android以及一些便携需求的小型web应用系统。它具备占用资源低,处理速度快等优点。接下来我们具体认识一下。
我们在项目开发中需要引入libsqlite3.dylib,那么sqllite有哪些具体方法呢?
sqlite3 *db, 数据库句柄,跟文件句柄FILE很类似
sqlite3_stmt *stmt, 这个相当于ODBC的Command对象,用于保存编译好的SQL语句
sqlite3_open(), 打开数据库,没有数据库时创建。
sqlite3_exec(), 执行非查询的sql语句
Sqlite3_step(), 在调用sqlite3_prepare后,使用这个函数在记录集中移动。
Sqlite3_close(), 关闭数据库文件还有一系列的函数,用于从记录集字段中获取数据,如
sqlite3_column_text(), 取text类型的数据。
sqlite3_column_blob(),取blob类型的数据
sqlite3_column_int(), 取int类型的数据
为了系统而且方面的学习sqlite 整理一个sqlite管理类DBManager,实现功能具体涵盖了:数据库的创建,打开,关闭,升级,数据的增删改查,以及事务的开启和开启事务的好处。
DBManager.h
#import <Foundation/Foundation.h> @interface DBManager : NSObject<NSCopying> //创建数据库管理者单例
+(instancetype)shareManager; //打开数据库
-(void)openDb; //关闭数据库
-(void)closeDb; //执行sql语句
-(void)execSql:(NSString *)sql; //创建数据库表
-(void)creatTable; //删除表结构
-(void)dropTable; //插入数据
-(void)insertData:(NSString*)tempName; //插入数据未开启事务
-(void)insertDataByNomarl:(NSArray*)tempNames; //插入数据开启事务
-(void)insertDataByTransaction:(NSArray*)tempNames; //删除数据
-(void)deleteData:(NSString*)tempName; //删除数据
-(void)deleteData; //修改数据
-(void)updateData:(NSString*)tempName; //查询数据
-(void)queryData; @end
DBManager.m
#import "DBManager.h"
#import <sqlite3.h> #define DBNAME @"myDb" //数据库名字
#define TBNAME @"persons" //表名
#define DBVERSION 1 //数据库版本
#define DBVERSIONKEY @"dbversion_key" //存储数据库版本key static DBManager *instance=nil; @implementation DBManager
{
//创建数据库实例
sqlite3 *db;
} -(instancetype)init
{
self=[super init];
if (self) {
[self creatTable];
[self upgrade];
}
return self;
} //创建数据库管理者单例
+(instancetype)shareManager
{
if(instance==nil){
@synchronized(self){
if(instance==nil){
instance =[[[self class]alloc]init];
}
}
}
return instance;
} -(id)copyWithZone:(NSZone *)zone
{ return instance;
} +(id)allocWithZone:(struct _NSZone *)zone
{
if(instance==nil){
instance =[super allocWithZone:zone];
}
return instance;
} //打开数据库
-(void)openDb
{ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documents = [paths objectAtIndex:];
NSString *database_path = [documents stringByAppendingPathComponent:DBNAME]; if (sqlite3_open([database_path UTF8String], &db) == SQLITE_OK) {
NSLog(@"数据库打开成功");
}else{
[self closeDb];
NSLog(@"数据库打开失败");
} } //关闭数据库
-(void)closeDb
{
sqlite3_close(db);
} //检查数据库是否需要升级
- (void)upgrade {
//获取存储好的原版本号
NSInteger oldVersionNum = [[NSUserDefaults standardUserDefaults] integerForKey:DBVERSIONKEY];
if (DBVERSION <= oldVersionNum || oldVersionNum == ) {
return;
} //升级
[self upgrade:oldVersionNum]; // 保存新的版本号到库中 -这里大家可以使用NSUserDefault存储
[[NSUserDefaults standardUserDefaults]setInteger:DBVERSION forKey:DBVERSIONKEY];
} //根据不同版本执行不同的升级逻辑
- (void)upgrade:(NSInteger)oldVersion {
if (oldVersion >= DBVERSION) {
return;
}
switch (oldVersion) {
case :
//执行相应的升级操作
break;
case :
//执行相应的升级操作
break;
case :
//执行相应的升级操作
break;
default:
break;
}
oldVersion ++;
// 递归判断是否需要升级
[self upgrade:oldVersion];
} //执行sql语句
-(void)execSql:(NSString *)sql
{
[self openDb];
char *err;
if (sqlite3_exec(db, [sql UTF8String], NULL, NULL, &err) == SQLITE_OK) {
NSLog(@"数据库操作数据成功!");
}else{
sqlite3_free(err);
NSLog(@"数据库操作数据失败!");
}
sqlite3_close(db);
} //创建数据库表
-(void)creatTable
{
NSString *creatTableSql=[NSString stringWithFormat:@"create table if not exists %@(person_id integer primary key,name text)",TBNAME];
[self execSql:creatTableSql];
} //删除数据库表
-(void)dropTable
{
NSString *dropTableSql=[NSString stringWithFormat:@"drop table %@",TBNAME];
[self execSql:dropTableSql];
} //插入数据
-(void)insertData:(NSString*)tempName
{
NSString *insertSql = [NSString stringWithFormat:@"insert into %@ (name) values ('%@')",TBNAME,tempName];
[self execSql:insertSql];
} //插入数据未开启事务
-(void)insertDataByNomarl:(NSArray*)tempNames
{
[self openDb];
for(NSString *name in tempNames){
NSString *sql = [NSString stringWithFormat:@"insert into %@ (name) values ('%@')",TBNAME,name];
char *err;
if (sqlite3_exec(db, [sql UTF8String], NULL, NULL, &err) == SQLITE_OK) {
NSLog(@"数据库操作数据成功!");
}else{
sqlite3_free(err);
NSLog(@"数据库操作数据失败!");
}
}
[self closeDb];
} //插入数据开启事务
-(void)insertDataByTransaction:(NSArray*)tempNames
{
@try{
char *errorMsg;
[self openDb];
if (sqlite3_exec(db, "BEGIN", NULL, NULL, &errorMsg)==SQLITE_OK) { NSLog(@"启动事务成功"); sqlite3_free(errorMsg); //执行真正的操作
for(NSString *name in tempNames){
NSString *sql = [NSString stringWithFormat:@"insert into %@ (name) values ('%@')",TBNAME,name];
char *err;
if (sqlite3_exec(db, [sql UTF8String], NULL, NULL, &err) == SQLITE_OK) {
NSLog(@"数据库操作数据成功!");
}else{
sqlite3_free(err);
NSLog(@"数据库操作数据失败!");
}
} if (sqlite3_exec(db, "COMMIT", NULL, NULL, &errorMsg)==SQLITE_OK) { NSLog(@"提交事务成功");
} sqlite3_free(errorMsg); }else{
sqlite3_free(errorMsg);
} } @catch(NSException *e){ char *errorMsg;
if (sqlite3_exec(db, "ROLLBACK", NULL, NULL, &errorMsg)==SQLITE_OK) {
NSLog(@"回滚事务成功");
}
sqlite3_free(errorMsg); }
[self closeDb];
} //删除数据
-(void)deleteData:(NSString*)tempName
{
NSString *deleteSql=[NSString stringWithFormat:@"delete from %@ where name = '%@'",TBNAME,tempName];
[self execSql:deleteSql]; } //删除数据
-(void)deleteData
{
NSString *deleteSql=[NSString stringWithFormat:@"delete from %@ ",TBNAME];
[self execSql:deleteSql];
} //修改数据
-(void)updateData:(NSString*)tempName
{
NSString *updateSql=[NSString stringWithFormat:@"update %@ set name ='test' where name = '%@'",TBNAME,tempName];
[self execSql:updateSql]; } //查询数据
-(void)queryData
{
[self openDb];
NSString *querySql =[NSString stringWithFormat:@"select * from %@",TBNAME];
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(db, [querySql UTF8String], -, &stmt, nil) == SQLITE_OK) { while (sqlite3_step(stmt)==SQLITE_ROW) { char *name = (char *)sqlite3_column_text(stmt, );
NSString *nameString = [[NSString alloc] initWithUTF8String:name];
NSLog(@"nameString---->%@",nameString); } sqlite3_finalize(stmt);
}
[self closeDb]; }
@end
具体使用方法:
#import "DBManager.h"
#import <sqlite3.h> #define DBNAME @"myDb" //数据库名字
#define TBNAME @"persons" //表名
#define DBVERSION 1 //数据库版本
#define DBVERSIONKEY @"dbversion_key" //存储数据库版本key static DBManager *shareManager=nil; @implementation DBManager
{
//创建数据库实例
sqlite3 *db;
} -(instancetype)init
{
self=[super init];
if (self) {
[self creatTable];
[self upgrade];
}
return self;
} //创建数据库管理者单例
+(instancetype)shareManager
{
if(shareManager==nil){
@synchronized(self){
if(shareManager==nil){
shareManager =[[[self class]alloc]init];
}
}
}
return shareManager;
} -(id)copyWithZone:(NSZone *)zone
{ return shareManager;
} +(id)allocWithZone:(struct _NSZone *)zone
{
if(shareManager==nil){
shareManager =[super allocWithZone:zone];
}
return shareManager;
} //打开数据库
-(void)openDb
{ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documents = [paths objectAtIndex:];
NSString *database_path = [documents stringByAppendingPathComponent:DBNAME]; if (sqlite3_open([database_path UTF8String], &db) == SQLITE_OK) {
NSLog(@"数据库打开成功");
}else{
[self closeDb];
NSLog(@"数据库打开失败");
} } //关闭数据库
-(void)closeDb
{
sqlite3_close(db);
} //删除数据库
-(void)dropDb
{ } //检查数据库是否需要升级
- (void)upgrade {
//获取存储好的原版本号
NSInteger oldVersionNum = [[NSUserDefaults standardUserDefaults] integerForKey:DBVERSIONKEY];
if (DBVERSION <= oldVersionNum || oldVersionNum == ) {
return;
} //升级
[self upgrade:oldVersionNum]; // 保存新的版本号到库中 -这里大家可以使用NSUserDefault存储
[[NSUserDefaults standardUserDefaults]setInteger:DBVERSION forKey:DBVERSIONKEY];
} //根据不同版本执行不同的升级逻辑
- (void)upgrade:(NSInteger)oldVersion {
//对比数据库版本
if (oldVersion >= DBVERSION) {
return;
}
switch (oldVersion) {
case :
//执行相应的升级操作
break;
case :
//执行相应的升级操作
break;
case :
//执行相应的升级操作
break;
default:
break;
}
oldVersion ++;
// 递归判断是否需要升级
[self upgrade:oldVersion];
} //执行sql语句
-(void)execSql:(NSString *)sql
{
[self openDb];
char *err;
if (sqlite3_exec(db, [sql UTF8String], NULL, NULL, &err) == SQLITE_OK) {
NSLog(@"数据库操作数据成功!");
}else{
sqlite3_free(err);
NSLog(@"数据库操作数据失败!");
}
sqlite3_close(db);
} //创建数据库表
-(void)creatTable
{
NSString *creatTableSql=[NSString stringWithFormat:@"create table if not exists %@(person_id integer primary key,name text)",TBNAME];
[self execSql:creatTableSql];
} //删除数据库表
-(void)dropTable
{
NSString *dropTableSql=[NSString stringWithFormat:@"drop table %@",TBNAME];
[self execSql:dropTableSql];
} //插入数据
-(void)insertData:(NSString*)tempName
{
NSString *insertSql = [NSString stringWithFormat:@"insert into %@ (name) values ('%@')",TBNAME,tempName];
[self execSql:insertSql];
} //插入数据未开启事务
-(void)insertDataByNomarl:(NSArray*)tempNames
{
[self openDb];
for(NSString *name in tempNames){
NSString *sql = [NSString stringWithFormat:@"insert into %@ (name) values ('%@')",TBNAME,name];
char *err;
if (sqlite3_exec(db, [sql UTF8String], NULL, NULL, &err) == SQLITE_OK) {
//NSLog(@"数据库操作数据成功!");
}else{
sqlite3_free(err);
//NSLog(@"数据库操作数据失败!");
}
}
[self closeDb];
} //插入数据开启事务
-(void)insertDataByTransaction:(NSArray*)tempNames
{
@try{
char *errorMsg;
[self openDb];
if (sqlite3_exec(db, "BEGIN", NULL, NULL, &errorMsg)==SQLITE_OK) { NSLog(@"启动事务成功"); sqlite3_free(errorMsg); //执行真正的操作
for(NSString *name in tempNames){
NSString *sql = [NSString stringWithFormat:@"insert into %@ (name) values ('%@')",TBNAME,name];
char *err;
if (sqlite3_exec(db, [sql UTF8String], NULL, NULL, &err) == SQLITE_OK) {
//NSLog(@"数据库操作数据成功!");
}else{
sqlite3_free(err);
// NSLog(@"数据库操作数据失败!");
}
} if (sqlite3_exec(db, "COMMIT", NULL, NULL, &errorMsg)==SQLITE_OK) { NSLog(@"提交事务成功");
} sqlite3_free(errorMsg); }else{
sqlite3_free(errorMsg);
} } @catch(NSException *e){ char *errorMsg;
if (sqlite3_exec(db, "ROLLBACK", NULL, NULL, &errorMsg)==SQLITE_OK) {
NSLog(@"回滚事务成功");
}
sqlite3_free(errorMsg); }
[self closeDb];
} //删除数据
-(void)deleteData:(NSString*)tempName
{
NSString *deleteSql=[NSString stringWithFormat:@"delete from %@ where name = '%@'",TBNAME,tempName];
[self execSql:deleteSql]; } //删除数据
-(void)deleteData
{
NSString *deleteSql=[NSString stringWithFormat:@"delete from %@ ",TBNAME];
[self execSql:deleteSql];
} //修改数据
-(void)updateData:(NSString*)tempName
{
NSString *updateSql=[NSString stringWithFormat:@"update %@ set name ='test' where name = '%@'",TBNAME,tempName];
[self execSql:updateSql]; } //查询数据
-(void)queryData
{
[self openDb];
NSString *querySql =[NSString stringWithFormat:@"select * from %@",TBNAME];
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(db, [querySql UTF8String], -, &stmt, nil) == SQLITE_OK) { while (sqlite3_step(stmt)==SQLITE_ROW) { char *name = (char *)sqlite3_column_text(stmt, );
NSString *nameString = [[NSString alloc] initWithUTF8String:name];
NSLog(@"nameString---->%@",nameString); } sqlite3_finalize(stmt);
}
[self closeDb]; } @end
重点来了,曾经做个IM即时通讯方面,聊天信息相对来说还是比较庞大一点,动不动就是成千上万条聊天信息,有时候执行一个消息已读状态的更新都要耗时很长,那时候偶还没有学习IOS开发,在Android平台上我已经领略过开启事务对效率的提升所带来的喜悦了,那么ios上面是怎么开启事务的呢?效率怎么样呢?让我们一探究竟:
开启事务:
//插入数据开启事务
-(void)insertDataByTransaction:(NSArray*)tempNames
{
@try{
char *errorMsg;
[self openDb];
if (sqlite3_exec(db, "BEGIN", NULL, NULL, &errorMsg)==SQLITE_OK) { NSLog(@"启动事务成功"); sqlite3_free(errorMsg); //执行真正的操作
for(NSString *name in tempNames){
NSString *sql = [NSString stringWithFormat:@"insert into %@ (name) values ('%@')",TBNAME,name];
char *err;
if (sqlite3_exec(db, [sql UTF8String], NULL, NULL, &err) == SQLITE_OK) {
NSLog(@"数据库操作数据成功!");
}else{
sqlite3_free(err);
NSLog(@"数据库操作数据失败!");
}
} if (sqlite3_exec(db, "COMMIT", NULL, NULL, &errorMsg)==SQLITE_OK) { NSLog(@"提交事务成功");
} sqlite3_free(errorMsg); }else{
sqlite3_free(errorMsg);
}
} @catch(NSException *e){ char *errorMsg;
if (sqlite3_exec(db, "ROLLBACK", NULL, NULL, &errorMsg)==SQLITE_OK) {
NSLog(@"回滚事务成功");
}
sqlite3_free(errorMsg);
}
[self closeDb];
}
同时准备一个未开启事务的:
//插入数据未开启事务
-(void)insertDataByNomarl:(NSArray*)tempNames
{
[self openDb];
for(NSString *name in tempNames){
NSString *sql = [NSString stringWithFormat:@"insert into %@ (name) values ('%@')",TBNAME,name];
char *err;
if (sqlite3_exec(db, [sql UTF8String], NULL, NULL, &err) == SQLITE_OK) {
NSLog(@"数据库操作数据成功!");
}else{
sqlite3_free(err);
NSLog(@"数据库操作数据失败!");
}
}
[self closeDb];
}
测试程序:
//测试事务
NSMutableArray *testArray =[[NSMutableArray alloc]init];
int testMaxCount =;
for(int i=;i<testMaxCount;i++){
NSString *string = [[NSString alloc] initWithFormat:@"%d",i];
[testArray addObject:string];
} //未开启事务插入
CFAbsoluteTime start = CFAbsoluteTimeGetCurrent(); [[DBManager shareManager]insertDataByNomarl:testArray];
CFAbsoluteTime end=CFAbsoluteTimeGetCurrent();
NSLog(@"普通插入 time cost: %0.3f", end - start); //删除数据
[[DBManager shareManager]deleteData]; //开启事务插入
start = CFAbsoluteTimeGetCurrent(); [[DBManager shareManager]insertDataByTransaction:testArray]; end=CFAbsoluteTimeGetCurrent();
NSLog(@"开启事务插入 time cost: %0.3f", end - start); //删除数据
[[DBManager shareManager]deleteData];
测试结果:测试数据10000条 单位(秒)
开启事务耗时:0.049
未开启事务耗时:5.614
看到上面的执行结果 是不是惊呆了。
关于数据库升级:由于项目业务发展,数据库有可能要考虑到升级,比如数据库新增表或者已有表结构变化,这时候我们就要考虑到数据升级来做版本兼容:
什么时候检查:
-(instancetype)init
{
self=[super init];
if (self) {
[self creatTable];
[self upgrade];
}
return self;
}
怎么实现版本升级:
//检查数据库是否需要升级
- (void)upgrade {
//获取存储好的原版本号
NSInteger oldVersionNum = [[NSUserDefaults standardUserDefaults] integerForKey:DBVERSIONKEY];
if (DBVERSION <= oldVersionNum || oldVersionNum == ) {
return;
} //升级
[self upgrade:oldVersionNum]; // 保存新的版本号到库中 -这里大家可以使用NSUserDefault存储
[[NSUserDefaults standardUserDefaults]setInteger:DBVERSION forKey:DBVERSIONKEY];
} //根据不同版本执行不同的升级逻辑
- (void)upgrade:(NSInteger)oldVersion {
//对比数据库版本
if (oldVersion >= DBVERSION) {
return;
}
switch (oldVersion) {
case :
//执行相应的升级操作
break;
case :
//执行相应的升级操作
break;
case :
//执行相应的升级操作
break;
default:
break;
}
oldVersion ++;
// 递归判断是否需要升级
[self upgrade:oldVersion];
}
至此原生的Sqlite基础使用就告一段落了,至于高级使用一般情况涉及到的多数是sql语句的使用,sql语句不善长的小伙伴可以去熟悉一下sql数据!这时就在想了IOS有没有像Android一样的第三方数据库框架呢?也让我等sql小白缓解一下压力?特意查询了一下,以下仅供参考:Sqlitepersistentobjects ,FMDB(这个在两年前使用过)。
IOS数据存储之Sqlite数据库的更多相关文章
- Android数据存储之SQLite数据库
Android数据存储 之SQLite数据库简介 SQLite的相关知识,并结合Java实现对SQLite数据库的操作. SQLite是D.Richard Hipp用C语言编写的开源嵌入式数据库引擎. ...
- IOS数据存储之FMDB数据库
前言: 最近几天一直在折腾数据库存储,之前文章(http://www.cnblogs.com/whoislcj/p/5485959.html)介绍了Sqlite 数据库,SQLite是一种小型的轻量级 ...
- IOS 数据存储之 SQLite详解
在IOS开发中经常会需要存储数据,对于比较少量的数据可以采取文件的形式存储,比如使用plist文件.归档等,但是对于大量的数据,就需要使用数据库,在IOS开发中数据库存储可以直接通过SQL访问数据库, ...
- IOS 数据存储之 SQLite具体解释
在IOS开发中常常会须要存储数据,对于比較少量的数据能够採取文件的形式存储,比方使用plist文件.归档等,可是对于大量的数据,就须要使用数据库,在IOS开发中数据库存储能够直接通过SQL訪问数据库, ...
- Android 数据存储之 SQLite数据库存储
----------------------------------------SQLite数据库---------------------------------------------- SQLi ...
- 【Android 应用开发】Android 数据存储 之 SQLite数据库详解
. 作者 :万境绝尘 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/19028665 . SQLiteDataBase示例程序下 ...
- Android之ListView,AsyncTask,GridView,CardView,本地数据存储,SQLite数据库
版权声明:未经博主允许不得转载 补充 补充上一节,使用ListView是用来显示列表项的,使用ListView需要两个xml文件,一个是列表布局,一个是单个列表项的布局.如我们要在要显示系统所有app ...
- Andoid数据存储之SQLite数据库
SQLite是一个嵌入式的并且是一个轻量级的数据库: SQLite数据库支持大部分SQL语法, 允许使用SQL语句操作数据库, 其本质是一个文件, 不需要安装启动: SQLite数据库打开只是打开了一 ...
- Android 数据存储 之 SQLite数据库详解
. 作者 :万境绝尘 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/19028665 . SQLiteDataBase示例程序下 ...
随机推荐
- 【BZOJ】4001: [TJOI2015]概率论
题意 求节点数为\(n\)的有根树期望的叶子结点数.(\(n \le 10^9\)) 分析 神题就打表找规律.. 题解 方案数就是卡特兰数,$h_0=1, h_n = \sum_{i=0}^{n-1} ...
- Hibernate中Criteria的完整用法
1,CriteriaHibernate 设计了 CriteriaSpecification 作为 Criteria 的父接口,下面提供了 Criteria和DetachedCriteria .2,De ...
- CentOS 简单设置samba服务
1.安装 yum -y install samba 2.设置配置文件 1) 备份Samba的配置文件:cp /etc/samba/smb.conf /etc/samba/smb.conf.bak ...
- lua 时间戳和时间互转
1.时间戳转换成时间 local t = 1412753621000 function getTimeStamp(t) return os.date("%Y%m%d%H", ...
- phantomjs 双向认证,访问nginx,https
应用背景: phantomjs的一个爬虫,访问https站点,单向认证(只认证服务器身份)的都可以,双向认证(服务器和客户端都需要认证)必须上传本地证书: 开始用一个包含公钥私钥的PEM证书访问,怎么 ...
- php随机ip
$ip_long = array( array('607649792', '608174079'), //36.56.0.0-36.63.255.255 array('1038614528', '10 ...
- OpenGL Insights 阅读有感 - Tile Based架构下的性能调校 翻译
Performance Tunning for Tile-Based Architecture Tile-Based架构下的性能调校 by Bruce Merry GameKnife译 译序 在大概1 ...
- Unity3d使用高通Vuforia发布IOS工程不支持64位的一些解决办法
1.将Unit升级至4.6.x或5.0.x,将Vuforia差距升级到最新版本(vuforia-unity-mobile-android-ios-4-0-105 ) 2.平台Other Setting ...
- DAO模型
DAO模型 前面我们在使用JDBC时解决的都是一些很简单的问题,例如登录,注册等等,所以有些例直接把代码写在了main方法中.这种写法很容易出现代码臃肿,耦合度高,不能模块化开发等诸多弊端,特别是将来 ...
- 使用 Productivity Power Tools 2013来帮助你提高 VS2013的工作效率
Visual Studio Gallery中发布了Productivity Power Tools 2013 的更新.在此版本中,此版本解决了客户报告的大量错误和问题,并介绍了一项称为语法行压缩的新功 ...