简体   繁体   English

iOS内存加载异步图像导致UIScrollview崩溃

[英]iOS Memory Crashes UIScrollview with asynchronous image loading

I am presenting a two columned scrollview of images similar to Pinterest by using PSCollectionView https://github.com/ptshih/PSCollectionView and loading the images asynchronously using https://github.com/rs/SDWebImage 通过使用PSCollectionView https://github.com/ptshih/PSCollectionView并使用https://github.com/rs/SDWebImage异步加载图像,我呈现了类似于Pinterest的图像的两列滚动视图

On iPhone 5 it is running continuously with no crashes, but on iPhone 4 I run into memory warnings and while scrolling down continually the app eventually crashes. 在iPhone 5上,它连续运行且没有崩溃,但是在iPhone 4上,我遇到了内存警告,并且不断向下滚动时,该应用最终崩溃了。

SDWebImage claims they release cache upon receiving a memory warning and the PSCollectionView manages the # of views by dequeuing reusable views, so I'm not sure what exactly is triggering the problems in memory. SDWebImage声称它们在收到内存警告时释放缓存,而PSCollectionView通过使可重用视图出队PSCollectionView管理视图数,因此我不确定到底是什么触发了内存问题。

I've ran memory profiling but I am not seeing anything that is glaringly obvious--the most memory intensive operation is coming from SDWebImage loading the images asynchronously. 我已经进行了内存分析,但是看不到任何明显的东西-最耗费内存的操作来自SDWebImage异步加载图像。 It does seem like the objectForKey method of getting data from my PFObjects (from parse.com) also takes a good chunk of memory. 似乎从我的PFObjects (从parse.com)获取数据的objectForKey方法也占用了大量内存。

I've attached below in particular the code snippet that is setting the contents of each PSCollectionView Cell. 我在下面特别附上了设置每个PSCollectionView Cell内容的代码段。

Please let me know if you see any specific problems with the code that may lead to memory problems & also let me know if you have suggestions on how to better diagnose this memory problem. 如果您发现代码中可能导致内存问题的任何特定问题,请让我知道;如果您有关于如何更好地诊断此内存问题的建议,也请告诉我。

- (UIView *)collectionView:(PSCollectionView *)collectionView cellForRowAtIndex:(NSInteger)index withRect:(CGRect) recttouse {


    FPCollectionViewCell *thefpcell = (FPCollectionViewCell *)
    [collectionView dequeueReusableViewForClass:[FPCollectionViewCell class]];

    thefpcell.frame = recttouse;

    if (thefpcell == nil) {


        thefpcell = [[FPCollectionViewCell alloc] initWithFrame:recttouse];

        //NSLog(@"creating this cell: %i", index);

        UILabel *cellText = [[UILabel alloc] initWithFrame:CGRectMake(2,0,143,20)];

        thefpcell.cellText = cellText;
        thefpcell.cellText.textAlignment = NSTextAlignmentCenter;


        UIImageView *cellImage = [[UIImageView alloc] initWithFrame:CGRectMake(0,20,recttouse.size.width, recttouse.size.height-20)];

        thefpcell.cellImage = cellImage;

         thefpcell.backgroundColor = [UIColor whiteColor];
        thefpcell.cellText.font = [UIFont fontWithName:@"HelveticaNeue-LightItalic" size:9];

        thefpcell.cellText.backgroundColor = [UIColor clearColor];


        [thefpcell addSubview:cellText];
        [thefpcell addSubview:cellImage];

        }

    //@Brian note--consider moving more of this to other blocks to try and improve performance
    [[NSOperationQueue mainQueue] addOperation:[NSBlockOperation blockOperationWithBlock:^{

            PFObject *thisobj = [self.contentObjectsArray objectAtIndex:index];

         thefpcell.cellText.frame = CGRectMake(2,0,143,20);


        thefpcell.cellImage.frame = CGRectMake(0,20,recttouse.size.width, recttouse.size.height-20);


        thefpcell.cellText.text = [thisobj objectForKey:@"Caption"];

        NSString *imglink = [thisobj objectForKey:@"imgLink"];
        NSString *imgurl;
        if(imglink.length<2)
        {
            PFFile *mydata = [thisobj objectForKey:@"imageFile"];
            imgurl = mydata.url;

        }
        else
        {
            imgurl =imglink;
        }

              UIImage *cellplaceholder = [UIImage imageWithContentsOfFile:@"placeholder.png"];
        [thefpcell.cellImage setImageWithURL:[NSURL URLWithString:imgurl] placeholderImage:cellplaceholder];


        thefpcell.layer.cornerRadius = 9.0;
        thefpcell.layer.masksToBounds = YES;

        }]];


    //NSLog(@"got it: %i", index);

    return thefpcell;
}

Hmm. 嗯。 There's nothing obvious here. 这里没有什么明显的。

