在我的应用程序中,我有一个图像阵列,可以保存我相机拍摄的所有图像。我正在使用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
}
答案 0 :(得分:6)
我自己的应用程序中遇到了低内存问题,这些问题必须与许多高分辨率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。
Swift 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()
答案 1 :(得分:4)
最佳做法是保持imageArray短。该阵列应仅用于缓存当前滚动范围内的图像(以及即将显示的图像以获得更好的用户体验)。您应该将其余部分保留在CoreData中并在滚动期间动态加载它们。否则,即使使用缩略图,该应用最终也会崩溃。
答案 2 :(得分:2)
让我从简单的答案开始:你不应该实现成千上万人自己经历的东西。有一些很好的库可以通过实现磁盘缓存,内存缓存,缓冲区来解决这个问题。基本上你需要的一切,等等。
我可以推荐给您的两个库:
它们都很棒,所以它确实是优先考虑的问题(我更喜欢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
}];
所有内存处理和平衡都是由库本身完成的,因此您不必担心任何事情。您可以选择将其与调整大小方法结合使用,以存储较小的图像(如果这些图像很大),但这取决于您。
希望它有所帮助!
答案 3 :(得分:1)
当您从视图控制器收到内存警告时,您可以从阵列中删除未显示的照片并将其另存为文件,然后在需要时再次加载等等。或者只是通过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
答案 4 :(得分:1)
使用以下代码在存储图像时缩小图像的大小:
def foo(string : str) -> str:
if foo.__annotations__.get('string')==type(string):
print('continue...')
我建议优化您的代码,而不是创建一个照片数组,只需创建一个URL的数组(ios版本&lt; 8.1 from AssetLibrary)/ localIdentifier(版本&gt; 8.1照片库),并仅在需要时获取图像这些网址。即显示时。
如果将图像存储在数组中,ARC有时无法正确处理内存管理,并且在许多地方也会导致内存泄漏。
您可以使用 autoreleasepool 删除ARC无法发布的不必要的引用。
要进一步添加,如果您通过相机捕获任何图像,那么存储在数组中的大小远远大于图像的大小(虽然我不知道为什么!)。
答案 5 :(得分:0)
您可以将原始图像数据存储在一个数组中,而不是存储所有元数据和多余内容。我不知道你是否需要元数据,但你可以在没有元数据的情况下解决问题。另一种方法是将每个图像写入临时文件,然后再检索它。
答案 6 :(得分:-2)
对我有用的最佳路线是以全尺寸存储一组图像是使用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("\nFolder \"%@\" does not exist\nCreating 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)
}
}
}