Note that there are some explanatory texts on larger screens.

plurals
  1. POUIImageView performance within UITableViewCell using ASIHTTPRequest
    primarykey
    data
    text
    <p>This problem has been vexing me for a little over a day. The gist is that I have a Grouped UITableView setup such that there is only one entry per section. Each cell within the table view contains a UIImageView that takes up the entire width and length of the cell. Furthermore, each cell is at a specified set height and width.</p> <p>Here is my init method:</p> <pre><code>- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{ self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; if (self) { [[self layer] setMasksToBounds:NO]; [[self layer] setShouldRasterize:YES]; [[self layer] setRasterizationScale:[[UIScreen mainScreen] scale]]; [[self layer] setCornerRadius:10]; _bgCoverView = [[UIImageView alloc] initWithFrame:CGRectZero]; [_bgCoverView setClipsToBounds:YES]; [[_bgCoverView layer] setCornerRadius:10]; [_bgCoverView setContentMode:UIViewContentModeScaleAspectFill]; [self.contentView addSubview:_bgCoverView]; [_bgCoverView release]; //Init other parts of the cell, but they're not pertinent to the question } return self; } </code></pre> <p>This is my layout subviews:</p> <pre><code>- (void)layoutSubviews{ [super layoutSubviews]; if (!self.editing){ [_bgCoverView setFrame:CGRectMake(0, 0, tableViewCellWidth, self.contentView.frame.size.height)]; } } </code></pre> <p>This is my setter for the cell:</p> <pre><code>- (void)setGroup:(Group*)group{ //Set background cover for the contentView NSSet *usableCovers = [[group memories] filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"pictureMedData != nil || pictureFullData != nil || (pictureMedUrl != nil &amp;&amp; NOT pictureMedUrl contains[cd] '/missing.png')"]]; Memory *mem = [usableCovers anyObject]; [_bgCoverView setImage:[UIImage imageWithContentsOfFile:@"no_pics_yet.png"]]; if (mem != nil){ [Folio cacheImageAtStringURL:[mem pictureMedUrl] forManagedObject:mem withDataKey:@"pictureMedData" inDisplay:_bgCoverView]; } } </code></pre> <p>This is where I think I'm getting a performance ding. First, there's the filtered set call. If there are a lot of objects in the set, this could slow stuff down. However, there tend to be relatively few objects. Furthermore, when I removed this line of code, I saw no performance change, so it's pretty unlikely.</p> <p>So, that pretty much leaves my cache method (which is located in a helper class). Here's the method:</p> <pre><code>+(void)cacheImageAtStringURL:(NSString *)urlString forManagedObject:(NSManagedObject*)managedObject withDataKey:(NSString*)dataKey inDisplay:(NSObject *)obj{ int objType = 0; //Assume UIButton if (obj == nil) objType = -1; else if ([obj isKindOfClass:[UIImageView class]]) objType = 1; //Assign to UIImageView NSData *data = (NSData*)[managedObject valueForKey:dataKey]; if ([data bytes]){ if (objType == 0) [(UIButton*)obj setBackgroundImage:[UIImage imageWithData:data] forState:UIControlStateNormal]; else if (objType == 1) [(UIImageView*)obj setImage:[UIImage imageWithData:data]]; return; } //Otherwise, do an ASIHTTPRequest and set the image when it's returned, save the data into core data so that we get a cache hit the next time. } </code></pre> <p>Regardless of if the images are cached or not, there are major performance issues with scrolling down on this view. If I comment out the cache method, it works pretty well. Additionally, other views that call this method are sluggish, so I'm pretty sure it's something here.</p> <p>I will also say, however, that I'm also suspicious of the cornerRadius code, however, I heard that if you set shouldRasterize to YES, it'll result in a performance speed-up. Still, I'm not sure if that's 100% true or if my implementation is off.</p> <p>Any help would be greatly appreciated.</p> <hr> <p><strong>UPDATE</strong></p> <p>Still not 100% fixed yet, but we are getting there.</p> <p>These variables must be set on the request:</p> <pre><code>[request setCachePolicy:ASIOnlyLoadIfNotCachedCachePolicy]; [request setCacheStoragePolicy:ASICachePermanentlyCacheStoragePolicy]; </code></pre> <p>The first tells the cache to only ping the server for images if they are not cached. The second tells the cache to store the images permanently for the lifecycle of the app.</p> <p>This got me part-way there. Some images loaded instantly whereas others seemed sluggish. I did more research and added these lines of code in my app delegate:</p> <pre><code>[[ASIDownloadCache sharedCache] setShouldRespectCacheControlHeaders:NO]; [ASIHTTPRequest setDefaultCache:[ASIDownloadCache sharedCache]]; </code></pre> <p>The first line is crucial. ASIHTTPRequest's default action is to follow what the web server tells you. So, if it tells you in its headers not to cache a response, it won't. This line overrides that. Now, this worked really well on my iOS simulator. Then, I tried it on my 4S and it was slow again.</p> <p>The reason was because I set up a block of code in my request that, upon the successful retrieval of an image, I would save it to Core Data. This call was being triggered every time, even for cached requests. </p> <p>This is because ASIHTTPRequest will still make a request regardless of whether or not it uses a cached copy of your data. If it has a cached copy, it won't send out a request to a server, but will populate its request object's response data with cached data instead. So after my block takes care of display work, I call:</p> <pre><code>if ([request didUseCachedResponse]) return; </code></pre> <p>This works very well on the 4S and the 4. However, the 3GS is still disastrously slow :-(.</p> <p>This is because ASIHTTPRequest is using a cached copy of my NSData and not my UIImage. So, it needs to re-create the image each time it uses the cache. This makes sense because ASIHTTPRequest is only returning data, and doesn't know what you are going to do with the data afterwards, so it'll always call your code that's set up when the request is returned. And because my code block converts the image from data, that's what I get.</p> <p><strong>A WAY FORWARD</strong></p> <p>I think to get the performance speedup I want, it's necessary to implement a custom UIImage cache. If we find the file in the cache, we return the UIImage object. If not, then we create a request and save it in the cache on return.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload