首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >LZMA SDK解压缩iOS (xcode)使用了太多的内存

LZMA SDK解压缩iOS (xcode)使用了太多的内存
EN

Stack Overflow用户
提问于 2012-09-25 11:36:55
回答 3查看 3.6K关注 0票数 4

我正在尝试在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获得了更好的压缩,所以如果可能的话,我想坚持这个解决方案,但显然我需要让它在不崩溃的情况下工作。

谢谢!

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 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等)。

代码语言:javascript
复制
    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];
    }
票数 1
EN

Stack Overflow用户

发布于 2012-09-30 18:51:05

好的,这是一个棘手的问题。你遇到问题的原因是因为iOS没有虚拟内存,而你的桌面系统有。lzmaSDK库的编写方式假定您的系统有足够的虚拟内存用于解压缩。您不会看到在桌面上运行时出现问题。只有在iOS上分配大量内存进行解压缩时,才会遇到问题。最好是通过重写lzma SDK来解决这个问题,这样它就可以直接更好地利用映射内存,但这不是一项微不足道的任务。以下是如何解决此问题的方法。

使用7za

实际上,有两个命令行选项需要传递给7zip存档程序,以便将文件分成更小的块。我建议你只使用我最终使用的24兆大小,因为这是一个很好的空间/内存折衷。下面的命令行可以完成此任务,请注意,在本例中,我有一个名为XYZ.flat的大电影文件,我想将它们一起压缩到一个archive.7z文件中:

代码语言:javascript
复制
7za a -mx=9 -md=24m -ms=24m Animations_9_24m_NOTSOLID.7z *.flat

如果您将此分段文件与未将文件拆分为段的版本进行比较,您将看到文件在分段后变得稍大一些:

代码语言:javascript
复制
$ 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可以处理的。

通过打印压缩文件的标题信息来仔细检查您的结果:

代码语言:javascript
复制
$ 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参数的输出进行比较,您将看到:

代码语言:javascript
复制
$ 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上解压缩时尝试分配大量的内存。

票数 0
EN

Stack Overflow用户

发布于 2013-03-02 23:00:18

我也遇到过同样的问题,但我发现了一个更实用的变通方法:

  • 使用LZMA SDK的CPP接口。它只使用很少的内存,并且不会像C接口那样受到内存消耗问题的困扰(正如tradergordo已经正确地说过的那样)。
  • 看看LZMAAlone.cpp,去掉任何不必要的东西(比如编码、7-zip文件格式之类的东西,还有btw。编码仍然需要大内存),并为CPP LZMA解压缩器创建一个很小的头文件,例如:

外部"C“int extractLZMAFile(const char *filePath,const char *outPath);

对于非常大的文件(如100MB+数据库文件),我会使用LZMA解压来压缩这个文件。当然,由于LZMA本身没有任何文件容器,您需要提供解压缩文件的名称

上有一个小小的iOS解压包

不幸的是,我不能提供任何消息来源,尽管我很想。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/12575874

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档