SDWebImage registers for UIApplicationDidReceiveMemoryWarningNotification (which is important because in iOS7, NSCache does not respond to memory pressure like it used to). SDWebImage注册UIApplicationDidReceiveMemoryWarningNotification (这很重要,因为在iOS7中, NSCache不像NSCache那样响应内存压力)。 You can confirm this by placing a breakpoint in clearMemory in SDImageCache , and run the app, simulating/reproducing memory warning and confirm, and confirm that this is getting called. 您可以通过在clearMemory中放置一个断点来确认这SDImageCache ,然后运行该应用程序,模拟/复制内存警告并确认,并确认正在调用该方法。 But I think you'll find it is. 但我想您会发现的。 I can't speak to parse.com's handling of the fact that NSCache is no longer automatically purged like it used to be. 我无法说服parse.com处理NSCache不再像NSCache被自动清除的事实。

You should do some heapshots/generations in Instruments as illustrated in WWDC 2012 video iOS App Performance: Memory . 您应该按照WWDC 2012视频iOS App Performance:Memory中的说明在Instruments中进行一些堆放/生成。 So run the the app, get it to a point of quiescence, simulate memory warning, mark heapshot/generation, use the app a bit, simulate memory warning again, and mark heapshot/generation again. 因此,运行该应用程序,使其达到静止状态,模拟内存警告,标记堆快照/生成,稍微使用该应用程序,再次模拟内存警告,然后再次标记堆快照/生成。 Then look at the the objects allocated and still live between the two generations and you'll be on your way towards identifying what's not getting released on you. 然后查看分配的对象,它们仍然在两代之间存在,您将逐步确定哪些内容没有释放。 You can probably identify whether there's something that parse.com isn't cleaning up, or whether it's in your code or in SDWebImage. 您可能可以识别出parse.com是否没有清除某些内容,或者是否在您的代码或SDWebImage中。


A couple of unrelated observations: 几个不相关的观察:

  1. This is going to sound like curious counsel given that you're trying to reduce memory impact, but cellplaceholder is probably the one situation where you really do want to use imageNamed rather than imageWithContentsOfFile . 考虑到您正试图减少内存影响,这听起来像是个好奇的建议,但是cellplaceholder可能是您确实要使用imageNamed而不是imageWithContentsOfFile的一种情况。 Assuming this placeholder will be needed a lot, it will (a) be faster; 假设将非常需要此占位符,它将(a)更快; and (b) might actually negligibly reduce your high water mark if the placeholder is temporarily used a lot (rather than having a bunch of short-live instances of the same placeholder). (b)如果大量使用占位符(而不是一堆相同占位符的短期实例),实际上降低了您的高水位线。

  2. It's extremely unlikely, but if you're going to be updating the cell asynchronously yourself, you really should be re-retrieving the collection view cell to make sure this cell hasn't scrolled off the screen (call [collectionView cellForItemAtIndexPath:indexPath] , not to be confused with the similarly named UICollectionViewDataSource method) and make sure that's not nil ). 这种情况极不可能发生,但是,如果您要自己异步更新单元格,则确实应该重新检索collection view单元格,以确保该单元格没有移出屏幕(调用[collectionView cellForItemAtIndexPath:indexPath] ,不要与类似的UICollectionViewDataSource方法混淆),并确保它不是nil )。

    Frankly I'm not sure why you're dispatching this back to the main queue, anyway. 坦白说,我不确定您为什么将它分派回主队列。 SDWebImage will retrieve the images asynchronously already, so you don't generally have to do any asynchronous processing yourself, and if you really needed some asynchronous processing, you'd send that to some background queue (not back to the main queue). SDWebImage将已经异步检索图像,因此您通常不必自己进行任何异步处理,并且如果您确实需要一些异步处理,则可以将其发送到某个后台队列(而不是返回到主队列)。

    Regardless this construct: 不管这个构造:

     [[NSOperationQueue mainQueue] addOperation:[NSBlockOperation blockOperationWithBlock:^{ // ... }]]; 

    could be simplified to 可以简化为

     [[NSOperationQueue mainQueue] addOperationWithBlock:^{ // ... }]; 

It appears that this simple change of just resizing images before they are loaded from sdwebimage cache has solved my problem. 看起来,只需简单调整大小,然后再从sdwebimage缓存加载图像,就解决了我的问题。 SDWebImage process images before caching I must have been running into some kind of iOS burst limit on my iPhone 4 in terms of how many large sized images can be loaded quickly at once. SDWebImage在缓存之前先处理图像,就一次可以快速加载多少个大尺寸图像而言,我肯定在iPhone 4上遇到了某种iOS突发限制。

My latest memory profile shows that the cache appears to be dumping correctly after hitting a high memory threshold and it only seems to maybe get close to the level for a crash when it hits something like 4 or 5 gifs in a row. 我最新的内存配置文件显示,高速缓存在达到高内存阈值后似乎正在正确转储,并且似乎仅在连续命中4或5个gif时才接近崩溃的水平。

Thanks very much for the help! 非常感谢您的帮助!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM