使用大型UIImage数组时的内存问题导致崩溃(swift)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用大型UIImage数组时的内存问题导致崩溃(swift)相关的知识,希望对你有一定的参考价值。
在我的应用程序中,我有一个图像阵列,可以保存在我的相机上拍摄的所有图像。我正在使用collectionView来显示这些图像。但是,当此图像阵列达到大约20张图像时,它会崩溃。我相信这是由于内存问题..如何以一种内存效率的方式将图像存储在图像阵列中?
Michael Dauterman使用缩略图提供了答案。我希望除此之外还有一个解决方案。也许将图片存储到NSData或CoreData中?
Camera.swift:
//What happens after the picture is chosen
func imagePickerController(picker:UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject:AnyObject]){
//cast image as a string
let mediaType = info[UIImagePickerControllerMediaType] as! NSString
self.dismissViewControllerAnimated(true, completion: nil)
//if the mediaType it actually is an image (jpeg)
if mediaType.isEqualToString(kUTTypeImage as NSString as String){
let image = info[UIImagePickerControllerOriginalImage] as! UIImage
//Our outlet for imageview
appraisalPic.image = image
//Picture taken, to be added to imageArray
globalPic = image
//image:didFinish.. if we arent able to save, pass to contextInfo in Error Handling
if (newMedia == true){
UIImageWriteToSavedPhotosAlbum(image, self, "image:didFinishSavingWithError:contextInfo:", nil)
}
}
}
NewRecord.swift
var imageArray:[UIImage] = [UIImage]()
viewDidLoad(){
//OUR IMAGE ARRAY WHICH HOLDS OUR PHOTOS, CRASHES AROUND 20th PHOTO ADDED
imageArray.append(globalPic)
//Rest of NewRecord.swift is code which adds images from imageArray to be presented on a collection view
}
我自己的应用程序中遇到了低内存问题,这些问题必须与许多高分辨率UIImage对象一起使用。
解决方案是在imageArray中保存图像的缩略图(这会占用更少的内存),然后显示这些缩略图。如果用户确实需要查看全分辨率图像,则可以允许他们在图像上单击,然后从相机胶卷重新加载并显示完整尺寸的UIImage。
这里有一些允许您创建缩略图的代码:
// image here is your original image
let size = CGSizeApplyAffineTransform(image.size, CGAffineTransformMakeScale(0.5, 0.5))
let hasAlpha = false
let scale: CGFloat = 0.0 // Automatically use scale factor of main screen
UIGraphicsBeginImageContextWithOptions(size, !hasAlpha, scale)
image.drawInRect(CGRect(origin: CGPointZero, size: size))
let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
imageArray.append(scaledImage)
和more information about these techniques can be found in this NSHipster article。
斯威夫特4 -
// image here is your original image
let size = image.size.applying(CGAffineTransform(scaleX: 0.5, y: 0.5))
let hasAlpha = false
let scale: CGFloat = 0.0 // Automatically use scale factor of main screen
UIGraphicsBeginImageContextWithOptions(size, !hasAlpha, scale)
image.draw(in: CGRect(origin: .zero, size: size))
let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
最佳做法是保持imageArray短。该阵列应仅用于缓存当前滚动范围内的图像(以及即将显示的图像以获得更好的用户体验)。您应该将其余部分保留在CoreData中并在滚动期间动态加载它们。否则,即使使用缩略图,应用程序也最终会崩溃。
让我从简单的答案开始:你不应该实现成千上万人自己经历的东西。有一些很好的库可以通过实现磁盘缓存,内存缓存,缓冲区来解决这个问题。基本上你需要的一切,等等。
我可以向您推荐的两个库如下:
它们都很棒,所以它确实是偏好的问题(我更喜欢Haneke),但它们允许您在不同的线程上下载图像,无论是来自Web还是来自您的捆绑,或者来自文件系统。它们还具有UIImageView的扩展,允许您使用单行功能轻松加载所有图像,当您加载这些图像时,它们关心加载。
高速缓存
对于您的特定问题,您可以使用使用这些方法来处理问题的缓存,如下所示(来自文档):
[[SDImageCache sharedImageCache] storeImage:myImage forKey:myCacheKey];
现在,当您在此缓存中拥有它时,您可以轻松地检索它
SDImageCache *imageCache = [[SDImageCache alloc] initWithNamespace:@"myNamespace"];
[imageCache queryDiskCacheForKey:myCacheKey done:^(UIImage *image) {
// image is not nil if image was found
}];
所有内存处理和平衡都由库本身完成,因此您不必担心任何事情。您可以选择将其与调整大小方法结合使用,以存储较小的图像(如果这些图像很大),但这取决于您。
希望能帮助到你!
当您从视图控制器收到内存警告时,您可以从阵列中删除未显示的照片并将其另存为文件,然后在需要时再次加载等等。或者只是检测它们何时与collectionView:didEndDisplayingCell:forItemAtIndexPath
消失
将它们保存在这样的数组中:
var cachedImages = [(section: Int, row: Int, imagePath: String)]()
使用:
func saveImage(indexPath: NSIndexPath, image: UIImage) {
let imageData = UIImagePNGRepresentation(image)
let documents = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
let imagePath = (documents as NSString).stringByAppendingPathComponent("(indexPath.section)-(indexPath.row)-cached.png")
if (imageData?.writeToFile(imagePath, atomically: true) == true) {
print("saved!")
cachedImages.append((indexPath.section, indexPath.row, imagePath))
}
else {
print("not saved!")
}
}
让他们回来:
func getImage(indexPath indexPath: NSIndexPath) -> UIImage? {
let filteredCachedImages = cachedImages.filter({ $0.section == indexPath.section && $0.row == indexPath.row })
if filteredCachedImages.count > 0 {
let firstItem = filteredCachedImages[0]
return UIImage(contentsOfFile: firstItem.imagePath)!
}
else {
return nil
}
}
也使用像this answer这样的东西,以避免阻塞主线程
我举了一个例子:find it here
使用以下代码在存储时缩小图像的大小:
var newImage : UIImage
var size = CGSizeMake(400, 300)
UIGraphicsBeginImageContext(size)
image.drawInRect(CGRectMake(0,0,400,300))
newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
我建议优化你的代码,而不是创建一个照片数组,只需创建一个URL的数组(ios版本<8.1来自AssetLibrary)/ localIdentifier(版本> 8.1照片库),并仅在需要时通过这些URL获取图像。即在显示时。
有时,如果将图像存储在数组中,ARC无法正确处理内存管理,并且在许多地方也会导致内存泄漏。
您可以使用autoreleasepool删除ARC无法释放的不必要的引用。
要进一步添加,如果您通过相机捕获任何图像,那么存储在数组中的大小远远大于图像的大小(虽然我不知道为什么!)。
您可以将原始图像数据存储在一个数组中,而不是所有元数据和多余的东西。我不知道你是否需要元数据,但你可以在没有元数据的情况下解决问题。另一种方法是将每个图像写入临时文件,然后再检索它。
对我有用的最佳路线是以全尺寸存储一组图像是使用phphotoLibrary。 PHLibrary附带缓存和垃圾收集。其他解决方案不适用于我的目的。
ViewDidLoad:
//Check if the folder exists, if not, create it
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "title = %@", albumName)
let collection:PHFetchResult = PHAssetCollection.fetchAssetCollectionsWithType(.Album, subtype: .Any, options: fetchOptions)
if let first_Obj:AnyObject = collection.firstObject{
//found the album
self.albumFound = true
self.assetCollection = first_Obj as! PHAssetCollection
}else{
//Album placeholder for the asset collection, used to reference collection in completion handler
var albumPlaceholder:PHObjectPlaceholder!
//create the folder
NSLog("
Folder "%@" does not exist
Creating now...", albumName)
PHPhotoLibrary.sharedPhotoLibrary().performChanges({
let request = PHAssetCollectionChangeRequest.creationRequestForAssetCollectionWithTitle(albumName)
albumPlaceholder = request.placeholderForCreatedAssetCollection
},
completionHandler: {(success:Bool, error:NSError!)in
if(success){
println("Successfully created folder")
self.albumFound = true
if let collection = PHAssetCollection.fetchAssetCollectionsWithLocalIdentifiers([albumPlaceholder.localIdentifier], options: nil){
self.assetCollection = collection.firstObject as! PHAssetCollection
}
}else{
println("Error creating folder")
self.albumFound = false
}
})
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject]) {
//what happens after the picture is chosen
let mediaType = info[UIImagePickerControllerMediaType] as! NSString
if mediaType.isEqualToString(kUTTypeImage as NSString as String){
let image = info[UIImagePickerControllerOriginalImage] as! UIImage
appraisalPic.image = image
globalPic = appraisalPic.image!
if(newMedia == true){
UIImageWriteToSavedPhotosAlbum(image, self, "image:didFinishSavingWithError:contextInfo:", nil)
self.dismissViewControllerAnimated(true, completion: nil)
picTaken = true
println(photosAsset)
}
}
}
以上是关于使用大型UIImage数组时的内存问题导致崩溃(swift)的主要内容,如果未能解决你的问题,请参考以下文章