沙盒机制
什么是沙盒?
沙盒通过限制应用的执行操作来显著提高操作系统的安全性,在iOS上一个应用无法访问另一个应用程序的沙盒。每一个应用都具有一个沙盒,是可以用来存储数据的目录。如果应用需要访问的数据不在沙盒上,则需要通过系统接口请求数据。例如:应用程序无法直接访问用户设备的照片,要访问用户照片第三方应用程序需要访问系统界面,系统界面增加一层安全性。
沙盒在哪?
在xcode工程中,使用NSHomeDirectory()
来查看沙盒的根路径。
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
print(NSHomeDirectory())
return true
}
如果在模拟器上运行,在控制台得到:
/Users/ryan/Library/Developer/CoreSimulator/Devices/A9220FEF-FAC0-4B97-B036-0A1AE68C4ECC/data/Containers/Data/Application/80689FD1-6DF4-4B61-B2DF-718E039A6B37
沙盒是什么样的?
应用沙盒并不是空容器的形式生成,而是包含了一些目录并且这些目录有明确的作用。
我们使用以下代码查看沙盒路径下的目录内容。
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if let items = try? FileManager.default.contentsOfDirectory(atPath: NSHomeDirectory()) {
for item in items {
print(item)
}
}
// Override point for customization after application launch.
return true
}
命令行的输出:
.com.apple.mobile_container_manager.metadata.plist
Library
Documents
tmp
SystemData
第一项是操作系统的属性列表,剩下的条目是沙盒下的文件目录。
Documents
Documents目录非常适合用于存储于用户数据之间相关的数据,于macOS的Documents目录作用非常相似。
如果你的应用程序使用SQLite数据库存储用户数据,则数据库文件数据可以存储在Documents目录下。
Library
Library目录包含Caches目录和Preferences目录。
- Caches目录适合存储缓存数据,当操作系统需要释放存储空间时会清楚该目录,iCloud和iTunes不会备份该处文件。
- Preferences目录包含默认数据库属性列表。如果将键值对存储在默认数据库中,键值对将会存储在默认数据库的属性列表中。
let userDefaults = UserDefaults.standard
userDefaults.set(true, forKey: "DidLaunch")
tmp
临时数据应该存储在tmp目录,会实时清楚以保证占用不必要的磁盘空间,像Caches目录一样不被iCloud和iTunes保存。
Application Bundle
应用程序本身不位于沙盒中,使用Bundle.main.bundlePath
或者Bundle.main.bundleURL
查询Bundle路径。
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
print(NSHomeDirectory())
print(Bundle.main.bundlePath)
return true
}
控制台输出:
/Users/ryan/Library/Developer/CoreSimulator/Devices/A9220FEF-FAC0-4B97-B036-0A1AE68C4ECC/data/Containers/Bundle/Application/811BFF24-4A3C-4D7E-948B-942430212232/SandBoxTest.app
发现Bundle路径和沙盒路径不同,你不能也不应该修改Bundle里的内容,如果被修改操作系统会拒绝启动应用。
参考文章
文件管理实例
将网络获取数据序列号写入沙盒Cache目录下。
- (void)_archiveListDataWithArray:(NSArray<ListItem *> *)array {
// 获取沙盒Cache路径
NSArray *pathArray = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cachePath = [pathArray firstObject];
// 文件管理单例
NSFileManager *fileManager = [NSFileManager defaultManager];
// 创建文件夹路径
NSString *dataPath = [cachePath stringByAppendingPathComponent:@"Data"];
// 创建文件Data夹
NSError *creatError;
[fileManager createDirectoryAtPath:dataPath withIntermediateDirectories:YES attributes:nil error:&creatError];
// 创建文件路径
NSString *listDataPath = [dataPath stringByAppendingPathComponent:@"list"];
// 序列号
NSData * listData = [NSKeyedArchiver archivedDataWithRootObject:array requiringSecureCoding:YES error:nil];
// 写入文件
[fileManager createFileAtPath:listDataPath contents:listData attributes:nil];
// 读取文件数据
NSData *readListData = [fileManager contentsAtPath:listDataPath];
// 反序列化
__unused id unarchiveObj = [NSKeyedUnarchiver unarchivedObjectOfClasses:[NSSet setWithObjects:[NSArray class], [ListItem class], nil] fromData:readListData error:nil];
NSLog(@"");
}