5

I'm storing about 100 UIImage at one single array. I know there is a memory usage issue that eventually crashes the app, specifically on older devices(iPhone 4s). In terms of User Experience storing all the UIImages on DocumentsDirectory - is not an option(takes too long). So i was thinking about "merging" this two methods. Wait until i'll receive a memory usage warning,stop saving images to my array, and then start storing over the disk. I can't find the right way to handle Memory leak/warning/usage call

 override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        print("memory warning")
    }

When i'm testing on real device, it's just crashes - without call the method. Any suggestions?

Teja Nandamuri
  • 11,045
  • 6
  • 57
  • 109
Roi Mulia
  • 5,626
  • 11
  • 54
  • 105
  • 1
    I think, if you're having issues like this, either reduce the quality of your images or make them wait for the app to write to the disk. – brimstone Feb 11 '16 at 15:12
  • Hey @GabrielJones. Unfortunately i can't. Due to my app needs, all of them should exists together. I cant separate the group in other words. – Roi Mulia Feb 11 '16 at 15:14
  • @GabrielJones, and actually i reduced the quality by almost 60%(which is even less than what i need). A good question might be, what is the most effective way to resize an image, in terms of quality/bytes – Roi Mulia Feb 11 '16 at 15:16
  • Take a look at this: it's in Obj-c though...http://stackoverflow.com/a/7990532/4431068 – brimstone Feb 11 '16 at 15:20
  • What are the sizes of the images? – zaph Feb 11 '16 at 15:21
  • @GabrielJones , Thanks Gabriel - will check this now. – Roi Mulia Feb 11 '16 at 15:22
  • @zaph , In terms of bytes? – Roi Mulia Feb 11 '16 at 15:22
  • 1
    Also look at this: http://stackoverflow.com/q/29794281/4431068 – brimstone Feb 11 '16 at 15:22
  • @zaph . Iphone 4s - Back camera - before scaling - Size of Image: 1779702 bytes . After scaling - Size of Image: 114546 bytes – Roi Mulia Feb 11 '16 at 15:26
  • Where are you getting the images? If you are downloading them and not saving them, then you are using a ton of data. What are your app needs? It would be easier to help you if we knew. You cant have to show all 100 images at the same time, so you should be able to reduce memory usage somehow. – Will M. Feb 11 '16 at 15:30
  • Hey @WillM. My main concern here is the fetch time eventually. Obviously, storing them over the disk won't crash. But retrieve those files(from the disk) is a slow process(100 images in total). Thats why i tried store the UIImage over my app cache, so i can access that immediately. I'm generating those images using GPUImage(great) framework. Eventually, i'm doing X process on this image set and then clear them from memory/disk – Roi Mulia Feb 11 '16 at 15:35
  • And X process on the image set requires the whole image set at one time? You cant do it in batches while fetching the next batch on another thread? – Will M. Feb 11 '16 at 15:39
  • @WillM. I wish, but unfortunately i can't . I'm using AVAssetWriter to manipulate those images eventually, and as far as i know, it won't be better to pause,resume it each time(idk if it's even an option) – Roi Mulia Feb 11 '16 at 15:40

1 Answers1

1

Try to use image cache library. The comparison of the most popular is here: https://bpoplauschi.wordpress.com/2014/03/21/ios-image-caching-sdwebimage-vs-fastimage/

My experience: SDWebImage is the best for URL-sourced images usually from internet and Haneke is good for ID-based images for example thumbnails generated from video. Both available in CocoaPods.

SDWebImage uses CoreData SQLite DB for URL caching. It hasn't methods for "hand make" images but worldwide popular in ~REST applications downloading images from internet. I'm using it in just published in the AppStore MyHairDressers app. FastCache uses files for URL caching. But it also like SDWebImage not suited to cache "hand make" images. Both well suited for images downloaded by URLs. Haneke can store images by custom IDs not only by URLs. But like FastCache it requires some configuration. Here is code for some configurations:

``

HNKCacheFormat *cacheFormatThumbnail = [[HNKCache sharedCache] formats][CACHE_FORMAT_THUMBNAIL];
        if (cacheFormatThumbnail == nil)
        {
            cacheFormatThumbnail = [[HNKCacheFormat alloc] initWithName:CACHE_FORMAT_THUMBNAIL];
            cacheFormatThumbnail.size = CGSizeMake(100.0f, 56.0f);
            cacheFormatThumbnail.scaleMode = HNKScaleModeAspectFit;
            cacheFormatThumbnail.compressionQuality = 0.5f;
            cacheFormatThumbnail.diskCapacity = 10 * 1024 * 1024; // 10MB
            cacheFormatThumbnail.preloadPolicy = HNKPreloadPolicyLastSession;
            [[HNKCache sharedCache] registerFormat:cacheFormatThumbnail];
        }
        HNKCacheFormat *cacheFormatPhoto = [[HNKCache sharedCache] formats][CACHE_FORMAT_PHOTO];
        if (cacheFormatPhoto == nil)
        {
            cacheFormatPhoto = [[HNKCacheFormat alloc] initWithName:CACHE_FORMAT_PHOTO];
            CGFloat scale = [[UIScreen mainScreen] scale];
            cacheFormatPhoto.size = CGSizeMake(1280.0f * scale, 720.0f * scale);
            cacheFormatPhoto.scaleMode = HNKScaleModeAspectFit;
            cacheFormatPhoto.compressionQuality = 0.5f;
            cacheFormatPhoto.diskCapacity = 50 * 1024 * 1024; // 50MB
            cacheFormatPhoto.preloadPolicy = HNKPreloadPolicyNone;
            [[HNKCache sharedCache] registerFormat:cacheFormatPhoto];
        }

``

and here is example for creating cached images (TableViewCell contains CollectionView with thumbnails):

``

- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    VideoCell *cell = (VideoCell *)[super tableView:tableView cellForRowAtIndexPath:indexPath];
    VideoAsset *asset = (VideoAsset *)[self.fetchedResultsController objectAtIndexPath:indexPath];
    if ([asset thumbnails] == 0)
    {
        MBProgressHUD *hud = [[MBProgressHUD alloc] initWithView:[cell thumbnails]];
        hud.removeFromSuperViewOnHide = YES;
        [[cell thumbnails] addSubview:hud];
        hud.labelText = NSLocalizedString(@"H11",nil);
        [hud show:YES];
        CGFloat scale = [[UIScreen mainScreen] scale];
        CGSize size = CGSizeMake(100.0f * scale, 56.0f *scale);
        __weak typeof(cell) weakCell = cell;
        [asset generateThumbnails:self->thumbnailsCount offset:self->thumbnailsOffset size:size completion:^(NSArray *thumbnails) {
            dispatch_async(dispatch_get_main_queue(), ^{
                [hud hide:YES];
            });
            if ((thumbnails != nil) && ([thumbnails count] > 0))
            {
                HNKCache *cache = [HNKCache sharedCache];
                NSUInteger n = 0;
                NSUInteger keyHash = [[[asset assetURL] absoluteString] hash];
                for (UIImage *image in thumbnails)
                {
                    [cache setImage:image forKey:[NSString stringWithFormat:@"%lu@%i",(unsigned long)keyHash,(int)(n++)] formatName:CACHE_FORMAT_THUMBNAIL];
                    dispatch_async(dispatch_get_main_queue(), ^{
                        if (weakCell != nil)
                        {
                            __strong typeof(cell) strongCell = weakCell;
                            [[strongCell thumbnails] reloadData];
                        }
                    });
formatName:CACHE_FORMAT_PHOTO];
                }
            }
        }];
    }
    return (UITableViewCell *)cell;
}

``

and using (collection view cell of the collection in the table view cell):

``

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    ThumbnailCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
    NSString *key = [NSString stringWithFormat:@"%lu@%i",(unsigned long)[[[(VideoAsset *)self->_selectedObject assetURL] absoluteString] hash],(int)[indexPath item]];
    [cell setKey:key];
    [cell setTag:[indexPath item]];
    __weak typeof(cell) weakCell = cell;
    [[HNKCache sharedCache] fetchImageForKey:key formatName:CACHE_FORMAT_THUMBNAIL success:^(UIImage *image) {
        [[weakCell image] setImage:image];
    } failure:^(NSError *error) {
        if ([[error domain] isEqualToString:HNKErrorDomain] && ([error code] == HNKErrorImageNotFound))
        {
            [[weakCell image] setImage:[UIImage imageNamed:@"movieplaceholder"]];
        }
        else [error reportError];
    }];
    return cell;
}

``

  • Thanks, i'll check it! – Roi Mulia Feb 16 '16 at 22:48
  • SDWebImage uses CoreData SQLite DB for URL caching. It hasn't methods for "hand make" images but worldwide popular in ~REST applications downloading images from internet. I'm using it in just published in the AppStore MyHairDressers app. – Alexey Lobanov Feb 18 '16 at 01:04
  • I know it's very useful for online fetch, tho i'm using local created images..And i need to keep them some how over cache, at at least fill the cache and only them store them – Roi Mulia Feb 18 '16 at 01:17
  • I've put above in my answer some code example, sorry on ObjC, for "hand make" not internet -sourced images cache – Alexey Lobanov Feb 18 '16 at 01:33
  • Thanks Alexey for a very useful answer. I'll look deep into it! – Roi Mulia Feb 18 '16 at 01:37