CS193P El código de Cassini se ejecuta en el simulador pero falla en el dispositivo? "Mensaje del depurador: finalizado debido a un error de memory"

Estoy pasando por el curso en línea CS193P de Stanford haciendo ios dev. La conferencia 9 trata con UIScrollView / delegación a través de una aplicación de búsqueda simple UIImage url. Dicha aplicación funciona perfectamente en el simulador, pero luego se inicia y se cuelga en el dispositivo en vivo (iPhone5) después de intentar get un img con lo siguiente:

Mensaje del depurador: finalizado debido a un error de memory

Volví a mi código, volví a leer sobre la delegación, busqué SO (encontré un hilo similar, me aseguré de que mi esquema de proyecto NO tenga zombies habilitados). Actualicé mi dispositivo, mi comstackdor / os, y estoy algo avergonzado de lo que podría impedir que esto se ejecute en el dispositivo … El ejemplo de la class se puede download de Stanford en https://web.stanford.edu/class/cs193p /cgi-bin/drupal/system/files/sample_code/Cassini.zip pero este código se comporta de la misma manera. Esto fue originalmente escrito para ios 8.1 y estamos en 8.4, ¿hay algún problema conocido?

código para el controller de image:

 import UIKit class ImageViewController: UIViewController, UIScrollViewDelegate { // our Model // publicly settable // when it changes (but only if we are on screen) // we'll fetch the image from the imageURL // if we're off screen when this happens (view.window == nil) // viewWillAppear will get it for us later var imageURL: NSURL? { didSet { image = nil if view.window != nil { fetchImage() } } } // fetches the image at imageURL // does so off the main thread // then puts a closure back on the main queue // to handle putting the image in the UI // (since we aren't allowed to do UI anywhere but main queue) private func fetchImage() { if let url = imageURL { spinner?.startAnimating() let qos = Int(QOS_CLASS_USER_INITIATED.value) dispatch_async(dispatch_get_global_queue(qos, 0)) { () -> Void in let imageData = NSData(contentsOfURL: url) // this blocks the thread it is on dispatch_async(dispatch_get_main_queue()) { // only do something with this image // if the url we fetched is the current imageURL we want // (that might have changed while we were off fetching this one) if url == self.imageURL { // the variable "url" is capture from above if imageData != nil { // this might be a waste of time if our MVC is out of action now // which it might be if someone hit the Back button // or otherwise removed us from split view or navigation controller // while we were off fetching the image self.image = UIImage(data: imageData!) } else { self.image = nil } } } } } } @IBOutlet private weak var spinner: UIActivityIndicatorView! @IBOutlet private weak var scrollView: UIScrollView! { didSet { scrollView.contentSize = imageView.frame.size // critical to set this! scrollView.delegate = self // requinetworking for zooming scrollView.minimumZoomScale = 0.03 // requinetworking for zooming scrollView.maximumZoomScale = 1.0 // requinetworking for zooming } } // UIScrollViewDelegate method // requinetworking for zooming func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? { return imageView } private var imageView = UIImageView() // convenience computed property // lets us get involved every time we set an image in imageView // we can do things like resize the imageView, // set the scroll view's contentSize, // and stop the spinner private var image: UIImage? { get { return imageView.image } set { imageView.image = newValue imageView.sizeToFit() scrollView?.contentSize = imageView.frame.size spinner?.stopAnimating() } } // put our imageView into the view hierarchy // as a subview of the scrollView // (will install it into the content area of the scroll view) override func viewDidLoad() { super.viewDidLoad() scrollView.addSubview(imageView) } // for efficiency, we will only actually fetch the image // when we know we are going to be on screen override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) if image == nil { fetchImage() } } } 

El origen del problema que la image de descompression de datos (formatting de file representativo de datos de image) a la pantalla puede "comer" una gran cantidad de memory. Aquí hay un muy buen artículo sobre la descompression de imágenes de iOS -> Evitar la enfermedad de descompression de imágenes

Dado que todas las imágenes en la aplicación Cassini son MUY grandes (wave_earth_mosaic_3.jpg (9999 × 9999), pia03883-full.jpg (14400 × 9600)), el process de descompression de imágenes 'come' toda la memory del teléfono. Esto provoca un locking de la aplicación.

Para solucionar el problema de Cassini, modifiqué el código y agregué una function pequeña para networkingucir la resolución de las imágenes en 2.

Aquí hay un ejemplo de código (código fijo a Swift 2.0):

  ... if imageData != nil { // this might be a waste of time if our MVC is out of action now // which it might be if someone hit the Back button // or otherwise removed us from split view or navigation controller // while we were off fetching the image if let imageSource = UIImage(data: imageData!) { self.image = self.imageResize(imageSource) } } else { self.image = nil } ... func imageResize (imageOriginal:UIImage) -> UIImage { let image = imageOriginal.CGImage let width = CGImageGetWidth(image) / 2 let height = CGImageGetHeight(image) / 2 let bitsPerComponent = CGImageGetBitsPerComponent(image) let bytesPerRow = CGImageGetBytesPerRow(image) let colorSpace = CGImageGetColorSpace(image) let bitmapInfo = CGImageGetBitmapInfo(image) let context = CGBitmapContextCreate(nil, width, height, bitsPerComponent, bytesPerRow, colorSpace, bitmapInfo.rawValue) CGContextSetInterpolationQuality(context, CGInterpolationQuality.High) CGContextDrawImage(context, CGRect(origin: CGPointZero, size: CGSize(width: CGFloat(width), height: CGFloat(height))), image) let scaledImage = UIImage(CGImage: CGBitmapContextCreateImage(context)!) return scaledImage } 

Entonces, ahora la aplicación carga todas las imágenes sin falla.

SWIFT 2.0 solución:

Agregue esto a Info.plist para permitir la carga de HTTP.

 <key>NSAppTransportSecurity</key> <dict> <!--Include to allow all connections (DANGER)--> <key>NSAllowsArbitraryLoads</key> <true/> </dict>