张东轩的博客

合抱之木,生于毫末;九层之台,起于累土;千里之行,始于足下。

0%

iOS中的文件读取

iOS开发过程中遇到需要读取文件数据的场景,我们第一会想到以下两个接口:

1
2
+[NSData dataWithContentsOfURL:]
+[NSData dataWithContentsOfFile:]

这两个接口会把data直接加载到App的内存中,如果涉及到的文件数据比较大,那么可能会直接导致App爆内存crash

对于这种情况,iOSFoundation框架提供了mmap的数据加载方式,并以下面两种接口给开发者提供了这种能力:

1
2
+[NSData dataWithContentsOfFile:options:error:]
+[NSData dataWithContentsOfURL:options:error:]

其中option中字段NSDataReadingOptions的定义如下:

1
2
3
4
5
typedef NS_OPTIONS(NSUInteger, NSDataReadingOptions) {
NSDataReadingMappedIfSafe = 1UL << 0, // Hint to map the file in if possible and safe
NSDataReadingUncached = 1UL << 1, // Hint to get the file not to be cached in the kernel
NSDataReadingMappedAlways API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0)) = 1UL << 3, // Hint to map the file in if possible. This takes precedence over NSDataReadingMappedIfSafe if both are given.
};

我们使用NSDataReadingMappedIfSafe这个option后,iOS就不会把整个文件的数据全部load进的App的内存了,而是将文件的数据mmap到进程的地址空间中,这样就可以优化爆内存的问题。

下图是在使用NSDataReadingMappedIfSafe前后加载1.7G的文件数据内存占用的差异,可见差异非常大

由此可知使用mmap的方式确实可以解决iOS上load大文件爆内存的问题,后面我们就深入理解一下mmap的原理。