应用沙盒
为了缓解这个问题,App Sandbox 策略有两个方面:
- 应用沙盒使您能够描述您的应用如何与系统交互。然后,系统会授予您的应用完成工作所需的访问权限,仅此而已。
- App Sandbox 允许用户通过打开和保存对话框、拖放和其他熟悉的用户交互透明地授予您的应用额外访问权限。
应用沙盒基于一些简单的原则
通过在每个应用程序的基础上限制对敏感资源的访问,如果攻击者成功利用您应用程序中的安全漏洞,应用沙盒提供了最后一道防线,防止用户数据被盗、损坏或删除,或系统硬件被劫持. 例如,沙盒应用程序必须明确声明其使用权利使用以下任何资源的意图:
- 硬件(相机、麦克风、USB、打印机)
- 网络连接(入站或出站)
- 应用数据(日历、位置、联系人)
- 用户文件(下载、图片、音乐、电影、用户选择的文件)
系统在运行时拒绝对项目定义中未明确请求的任何资源的访问。例如,如果您正在编写一个草图应用程序,并且您知道您的应用程序永远不需要访问麦克风,那么您就不需要请求访问权限,并且系统知道拒绝您的(可能受到威胁的)应用程序使用的任何尝试它。
另一方面,沙盒应用程序可以访问您请求的特定资源,允许用户通过以通常方式(例如拖放)执行典型操作来扩展沙盒,并且可以自动执行许多被认为安全的附加操作,包括:
- 从服务菜单调用服务
- 读取世界上大多数可读的系统文件
- 打开用户选择的文件
解决应用沙盒违规问题
如果您的应用尝试执行应用沙盒不允许的操作,则会发生应用沙盒违规。例如,您已经在本快速入门中看到沙盒应用程序无法从 Web 检索内容。对系统资源访问的细粒度限制是应用沙盒如何在应用受到恶意代码入侵时提供保护的核心。解决此类违规涉及在 Xcode 中添加与您的应用程序需要的功能相对应的特定权利。
通过添加适当的权利来解决应用沙盒违规
-
退出快速入门应用程序。
-
在目标编辑器的 Capabilities 选项卡中,在 App Sandbox 部分中,选择与 Outgoing Connections (Client) 对应的权利。这样做会
TRUE
通过修改.entitlements
属性列表文件将所需权利的值应用到 Xcode 项目。注意: 网络权利是根据谁建立连接而不是数据流的主要方向来指定的。在此示例中,您需要出站连接功能,因为应用程序正在启动连接。充当服务器的应用程序需要入站连接权利。
-
构建并运行应用程序。
预期的网页现在显示在应用程序中。
容器目录和文件系统访问
当您采用 App Sandbox 时,您的应用程序可以访问以下位置:
- **应用程序容器目录。**首次启动时,操作系统会创建一个特殊目录供您的应用程序使用——并且仅供您的应用程序使用——称为容器。系统上的每个用户都在他们的主目录中为您的应用程序获取一个单独的容器;您的应用程序对运行它的用户具有不受限制的容器读/写访问权限。
- **应用组容器目录。**沙盒应用程序可以指定一项权利,使其能够访问一个或多个应用程序组容器目录,每个目录在具有该权利的所有应用程序之间共享。
- **用户指定的文件。**当这些文件被用户明确打开或被用户拖放到应用程序上时,沙盒应用程序(具有适当的权利)会自动获取对任意位置的文件的访问权限。
- **相关项目。**通过适当的授权,您的应用程序可以访问与用户指定的文件同名但扩展名不同的文件。这可用于访问功能相关的文件(例如与电影关联的字幕文件)或以不同格式保存修改后的文件(例如在用户添加图片)。
- **临时目录、命令行工具目录和特定的世界可读位置。**沙盒应用程序对某些其他明确定义的位置中的文件具有不同程度的访问权限。
这些政策将在以下各节中进一步详述。
搜索
应用沙盒设计指南
- 目录
介绍应用沙盒快速入门创建 Xcode 项目确认应用已被沙盒化解决应用沙盒违规问题下一步深度应用沙盒需要最后一道防线权利和系统资源访问容器目录和文件系统访问安全范围书签和持久资源访问应用沙盒和代码签名外部工具、XPC 服务和权限分离IPC 和 POSIX 信号量和共享内存为应用沙盒设计将应用程序迁移到沙盒应用沙盒清单修订记录
深度应用沙盒
您采用 App Sandbox 的具体步骤因您的 App 而异,但 App Sandbox 用于保护用户数据的访问控制机制保持一致:
- **权利。**与 macOS 沟通您的应用程序完成工作所需的特定系统资源,仅此而已。
- **容器。**仅访问被认为对您的应用程序安全的文件和目录。
- **持久资源访问。**在您的应用程序启动期间将安全范围的书签保留到用户专门授予您的应用程序访问权限的任何其他文件中。
- **代码签名。**明确地向系统标识您的应用程序,以便其他应用程序无法伪装成您的应用程序。
- **特权分离。**将您的应用程序拆分为更小的部分,每个部分都有自己的资源权限,如果任何一个部分受到损害,则可以最大限度地减少损失。
需要最后一道防线
您可以按照*安全编码指南中*推荐的做法保护您的应用程序免受恶意软件的攻击。但是,尽管您尽了最大努力来构建一个无懈可击的屏障——通过避免缓冲区溢出和其他内存损坏、防止用户数据泄露以及消除其他漏洞——您的应用程序仍可能被恶意代码利用。攻击者只需在您的防御或您链接的任何框架和库中找到一个漏洞,即可控制您的应用程序与系统的交互。
App Sandbox 旨在通过让您描述您的应用程序与系统的预期交互来正面应对这种情况。然后,系统仅授予您的应用完成其工作所需的访问权限。如果恶意代码获得了正确沙盒应用程序的控制权,它只能访问应用程序沙盒中的文件和资源。
要成功采用 App Sandbox,请使用不同于您可能习惯的思维方式,如表 2-1 所示。
开发时… | 采用 App Sandbox 时…… |
---|---|
添加功能 | 最小化系统资源使用 |
利用整个应用程序的访问权限 | 分区功能,然后不信任每个部分 |
使用最方便的API | 使用最安全的 API |
将限制视为限制 | 将限制视为保护措施 |
权利和系统资源访问
未经沙盒化的应用程序可以访问所有用户可访问的系统资源,包括内置摄像头和麦克风、网络套接字、打印以及大部分文件系统。如果成功受到恶意代码的攻击,此类应用程序可能会表现为具有广泛潜在危害的敌对代理。
当您为您的应用程序启用应用程序沙盒时,您将删除除最少量权限之外的所有权限,然后使用权利有意地逐个恢复它们。的权利是一个键-值对标识的特定功能,如能力以打开出站网络套接字。
一项特殊权利——启用应用沙盒——开启了应用沙盒。当您启用沙箱时,Xcode 会创建一个.entitlements
属性列表文件并在项目导航器中显示它。
如果您的应用程序需要功能,请使用目标编辑器的“摘要”选项卡向您的 Xcode 项目添加相应的权利来请求它。如果您不需要功能,请注意不要包含相应的权利。
您在逐个目标的基础上请求权利。如果您的应用程序只有一个目标(主应用程序),您只需为该目标请求授权。如果您将应用程序设计为使用主应用程序和帮助程序(以 XPC 服务的形式),您需要单独为每个目标请求权利。您可以在外部工具、XPC 服务和权限分离中了解更多相关信息。
您可能需要对应用程序的权利进行比 Xcode 目标编辑器中可用的更细粒度的控制。例如,您可能会请求临时例外权利,因为 App Sandbox 不支持您的应用程序所需的功能,例如向尚未提供任何脚本访问组的应用程序发送 Apple 事件的功能。要使用临时异常权限,请使用 Xcode 属性列表编辑器直接编辑目标的.entitlements
属性列表文件。
注意: 如果您请求临时例外权利,请务必遵循iTunes Connect网站上提供的有关权利的指南。特别是,提交一个 bug,询问您需要的功能,并使用 iTunes Connect 中的 Review Notes 字段来解释为什么您的应用程序需要临时异常。请务必提供错误编号。
macOS App Sandbox 权利在*Entitlement Key Reference*中的启用 App Sandbox中进行了描述。有关为 Xcode 项目中的目标请求权利的演练,请参阅App Sandbox 快速入门。
容器目录和文件系统访问
当您采用 App Sandbox 时,您的应用程序可以访问以下位置:
- **应用程序容器目录。**首次启动时,操作系统会创建一个特殊目录供您的应用程序使用——并且仅供您的应用程序使用——称为容器。系统上的每个用户都在他们的主目录中为您的应用程序获取一个单独的容器;您的应用程序对运行它的用户具有不受限制的容器读/写访问权限。
- **应用组容器目录。**沙盒应用程序可以指定一项权利,使其能够访问一个或多个应用程序组容器目录,每个目录在具有该权利的所有应用程序之间共享。
- **用户指定的文件。**当这些文件被用户明确打开或被用户拖放到应用程序上时,沙盒应用程序(具有适当的权利)会自动获取对任意位置的文件的访问权限。
- **相关项目。**通过适当的授权,您的应用程序可以访问与用户指定的文件同名但扩展名不同的文件。这可用于访问功能相关的文件(例如与电影关联的字幕文件)或以不同格式保存修改后的文件(例如在用户添加图片)。
- **临时目录、命令行工具目录和特定的世界可读位置。**沙盒应用程序对某些其他明确定义的位置中的文件具有不同程度的访问权限。
这些政策将在以下各节中进一步详述。
应用沙盒容器目录
应用沙盒容器目录具有以下特点:
-
它位于系统定义的路径中,在用户的主目录中。在沙盒应用程序中,当您的应用程序调用该
NSHomeDirectory
函数时将返回此路径。 -
您的应用对容器及其子目录具有不受限制的读/写访问权限。
-
macOS 寻路 API(位于 POSIX 层之上)是指特定于您的应用程序的位置。
大多数这些寻路 API 是指相对于您的应用程序容器的位置。例如,容器包含一个单独的
Library
目录(由NSLibraryDirectory
搜索路径常量指定),仅供您的应用程序使用,包括单独的目录Application Support
和Preferences
子目录。将容器用于支持文件不需要更改代码(从应用程序的沙盒前版本开始),但可能需要一次性迁移,如将应用程序迁移到沙盒 中所述。
一些寻路 API(在 POSIX 层之上)引用用户主目录之外的特定于应用程序的位置。例如,在沙盒应用程序中,该
NSTemporaryDirectory
函数提供了一个指向用户主目录之外但特定于您的应用程序和沙箱内的目录的路径;您对当前用户具有不受限制的读/写访问权限。这些寻路 API 的行为针对 App Sandbox 进行了适当调整,无需更改代码。 -
macOS 通过应用程序的代码签名建立并强制执行应用程序与其容器之间的连接。
-
容器位于隐藏位置,因此用户不会直接与其交互。具体来说,该容器不适用于用户文档。它用于您的应用程序使用的文件,以及数据库、缓存和其他特定于应用程序的数据。
对于鞋盒式应用程序,您在其中为用户的内容提供唯一的用户界面,内容进入容器,您的应用程序可以完全访问它。
容器外的 Powerbox 和文件系统访问
您的沙盒应用程序可以通过以下三种方式访问其容器外的文件系统位置:
- 在用户的特定方向
- 通过使用特定文件系统位置的权利(在权利和系统资源访问中描述)
- 当文件系统位置在某些世界可读的目录中时
与用户交互以扩展您的沙箱的 macOS 安全技术称为Powerbox。Powerbox 没有 API。当您使用NSOpenPanel
和NSSavePanel
类时,您的应用程序会透明地使用 Powerbox 。您可以通过使用 Xcode 设置权利来启用 Powerbox,如*权利密钥参考*中的启用用户选择的文件访问中所述。
当您从沙盒应用程序调用打开或保存对话框时,出现的窗口不是由 AppKit 而是由 Powerbox 显示的。当您采用 App Sandbox 时,Powerbox 的使用是自动的——它不需要对您的应用程序的 pre-sandbox 版本进行代码更改。您为打开或保存而实现的附件面板将忠实地呈现和使用。
IPC 和 POSIX 信号量和共享内存
通常,沙盒应用程序不能使用 Mach IPC、POSIX 信号量和共享内存或 UNIX 域套接字(有用)。但是,通过指定请求加入应用程序组的权利,应用程序可以使用这些技术与该应用程序组的其他成员进行通信。
注意: 沙盒应用程序不支持 System V 信号量。
UNIX 域套接字很简单;它们就像任何其他文件一样工作。
您希望在沙盒应用程序中访问的任何信号量或 Mach 端口都必须根据特殊约定命名:
- POSIX 信号量和共享内存名称必须以应用程序组标识符开头,后跟斜杠 (
/
),后跟您选择的名称。 - Mach 端口名称必须以应用程序组标识符开头,后跟句点 (
.
),然后是您选择的名称。
例如,如果您的应用程序组的名称是Z123456789.com.example.app-group
,您可以创建两个名为Z123456789.myappgroup/rdyllwflg
和 的信号量Z123456789.myappgroup/bluwhtflg
。您可以创建一个名为Z123456789.com.example.app-group.Port_of_Kobe
.
NSFileManager
- 什么是NSFileManager?
NSFileManager是用来管理文件系统的
它可以用来进行常见的文件\文件夹操作(拷贝、剪切、创建等) - NSFileManager使用了单例模式singleton
使用defaultManager方法可以获得那个单例对象
[NSFileManager defaultManager]
//NSFileManager 用于判断
NSString *filePath = @"/Users/zhaoxiaohu/Desktop/arr.plist";
NSString *filePath2 = @"/";
1) 判断文件是否存在
//创建文件管理对象
//调用defaultManager 创建一个文件管理的单例对象
//单例对象:在程序运行期间,只有一个对象存在
NSFileManager *fm = [NSFileManager defaultManager];
// YES 存在 NO 不存在
BOOL isYES = [fm fileExistsAtPath:filePath];
NSLog(@"-->%d",isYES);
2) 判断是否是一个目录
if(isYES){
BOOL isDir;
// 2) 判断是否是一个目录
[fm fileExistsAtPath:filePath isDirectory:&isDir];
if (isDir) {
NSLog(@"这是一个目录");
}else{
NSLog(@"这不是一个目录");
}
}
3) 判断文件是否可读
isYES = [fm isReadableFileAtPath:filePath];
4) 是否可写
isYES = [fm isWritableFileAtPath:filePath2];
5) 是否可删除
isYES = [fm isDeletableFileAtPath:filePath2];
NSLog(@"-->%d",isYES);
6)获取文件的信息(属性)
//创建文件对象
NSFileManager *fm = [NSFileManager defaultManager];
NSString *filePath = @"/Users/zhaoxiaohu/Desktop/arr.plist";
NSString *dirPath = @"/Users/zhaoxiaohu/Desktop/a";
//1)如何获取文件的信息(属性)
NSDictionary *dict = [fm attributesOfItemAtPath:filePath error:nil];
NSLog(@"%@",dict);
NSLog(@"%@,%@",[dict objectForKey:@"NSFileOwnerAccountName"],dict[@"NSFileOwnerAccountName"]);
7)获取指定目录下文件及子目录
//使用递归的方式 获取当前目录及子目录下的所有的文件及文件夹
NSArray *subPaths = [fm subpathsAtPath:dirPath];
//(推荐使用)subpathsOfDirectoryAtPath 不是使用递归的方式获取的
subPaths = [fm subpathsOfDirectoryAtPath:dirPath error:nil];
NSLog(@"subPaths = %@",subPaths);
8)获取指定目录下的文件及目录信息(不在获取后代路径)
subPaths = [fm contentsOfDirectoryAtPath:dirPath error:nil];
NSLog(@"subPaths = %@",subPaths);
9)创建目录
//创建文件管理对象
NSFileManager *fm = [NSFileManager defaultManager];
//如何创建目录 (路径 :/Users/zhaoxiaohu/Desktop/aaa)
NSString *createDirPath = @"/Users/zhaoxiaohu/Desktop/aaa/ccc/bbb/love.txt";
// fm createDirectoryAtPath:@"路径" withIntermediateDirectories:YES/NO 创建路径的时候,YES自动创建路径中缺少的目录,NO的不会创建缺少的目录 attributes:属性的字典 error:错误对象
BOOL isYES = [fm createDirectoryAtPath:createDirPath withIntermediateDirectories:YES attributes:nil error:nil];
if (isYES) {
NSLog(@"成功");
}
10)创建文件
//如何创建文件
NSString *str = @"每当我错过一个女孩,我就向山上放一块砖,于是就有了长城";
//writeToFile
//fm createFileAtPath:@"路径" contents: NSData类型的数据 attributes:文件的属性的字典
//创建NSData? 是一个处理二进制数据的类
//NSString -----> NSData (把NSString转化成NSData)
NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding];
BOOL isYes;
// createFileAtPath 创建文件
isYes = [fm createFileAtPath:createDirPath contents:data attributes:nil];
NSLog(@"isYes = %d",isYes);
11)copy文件
//如何copy文件
NSString *targetPath = @"/Users/zhaoxiaohu/Desktop/aaa/ccc/love.txt";
[fm copyItemAtPath:createDirPath toPath:targetPath error:nil];
NSString *targetPath = @"/Users/zhaoxiaohu/Desktop/aaa/love.txt";
12)移动文件
//如何移动文件
[fm moveItemAtPath:createDirPath toPath:targetPath error:nil];
13)删除文件
//如何删除文件
[fm removeItemAtPath:targetPath error:nil];
NSFileHandle
注意:NSFileHandle
类主要对文件内容进行读取和写入操作,可以使用NSFileHandle
做文件的断点续传。
NSFileHandle 此类主要是对文件内容进行读取和写入操作
NSFileMange 此类主要是对文件进行的操作以及文件信息的获取
常用处理方法
+ (id)fileHandleForReadingAtPath:(NSString *)path //打开一个文件准备读取`
`+ (id)fileHandleForWritingAtPath:(NSString *)path //打开一个文件准备写入`
`+ (id)fileHandleForUpdatingAtPath:(NSString *)path //打开一个文件准备更新`
`- (NSData *)availableData; //从设备或通道返回可用的数据`
`- (NSData *)readDataToEndOfFile; //从当前的节点读取到文件的末尾`
`- (NSData *)readDataOfLength:(NSUInteger)length; // 从当前节点开始读取指定的长度数据`
`- (void)writeData:(NSData *)data; //写入数据`
`- (unsigned long long)offsetInFile; //获取当前文件的偏移量`
`- (void)seekToFileOffset:(unsigned long long)offset; //跳到指定文件的偏移量`
`- (unsigned long long)seekToEndOfFile; //跳到文件末尾`
`- (void)truncateFileAtOffset:(unsigned long long)offset; //将文件的长度设为offset字节`
`- (void)closeFile; 关闭文件
基本用法一 追加数据
NSString *homePath = NSHomeDirectory( );
NSString *sourcePath = [homePath stringByAppendingPathConmpone:@"testfile.text"];
NSFileHandle *fielHandle = [NSFileHandle fileHandleForUpdatingAtPath:sourcePath];
[fileHandle seekToEndOfFile]; 将节点跳到文件的末尾
NSString *str = @"追加的数据"
NSData* stringData = [str dataUsingEncoding:NSUTF8StringEncoding];
[fileHandle writeData:stringData]; 追加写入数据
[fileHandle closeFile];
基本用法一 定位数据
NSFileManager *fm = [NSFileManager defaultManager];
NSString *content = @"abcdef";
[fm createFileAtPath:path contents:[content dataUsingEncoding:NSUTF8StringEncoding] attributes:nil];
NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:path];
NSUInteger length = [fileHandle availabelData] length]; 获取数据长度
[fileHandle seekToFileOffset;length/2]; 偏移量文件的一半
NSData *data = [fileHandle readDataToEndOfFile];
[fileHandle closeFile];
基本用法一 复制文件
NSFileHandle *infile, *outfile; //输入文件、输出文件
NSData *buffer; //读取的缓冲数据
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *homePath = NSHomeDirectory( );
NSString *sourcePath = [homePath stringByAppendingPathComponent:@"testfile.txt"]; // 源文件路径
NSString *outPath = [homePath stringByAppendingPathComponent:@"outfile.txt"]; //输出文件路径
BOOL sucess = [fileManager createFileAtPath:outPath contents:nil attributes:nil];
if (!success)
{
return N0;
}
infile = [NSFileHandle fileHandleForReadingAtPath:sourcePath]; //创建读取源路径文件
if (infile == nil)
{
return NO;
}
outfile = [NSFileHandle fileHandleForReadingAtPath:outPath]; //创建并打开要输出的文件
if (outfile == nil)
{
return NO;
}
[outfile truncateFileAtOffset:0]; //将输出文件的长度设为0
buffer = [infile readDataToEndOfFile]; //读取数据
[outfile writeData:buffer]; //写入输入
[infile closeFile]; //关闭写入、输入文件
[outfile closeFile];
数据库管理系统
SQL语言概念
SQL是一种结构化查询语言,TA是专为数据库建立的操作命令集,是一种功能齐全的数据库语言
- 常见的数据库语言:
MySQL、Oracle
数据库管理系统
数据库的特征:
- 以一定方式存储在一起
- 能为多个用户共享
- 具有尽可能少的冗余代码
- 与程序彼此独立的数据集合
讲了这么多数据库相关的知识,那么到底什么是数据库呢
- 数据库(Database)是按照数据结构来组织、存储和管理数据的仓库
数据库的分类
关系型数据库(主流)、对象数据库、层次式数据库
常用关系型数据库
PC端:Oracle、MySQL、SQL Server、Access、DB2、Sybase
嵌入式\移动客户端:SQLite
SQLite是一个轻量级的关系数据库。SQLite最初的设计目标是用于嵌入式系统,TA占用资源非常少,在嵌入式设备中,只需要几百K的内存就够了,目前应用于Android、iOS、Windows Phone等智能手机。iOS使用时SQLite,只需要加入libsqlite3.0.tbd依赖以及引入sqlite3.h头文件即可
数据库中有几个很重要的概念
表:是数据库中一个非常重要的对象,是其他对象的基础。根据信息的分类情况,一个数据库中可能包含若干个数据表
字段:表的“列”称为“字段”,每个字段包含某一专题的信息
记录:是指对应于数据表中一行信息的一组完整的相关信息
SQL中很重要的一点,一定要记住,*SQL对大小写不敏感*
SQL语句后面的分号
在数据库系统中分号是作为分隔每条SQL语句的标准方法,这样就可以在对服务器的相同请求中执行一条以上的语句
需要我们注意的是,某些数据库会有要求在每条SQL命令的末尾加上分号,而SQLite则属于另一类,TA的语句末尾不使用分号
- PS:在我们学习Objective-C时每段语句的末尾都要求加上分号,但是SQLite不需要我们要区分开来
SQLite数据库数据类型
SQLite是无类型的数据库,可以保存任何类型的数据,对于SQLite来说对字段不指定类型是完全有效的
为了使SQLite和其他数据库间的兼容性最大化,SQLite支持“类型近似”的观点,列的类型近似指的是存储列上数据的推荐类型。
SQLite近似类型规则
- 如果类型字符串中包含“int”,那么该字段的亲缘类型是INTEGER
- 如果类型字符串中包含“char”、“clob”或“text”,那么该字段的亲缘类型是TEXT,如VARCHAR
- 如果类型字符串中包含“blob”,那么该字段的亲缘类型是none
- 如果类型字符串中包含“real”、“floa”或“doub”,那么该字段的亲缘类型是real
- 其余情况下,字段的亲缘类型为NUMERIC
SQLite字段约束条件
- not null - 非空
- unique - 唯一
- primary key - 主键
- foreign key - 外键
- check - 条件检查,确保一列中的所有值满足一定条件
- default - 默认
- autoincreatement - 自增型变量
SQL语句
- SQL的语句我们可以分成两个部分来看,分别是:数据操作语言(DML)和数据定义语言(DDL)
查询和更新指令构成了SQL的DML部分:
- 数据插入命令——insert
- 数据库更新命令——update
- 数据库删除命令——delete
- 数据库检索命令——select
DDL部分是我们有能力创建活删除表格,我们也可以定义索引,规定表之间的链接,以及施加表间的约束
- 创建数据库命令——create database
- 修改数据库命令——alter database
- 创建新表的命令——create table
- 变更数据库中的表——alter table
- 删除表——drop table
- 创建索引——create index
- 删除索引——drop index
三、iOS的数据库技术的实现
开始使用SQLite所需要的几个步骤
需要的框架:libsqlite3.0.tbd
- 引入<sqlite3.h>头文件
- 打开数据库
- 执行SQL命令——创建表,增删改查等操作
- 关闭数据库
打开与关闭数据库
- 需要注意的是我们的iOS程序中,一般情况下只有一个数据库,我们可以在数据库中创建多张表来保存不同的信息,但是千万不要创建多个数据库,每个数据库中只有一张表,因为不断的连接,关闭数据库是很耗性能的
创建一个DB类用来进行对数据库的操作
打开数据库
#import "DB.h"
@implementation DB
// 创建数据库指针
static sqlite3 *db = nil;
// 打开数据库
+ (sqlite3 *)open {
// 此方法的主要作用是打开数据库
// 返回值是一个数据库指针
// 因为这个数据库在很多的SQLite API(函数)中都会用到,我们声明一个类方法来获取,更加方便
// 懒加载
if (db != nil) {
return db;
}
// 获取Documents路径
NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES) lastObject];
// 生成数据库文件在沙盒中的路径
NSString *sqlPath = [docPath stringByAppendingPathComponent:@"studentDB.sqlite"];
// 创建文件管理对象
NSFileManager *fileManager = [NSFileManager defaultManager];
// 判断沙盒路径中是否存在数据库文件,如果不存在才执行拷贝操作,如果存在不在执行拷贝操作
if ([fileManager fileExistsAtPath:sqlPath] == NO) {
// 获取数据库文件在包中的路径
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"studentDB" ofType:@"sqlite"];
// 使用文件管理对象进行拷贝操作
// 第一个参数是拷贝文件的路径
// 第二个参数是将拷贝文件进行拷贝的目标路径
[fileManager copyItemAtPath:filePath toPath:sqlPath error:nil];
}
// 打开数据库需要使用一下函数
// 第一个参数是数据库的路径(因为需要的是C语言的字符串,而不是NSString所以必须进行转换)
// 第二个参数是指向指针的指针
sqlite3_open([sqlPath UTF8String], &db);
return db;
}
关闭数据库
// 关闭数据库
+ (void)close {
// 关闭数据库
sqlite3_close(db);
// 将数据库的指针置空
db = nil;
}
创建一个学生类
创建表的方法
// 创建表方法
- (void)createTable {
// 将建表的sql语句放入NSString对象中
NSString *sql = @"create table if not exists stu (ID integer primary key, name text not null, gender text default '男')";
// 打开数据库
sqlite3 *db = [DB open];
// 执行sql语句
int result = sqlite3_exec(db, sql.UTF8String, nil, nil, nil);
if (result == SQLITE_OK) {
NSLog(@"建表成功");
} else {
NSLog(@"建表失败");
}
// 关闭数据库
[DB close];
}
获取表中所有的学生
// 获取表中保存的所有学生
+ (NSArray *)allStudents {
// 打开数据库
sqlite3 *db = [DB open];
// 创建一个语句对象
sqlite3_stmt *stmt = nil;
// 声明数组对象
NSMutableArray *mArr = nil;
// 此函数的作用是生成一个语句对象,此时sql语句并没有执行,创建的语句对象,保存了关联的数据库,执行的sql语句,sql语句的长度等信息
int result = sqlite3_prepare_v2(db, "select * from Students", -1, &stmt, nil);
if (result == SQLITE_OK) {
// 为数组开辟空间
mArr = [NSMutableArray arrayWithCapacity:0];
// SQLite_ROW仅用于查询语句,sqlite3_step()函数执行后的结果如果是SQLite_ROW,说明结果集里面还有数据,会自动跳到下一条结果,如果已经是最后一条数据,再次执行sqlite3_step(),会返回SQLite_DONE,结束整个查询
while (sqlite3_step(stmt) == SQLITE_ROW) {
// 获取记录中的字段值
// 第一个参数是语句对象,第二个参数是字段的下标,从0开始
int ID = sqlite3_column_int(stmt, 0);
const unsigned char *cName = sqlite3_column_text(stmt