我正在尝试在iPhone/iPad应用程序中使用LZMA SDK,我的起点是Mo为iPhone提供的LZMA示例项目,可以在这里找到:https://github.com/jk/lzmaSDK Original I here:http://www.modejong.com/iOS/lzmaSDK.zip (我两个都试过了,都得到了相同的结果)。
问题是,解压缩文件使用的内存与.7z包含的未压缩文件一样多。换句话说,假设我有一个40MB的压缩文件,未压缩的文件是一个大约250MB的二进制sqlite DB,它会慢慢地使用越来越多的内存,因为它会一直解压缩到250MB。这将使iPad1或iPhone4 (256MB内存)之前的任何东西崩溃。我有一种感觉,很多人最终都会遇到同样的问题,所以现在的解决方案可以帮助很多开发人员。
我最初在一台PC上创建了.7z文件,使用的是基于windows的7-zip (最新版本)和16MB的字典大小。它应该只需要18MB的RAM来解压(这就是在PC上查看任务管理器进行测试时的情况)。我也尝试使用keka (开源mac存档程序)创建存档文件,它没有解决任何问题,尽管我可以确认keka本身在mac上解压文件时只使用了19MB的内存,这是我所期望的。我猜下一步是将Keka的源代码与LZMA SDK的源代码进行比较。
在创建.7z文件时,我尝试了不同的字典大小和其他设置,但都没有帮助。我还尝试在压缩之前将我的单个二进制文件拆分成24个较小的片段,但也没有帮助(仍然使用超过250MB的RAM来提取这24个片段)。
请注意,我对原始代码所做的唯一更改是使用了更大的.7z文件。还要注意的是,一旦提取完成,它就会立即释放RAM,但这并没有帮助。我觉得它并没有像它应该提取的那样释放RAM,或者它将整个内容放到RAM中,直到最后完成,然后才将其移出RAM。此外,如果我试图使用mac应用程序提取相同的文件,同时运行仪器,我看不到相同的行为(例如,StuffIt扩展器在提取文件Keka时最大达到了60MB内存,这是一个开源的mac存档程序,最大内存为19MB )。
我还不是一个mac/xcode/objective-c开发人员,所以在这方面的任何帮助都将不胜感激。我可以使用zip或rar代替,但我使用LZMA获得了更好的压缩,所以如果可能的话,我想坚持这个解决方案,但显然我需要让它在不崩溃的情况下工作。
谢谢!

