将更改的图像保存到相机胶卷时因内存问题而终止
Posted
技术标签:
【中文标题】将更改的图像保存到相机胶卷时因内存问题而终止【英文标题】:Terminated due to memory issue when saving altered image to camera roll 【发布时间】:2016-12-09 02:06:01 【问题描述】:我正在构建一个允许用户为照片添加时间戳的应用。选择来自摄像机卷的照片我的应用有时崩溃,Xcode显示错误:“来自调试器的消息:由于内存问题而终止”。如果我尝试将时间戳添加到非常大的图像,或者如果我在同一图像上重复添加时间戳,我发现它会崩溃。
以下是我认为与该问题相关的代码。另外,here 是整个项目的链接。
这就是将时间戳添加到图像的方式。我把 cmets 放在它崩溃的地方。它并不总是在同一行代码上崩溃。我无法弄清楚为什么我的代码会因为内存问题错误而崩溃。
func textToImage(drawText text: NSString, inImage image: UIImage, atPoint point: CGPoint) -> UIImage
let color = UserDefaults.standard.colorForKey(key: "color")!
let font = UserDefaults.standard.value(forKey: "font") as! String
let size = UserDefaults.standard.value(forKey: "size") as! Int
let fontAndSize = UIFont(name: font, size: CGFloat(size))!
let location = Locations(rawValue: UserDefaults.standard.value(forKey: "location") as! String)!
let scale = UIScreen.main.scale
UIGraphicsBeginImageContextWithOptions(image.size, false, scale)
// sometimes terminates here
let textFontAttributes = [
NSFontAttributeName: fontAndSize,
NSForegroundColorAttributeName: color
] as [String : Any]
image.draw(in: CGRect(origin: CGPoint.zero, size: image.size))
let rectSize = text.boundingRect(with: CGSize(width:CGFloat(MAXFLOAT), height: CGFloat(MAXFLOAT)), options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: textFontAttributes, context: nil).size
// sometimes terminates here
if location == .topRight || location == .bottomRight || location == .center || location == .topCenter || location == .bottomCenter
// Calculate the text size
if location == .center || location == .topCenter || location == .bottomCenter
// Subtract half the text width from the x origin
let rect = CGRect(x:point.x-(rectSize.width / 2), y:point.y-(rectSize.height / 2), width:rectSize.width, height: rectSize.height)
text.draw(in: rect, withAttributes: textFontAttributes)
else
// Subtract the text width from the x origin
let rect = CGRect(x:point.x-rectSize.width, y:point.y-(rectSize.height / 2), width:rectSize.width, height: rectSize.height)
text.draw(in: rect, withAttributes: textFontAttributes)
else
let rect = CGRect(x:point.x, y:point.y-(rectSize.height / 2), width:rectSize.width, height: rectSize.height)
text.draw(in: rect, withAttributes: textFontAttributes)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
func selectPhotoFromCameraRoll(mediaType: String)
imagePicker.sourceType = .photoLibrary
if mediaType == "Video"
imagePicker.mediaTypes = [kUTTypeMovie as String]
newMedia = false
present(imagePicker, animated: true, completion: nil)
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
if let mediaType = info[UIImagePickerControllerMediaType] as? String
if mediaType.isEqual((kUTTypeImage as String))
if let pickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage
let width = pickedImage.size.width
let height = pickedImage.size.height
let location = Locations(rawValue: UserDefaults.standard.value(forKey: "location") as! String)!
var point = CGPoint(x: 0, y: 0)
switch location
case .topLeft:
point = CGPoint(x: 30, y: 50)
case .topRight:
point = CGPoint(x: width - 30, y: 50)
case .bottomLeft:
point = CGPoint(x: 30, y: height - 50)
case .bottomRight:
point = CGPoint(x: width - 30, y: height - 50)
case .center:
point = CGPoint(x: width / 2, y: height / 2)
case .topCenter:
point = CGPoint(x: width / 2, y: 50)
case .bottomCenter:
point = CGPoint(x: width / 2, y: height - 50)
let savedFormat = UserDefaults.standard.value(forKey: "format") as! String
var date = Date()
if !currentDateBool
date = UserDefaults.standard.value(forKey: "selectedDate") as! Date
let timestampText = getFormattedDateFromFormatType(formatType: savedFormat, date: date) as NSString
let timestampImage = textToImage(drawText: timestampText, inImage: pickedImage, atPoint: point)
if newMedia
UIImageWriteToSavedPhotosAlbum(timestampImage, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)
else
UIImageWriteToSavedPhotosAlbum(timestampImage, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)
else if mediaType.isEqual((kUTTypeMovie as String))
if let videoUrl = info[UIImagePickerControllerMediaURL] as? NSURL
if let videoPath = videoUrl.relativePath
if newMedia
UISaveVideoAtPathToSavedPhotosAlbum(videoPath, nil, nil, nil)
dismiss(animated: true, completion: nil)
func image(_ image: UIImage, didFinishSavingWithError error: NSError?, contextInfo: UnsafeRawPointer)
if let error = error
// we got back an error!
let ac = UIAlertController(title: "Save error", message: error.localizedDescription, preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
present(ac, animated: true)
else
let ac = UIAlertController(title: "Saved!", message: "Your altered image has been saved to your photos.", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
present(ac, animated: true)
【问题讨论】:
【参考方案1】:您将相机胶卷中的照片视为 UIImage。不是。
让我们假设一个最坏的情况。假设我们有一个 12MP 相机。那么你派生的 UIImage 将是 4032x3024 像素。现在您要求制作一个图形上下文为屏幕分辨率缩放。假设此设备的屏幕具有 3 倍分辨率。然后您要求图形上下文 12096x9072,即 109734912。这是 points。要存储颜色数据,您必须将其乘以颜色信息为每个点占用的空间。我不知道那是多少,但我们肯定会再上升一个数量级。无论如何,无论你如何分割它,这都是你要求的大量内存。
【讨论】:
感谢您的回复!您对我应该如何处理图像有什么建议吗? 谢谢。我将研究照片框架,因为我没有太多经验。你能指出我正确的方向并告诉我是否根本不应该使用 UIGraphicsBeginImageContextWithOptions 吗? 抱歉,如果我的问题不清楚。用户的相机胶卷中有一张现有照片。用户想在照片上添加日期/时间文本。我唯一要更改的是在照片中添加文字。我上面的代码打开相机胶卷,让用户选择一张照片,向照片添加文本,然后将该照片的新版本保存到相机胶卷中,其中包括照片上的日期/时间文本。 “用户想在照片上添加日期/时间文本”我不相信。也许用户想看到照片显示上面有日期/时间,但那将是完全不同的事情;您可以使照片变小,而不是变大,以便以规定的日期/时间显示它。正如我所说,您永远不会修改用户的原始照片,即使您这样做了,您也肯定不会为了这样做而将其变大。 我不想把照片放大。我只是想在照片中添加文本,除了添加文本之外根本不更改照片。如果显示日期/时间的照片是我应该做的,那么我会尝试。但是,如果照片是通过电子邮件发送的,它仍然需要保留显示在其上的日期/时间。以上是关于将更改的图像保存到相机胶卷时因内存问题而终止的主要内容,如果未能解决你的问题,请参考以下文章
将 FBO 保存到相机胶卷后清理后崩溃错误? Swift 2.0 选择器语法