2015-09-04 8 views
4

Я просматриваю онлайн-курс Стэнфорда CS193P, делающий ios dev. Лекция 9 относится к UIScrollView/делегирование через простой URL-адрес UIImage fetch app. Указанное приложение отлично работает в тренажере, но запускает затем падает на живом устройстве (iPhone5) после попытки принести в IMG со следующим:CS193P Код Cassini запускается на тренажере, но падает на устройство? «Сообщение от отладчика: завершено из-за ошибки памяти»

Сообщение от отладчика: Отменено из-за ошибки памяти

Я пошел вернуться в мой код, перечитать о делегировании, обыскать SO (я нашел подобный поток, я убедился, что моя схема проекта не активирована зомби). Я обновил свое устройство, свой компилятор/os и немного пошутил о том, что может помешать этому запуску на устройстве ... Пример класса можно скачать из Стэнфорда по адресу https://web.stanford.edu/class/cs193p/cgi-bin/drupal/system/files/sample_code/Cassini.zip, но этот код ведет себя одинаково! Это было первоначально написано для ios 8.1, и мы на 8.4, есть ли какие-либо известные проблемы?

код контроллера ImageView:

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     // required for zooming 
      scrollView.minimumZoomScale = 0.03   // required for zooming 
      scrollView.maximumZoomScale = 1.0    // required for zooming 
     } 
    } 

    // UIScrollViewDelegate method 
    // required 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() 
     } 
    } 
} 

ответ

2

Источник вопроса, что декомпрессия изображение из данных (файла формата представителя данных изображения) на экран может «съесть» много памяти. Вот очень хорошая статья о IOS изображения декомпрессия ->Avoiding Image Decompression Sickness

Поскольку все изображения в приложении Кассини ОЧЕНЬ большой (wave_earth_mosaic_3.jpg (9999 × 9999), pia03883-full.jpg (14400 × 9600)) процесс декомпрессии изображения «есть» всю память телефона. Это приводит к сбою приложения.

Чтобы исправить Кассини вопрос я изменил код и добавил небольшую функцию, чтобы понизить разрешение изображения на 2.

Вот пример кода (код фиксируется на 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 
} 

Так что теперь загрузки приложения все изображения без сбоев.

СВИФТ 2,0 фикс:

добавить это info.plist разрешить HTTP ЗАГРУЗКА

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

это приложение позволяет работать на устройстве, спасибо большое за ссылку на страницу объяснения - но это кажется временным решением, что, если бы я хотел получить изображение с полным разрешением? это не выполнимо? – John

+0

Вы правы. Вам нравится просто не «хотеть» показывать такие большие изображения на устройстве. – CTiPKA

+0

@John, если вы хотите узнать больше об отображении огромных изображений, это [видео WWDC] (https://developer.apple.com/videos/play/wwdc2011-104/) должно помочь вам (начиная с ~ 43: 40, они идут об этом подробно, но весь сеанс потрясающий) - и в целом [WWDC] (https://developer.apple.com/videos/) - отличный источник знаний. –