发布于 2012-10-01 14:38:14
Igor Pavlov,7zip的作者,给我发了一封电子邮件,他基本上说我在原始问题中的观察结果是SDK c版本的一个众所周知的局限性。C++版本没有此限制。实际报价:
"7-Zip使用另一个用C++编写的多线程解码器。那个C++ .7z解码器不需要为整个实体块分配内存块。也请阅读此线程:
http://sourceforge.net/projects/sevenzip/forums/forum/45797/topic/5655623“
因此,在有人修复iOS的SDK之前,解决方法是:
1)决定文件解压缩操作的内存限制。
2)归档中任何超过上述1限制的单个文件都必须拆分,您可以使用任何二进制拆分应用程序进行拆分,例如splits:http://www.fourmilab.ch/splits/
3)文件准备就绪后,使用MoDJ在其答案中所述的字典/块大小选项创建7z文件,例如24 meg limit: 7za a -mx=9 -md=24m -ms=24m CompressedFile.7z源文件*
4)在您的iOS应用程序中,在解压缩文件后,确定哪些文件已被拆分,并再次将它们连接在一起。代码并不复杂(我假设splits.exe使用的命名约定是file.001、file.002等)。
if(iParts>1)
{
//If this is a multipart binary split file, we must combine all of the parts before we can use it
NSString *finalfilePath = whateveryourfinaldestinationfilenameis
NSString *splitfilePath = [finalfilePath stringByAppendingString:@".001"];
NSFileHandle *myHandle;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
//If the target combined file exists already, remove it
if ([fileManager fileExistsAtPath:finalfilePath])
{
BOOL success = [fileManager removeItemAtPath:finalfilePath error:&error];
if (!success) NSLog(@"Error: %@", [error localizedDescription]);
}
myHandle = [NSFileHandle fileHandleForUpdatingAtPath:splitfilePath];
NSString *nextPart;
//Concatenate each piece in order
for (int i=2; i<=iParts; i++) {
//Assumes fewer than 100 pieces
if (i<10) nextPart = [splitfilePath stringByReplacingOccurrencesOfString:@".001" withString:[NSString stringWithFormat:@".00%d", i]];
else nextPart = [splitfilePath stringByReplacingOccurrencesOfString:@".001" withString:[NSString stringWithFormat:@".0%d", i]];
NSData *datapart = [[NSData alloc] initWithContentsOfFile:(NSString *)nextPart];
[myHandle seekToEndOfFile];
[myHandle writeData:datapart];
}
[myHandle closeFile];
//Rename concatenated file
[fileManager moveItemAtPath:splitfilePath toPath:finalfilePath error:&error];
}发布于 2012-09-30 18:51:05
好的,这是一个棘手的问题。你遇到问题的原因是因为iOS没有虚拟内存,而你的桌面系统有。lzmaSDK库的编写方式假定您的系统有足够的虚拟内存用于解压缩。您不会看到在桌面上运行时出现问题。只有在iOS上分配大量内存进行解压缩时,才会遇到问题。最好是通过重写lzma SDK来解决这个问题,这样它就可以直接更好地利用映射内存,但这不是一项微不足道的任务。以下是如何解决此问题的方法。
使用7za
实际上,有两个命令行选项需要传递给7zip存档程序,以便将文件分成更小的块。我建议你只使用我最终使用的24兆大小,因为这是一个很好的空间/内存折衷。下面的命令行可以完成此任务,请注意,在本例中,我有一个名为XYZ.flat的大电影文件,我想将它们一起压缩到一个archive.7z文件中:
7za a -mx=9 -md=24m -ms=24m Animations_9_24m_NOTSOLID.7z *.flat如果您将此分段文件与未将文件拆分为段的版本进行比较,您将看到文件在分段后变得稍大一些:
$ ls -la Animations_9_24m.7z Animations_9_24m_NOTSOLID.7z
-rw-r--r-- 1 mo staff 8743171 Sep 30 03:01 Animations_9_24m.7z
-rw-r--r-- 1 mo staff 9515686 Sep 30 03:21 Animations_9_24m_NOTSOLID.7z因此,分段减少了大约800K的压缩,但这并不是很大的损失,因为现在解压缩例程将不会尝试分配一堆内存。解压缩内存使用量现在限制在24兆字节,这是iOS可以处理的。
通过打印压缩文件的标题信息来仔细检查您的结果:
$ 7za l -slt Animations_9_24m_NOTSOLID.7z
Path = Animations_9_24m_NOTSOLID.7z
Type = 7z
Method = LZMA
Solid = +
Blocks = 7
Physical Size = 9515686
Headers Size = 1714注意上面输出中的“block”元素,它表明数据已经被分割成不同的24兆块。
如果将上面的分段文件信息与不带-ms=24m参数的输出进行比较,您将看到:
$ 7za l -slt Animations_9_24m.7z
Path = Animations_9_24m.7z
Type = 7z
Method = LZMA
Solid = +
Blocks = 1
Physical Size = 8743171
Headers Size = 1683注意“块”的值,你不会只想要一个巨大的块,因为这会在iOS上解压缩时尝试分配大量的内存。
发布于 2013-03-02 23:00:18
我也遇到过同样的问题,但我发现了一个更实用的变通方法:
外部"C“int extractLZMAFile(const char *filePath,const char *outPath);
对于非常大的文件(如100MB+数据库文件),我会使用LZMA解压来压缩这个文件。当然,由于LZMA本身没有任何文件容器,您需要提供解压缩文件的名称
上有一个小小的iOS解压包
不幸的是,我不能提供任何消息来源,尽管我很想。
https://stackoverflow.com/questions/12575874
复制相似问题