John Doe John Doe - 1 month ago 15
Swift Question

Retrieve Images Without Running Out Of Memory

I'm attempting to retrieve images from the user's camera roll but I'm encountering an issue where it runs out of memory. I load 50 images at a time, but unfortunately after a few loads it runs out of memory and crashes. I've tried wrapping the entire statement in an autoreleasepool but that proved not effective. here is my code...

func retrieveImages(thumbnail: Bool, indexOfLibrary: Int) {
/* Retrieve the items in order of modification date, ascending */
var indexSet: NSIndexSet!

let options = PHFetchOptions()

options.sortDescriptors = [NSSortDescriptor(key: "creationDate",
ascending: false)]

/* Then get an object of type PHFetchResult that will contain
all our image assets */
let assetResults = PHAsset.fetchAssetsWithMediaType(.Image,
options: options)

if totalPostsCounted == false {
assetResults.enumerateObjectsUsingBlock { (object: AnyObject!, count: Int, stop: UnsafeMutablePointer<ObjCBool>) -> Void in
if object is PHAsset {
self.totalPosts = count
} else {
self.totalPostsCounted = true
}
}
}

if thumbnail == true {
if totalPosts - libraryImageThumbnails.count < 50 {
allImagesLoaded = true
} else {
if firstTimeLoadedThumb == true {

} else {
firstTimeLoadedThumb = true

}
}

} else {
if totalPosts - libraryImages.count < 50 {
allImagesLoaded = true
} else {
if firstTimeLoaded == true {
} else {
firstTimeLoaded = true

}
}


}


if thumbnail == true {
fiftyIncrementThumb = fiftyIncrementThumb + 50
increment = fiftyIncrementThumb
} else {
fiftyIncrement = fiftyIncrement + 50
increment = fiftyIncrement
}

let imageManager = PHCachingImageManager()
autoreleasepool { () -> () in

assetResults.enumerateObjectsUsingBlock { (object, count: Int, stop: UnsafeMutablePointer<ObjCBool>) -> Void in
if object is PHAsset {

var imageSize: CGSize!

var array: NSArray!

if thumbnail == true {
array = self.libraryImageThumbnails
} else {
array = self.libraryImages
}
autoreleasepool {
if count >= array.count && count <= increment {
if thumbnail == true {
imageSize = CGSize(width: 100, height: 100)

} else {
imageSize = CGSize(width: self.cameraView.bounds.width, height: self.cameraView.bounds.height)

}

let asset = object as! PHAsset
let options = PHImageRequestOptions()
options.deliveryMode = .FastFormat
options.synchronous = true

imageManager.requestImageForAsset(asset,
targetSize: imageSize,
contentMode: .AspectFill,
options: options,
resultHandler: { (image, _: [NSObject : AnyObject]?) -> Void in
if thumbnail == true {
self.libraryImageThumbnails.append(image!)
} else {
self.libraryImages.append(image!)
}
})
}
}
}
}


}


}


I also tried this and it returned nil and crashed...

let library = ALAssetsLibrary()
library.enumerateGroupsWithTypes(ALAssetsGroupAll, usingBlock: { (group: ALAssetsGroup!, stop: UnsafeMutablePointer<ObjCBool>) -> Void in

group.enumerateAssetsUsingBlock({ (asset, count: Int, stop: UnsafeMutablePointer<ObjCBool>) -> Void in
if asset != nil {
self.libraryImageThumbnails.append(asset)
self.collectionTable.reloadData()

}

})
}, failureBlock: { (error: NSError!) -> Void in

})


Tell me if you need any more information! Thank you!

Answer

Here's something I've done in a project that might help:

Some properties:

let requestOptions = PHImageRequestOptions()
let fetchOptions = PHFetchOptions()
let cachingImageManager = PHCachingImageManager()
var assets: [PHAsset] = [] {
    willSet {
        cachingImageManager.stopCachingImagesForAllAssets()
    }

    didSet {
        cachingImageManager.startCachingImagesForAssets(assets,
            targetSize: PHImageManagerMaximumSize,
            contentMode: .AspectFit,
            options: self.requestOptions
        )
    }
}


func fetchPhotos() {
    requestOptions.resizeMode = PHImageRequestOptionsResizeMode.Exact
    requestOptions.version = .Current
    requestOptions.deliveryMode = PHImageRequestOptionsDeliveryMode.HighQualityFormat
    requestOptions.synchronous = true

    fetchOptions.sortDescriptors = [NSSortDescriptor(key:"creationDate", ascending: false)]
    if #available(iOS 9.0, *) {
        fetchOptions.fetchLimit = 50
    } else {

        // Fallback on earlier versions
    }

    fetchResults = PHAsset.fetchAssetsWithMediaType(PHAssetMediaType.Image, options: fetchOptions)
    guard let fetchResults = fetchResults else {
        print("No PHAssets")
        return
    }
    dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)) { [weak self] in
        fetchResults.enumerateObjectsUsingBlock{ object, index, stop in
            let asset = object as! PHAsset
            self?.assets.append(asset)
        }
        dispatch_async(dispatch_get_main_queue()) {
            self?.photoCollectionView.reloadData()
        }
    }
}