2015-05-01 2 views
6

Я использую GPUImage и многие экземпляры GPUImageView. Цель состоит в том, чтобы отобразить исходное изображение, наложить несколько фрагментов отфильтрованных изображений сверху и, наконец, оживить фильтры срезов медленно по исходному изображению. Представьте себе изображение, в котором некоторые полосы сепии перекатываются, чтобы показать нормальное изображение и изображение сепии в сечениях.GPUImage и GPUImageView: приложение завершено из-за ошибки памяти

я обернуты эту функцию в подклассе UIView, как показано ниже:

import Foundation 
import QuartzCore 

class FilteredImageMaskView : UIView { 

init(frame: CGRect, image: UIImage){ 
    super.init(frame: frame); 

    let imageViewFrame = CGRectMake(frame.origin.x, 0.0, frame.size.width, frame.size.height); 

    let origImage = GPUImagePicture(image: image); 
    origImage.forceProcessingAtSizeRespectingAspectRatio(imageViewFrame.size); 

    // Display the original image without a filter 
    let imageView = GPUImageView(frame: imageViewFrame); 
    origImage.addTarget(imageView); 
    origImage.processImageWithCompletionHandler(){ 
     origImage.removeAllTargets(); 

     var contentMode = UIViewContentMode.ScaleAspectFit; 
     imageView.contentMode = contentMode; 

     // Width of the unfiltered region 
     let regularWidth: CGFloat = 30.0; 
     // Width of filtered region 
     let filterWidth: CGFloat = 30.0; 

     // How much we are moving each bar 
     let totalXMovement = (regularWidth + filterWidth) * 2; 

     // The start X position 
     var currentXForFilter: CGFloat = -totalXMovement; 

     // The filter being applied to an image 
     let filter = GPUImageSepiaFilter(); 
     filter.intensity = 0.5; 
     // Add the filter to the originalImage 
     origImage.addTarget(filter); 

     let filteredViewCollection = FilteredViewCollection(filteredViews: [GPUImageView]()); 

     // Iterate over the X positions until the whole image is covered 
     while(currentXForFilter < imageView.frame.width + totalXMovement){ 
      let frame = CGRectMake(currentXForFilter, imageViewFrame.origin.y, imageViewFrame.width, imageViewFrame.height); 
      var filteredView = GPUImageView(frame: frame); 
      filteredView.clipsToBounds = true; 
      filteredView.layer.contentsGravity = kCAGravityTopLeft; 

      // This is the slice of the overall image that we are going to display as filtered 
      filteredView.layer.contentsRect = CGRectMake(currentXForFilter/imageViewFrame.width, 0.0, filterWidth/imageViewFrame.width, 1.0); 
      filteredView.fillMode = kGPUImageFillModePreserveAspectRatio; 

      filter.addTarget(filteredView); 

      // Add the filteredView to the super view 
      self.addSubview(filteredView); 

      // Add the filteredView to the collection so we can animate it later 
      filteredViewCollection.filteredViews.append(filteredView); 

      // Increment the X position   
      currentXForFilter += regularWidth + filterWidth; 
     } 

     origImage.processImageWithCompletionHandler(){ 
      filter.removeAllTargets(); 

      // Move to the UI thread 
      ThreadUtility.runOnMainThread(){ 
       // Add the unfiltered image 
       self.addSubview(imageView); 
       // And move it behind the filtered slices 
       self.sendSubviewToBack(imageView); 

       // Animate the slices slowly across the image 
       UIView.animateWithDuration(20.0, delay: 0.0, options: UIViewAnimationOptions.Repeat, animations: { [weak filteredViewCollection] in 
        if let strongfilteredViewCollection = filteredViewCollection { 
         if(strongfilteredViewCollection.filteredViews != nil){ 
          for(var i = 0; i < strongfilteredViewCollection.filteredViews.count; i++){ 
           strongfilteredViewCollection.filteredViews[i].frame.origin.x += totalXMovement; 
           strongfilteredViewCollection.filteredViews[i].layer.contentsRect.origin.x += (totalXMovement/imageView.frame.width); 
          } 
         } 
        } 
       }, completion: nil); 
      } 
     } 
    } 
} 

required init(coder aDecoder: NSCoder) { 
    super.init(coder: aDecoder); 
} 

} 

class FilteredViewCollection { 
    var filteredViews: [GPUImageView]! = [GPUImageView](); 

    init(filteredViews: [GPUImageView]!){ 
     self.filteredViews = filteredViews; 
    } 
} 

Экземпляр FilteredImageMaskView добавляется программно к представлению в ViewController. Когда этот viewController отклоняется, предполагается, что ресурсы будут утилизированы - я старался избегать циклов сохранения. Когда я наблюдаю за потреблением памяти в отладчике на реальном устройстве, память отключается соответствующим образом при отключении viewController. Однако, если я повторно загружаю этот viewController, чтобы посмотреть изображение, а затем отпустите его, а затем перезагрузите его снова, я в конечном итоге столкнусь с «Приложением прекращено из-за ошибки памяти»

Если я подожду некоторое время после отклонения viewController, ошибки памяти кажутся менее частыми, что заставляет меня думать, что память все еще освобождается после того, как viewController уволен ...? Но я также видел ошибку после нескольких раз не так быстрого открытия и закрытия viewController.

Я должен использовать GPUImage и/или GPUImageView неэффективно, и я ищу руководство.

Спасибо!

EDIT: См. Ниже для реализации контроллера контроллера.

import UIKit 

class ViewImageViewController: UIViewController, FetchImageDelegate { 

    var imageManager = ImageManager(); 

    @IBOutlet var mainView: UIView! 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     imageManager.fetchImageAsync(delegate: self); 
    } 

    // This callback is dispatched on the UI thread 
    func imageFetchCompleted(imageData: [UInt8]) { 
     let imageView = FilteredImageMaskView(frame: self.mainView.frame, image: UIImage(data: imageData)); 
     mainView.addSubview(imageView); 

     var timer = NSTimer.scheduledTimerWithTimeInterval(NSTimeInterval(10.0), target: self, selector: Selector("displayReminder"), userInfo: nil, repeats: false); 
    } 

    func displayReminder(){ 
     // Show an alert or message here 
    } 

} 

class ImageManager { 

    func fetchImageAsync(delegate: FetchImageDelegate) { 
     // This dispatches a high priority background thread 
     ThreadUtility.runOnHighPriorityBackgroundThread() { [weak delegate] in 
      // Get the image (This part could take a while in the real implementation) 
      var imageData = [UInt8](); 

      // Move to the UI thread 
      ThreadUtility.runOnMainThread({ 
       if let strongDelegate = delegate { 
        strongDelegate.imageFetchCompleted(imageData); 
       } 
      }); 
     } 
    } 
} 

Теперь, когда я просматривал эту усеченную версию, делает прохождение self к ImageManager создать цикл сохранить хотя я ссылаться на него weak ют в фоновом потоке? Могу ли я передать это как слабое право справки с ViewImageViewController? Конечно, возможно, что ViewImageViewController уволен до завершения метода fetchImageAsync и вызывается обратный вызов.

EDIT: Я думаю, что нашел проблему. Если вы посмотрите на ViewImageViewController в обратном вызове, я создаю NSTimer и передаю self. Мое подозрение заключается в том, что создается цикл сохранения, если viewController отклоняется до выполнения таймера. Это объясняет, почему, если я жду несколько дополнительных секунд, я не получаю ошибку памяти - потому что таймер срабатывает, и viewController правильно распределяется. Вот исправление (я думаю).

// This is on the ViewImageViewController 
var timer: NSTimer!; 

// Then instead of creating a new variable, assign the timer to the class variable 
self.timer = NSTimer.scheduledTimerWithTimeInterval(NSTimeInterval(10.0), target: self, selector: Selector("displayReminder"), userInfo: nil, repeats: false); 

// And finally, on dismiss of the viewcontroller (viewWillDisappear or back button click event, or both) 
func cancelTimer() { 
    if(self.timer != nil){ 
     self.timer.invalidate(); 
     self.timer = nil; 
    } 
} 
+0

Можете ли вы дать больше информации о контроллере? Действительно ли оно освобождается?Если у вас есть утечка памяти, проблема будет не в коде, который вы вообще показывали. – matt

+0

Конечно. Я обновлю OP. – a432511

+0

@matt добавлен по вашему запросу ... и вы можете быть правы ... см. Выше – a432511

ответ

2

Думаю, я нашел проблему. Если вы посмотрите на ViewImageViewController в обратном вызове, я создаю NSTimer и передаю self. Мое подозрение заключается в том, что создается цикл сохранения, если viewController отклоняется до выполнения таймера. Это объясняет, почему, если я жду несколько дополнительных секунд, я не получаю ошибку памяти - потому что таймер срабатывает, и viewController правильно распределяется. Вот исправление (я думаю).

// This is on the ViewImageViewController 
var timer: NSTimer!; 

// Then instead of creating a new variable, assign the timer to the class variable 
self.timer = NSTimer.scheduledTimerWithTimeInterval(NSTimeInterval(10.0), target: self, selector: Selector("displayReminder"), userInfo: nil, repeats: false); 

// And finally, on dismiss of the viewcontroller (viewWillDisappear or back button click event, or both) 
func cancelTimer() { 
    if(self.timer != nil){ 
     self.timer.invalidate(); 
     self.timer = nil; 
    } 
} 
+1

Очень возможно. См. Мое обсуждение таймеров и циклов сохранения - начинается около 2/3 пути через этот раздел: http://www.apeth.com/iOSBook/ch12.html#_unusual_memory_management_situations Как я цитирую документы: «Целевой объект сохраняется по таймеру и отпускается, когда таймер недействителен ». Мой пример - повторяющийся таймер; пример одиночного таймера с длинным интервалом, который никогда не получает возможности стрелять, мне не приходил! Я сделаю записку для себя, чтобы включить ее в любые будущие издания книги. – matt

+0

@matt действительно кажется, что это была проблема. Теперь все правильно работает с момента внедрения вышеописанного кода. Иногда он принимает тонкую зубчатую гребенку и некоторое терпение, чтобы найти эти надоедливые удержания: -D – a432511

+0

Ну, это требует поиска в нужном месте. Вы искали не то место; ваш материал GPUImage был полной красной селедкой, и я был тем, кто сказал вам об этом и заставил вас смотреть в нужное место. Но я сказал вам, что бесплатно, поэтому в конце концов вы выбросили 100 очков своего собственного представителя, не имеющим никакой цели! – matt

0

The FilteredImageMaskView сильно ссылки в блоке processImageWithCompletionHandler, который, вероятно, образуют цикл удержания. Попробуйте использовать слабое я, а не в блоке

+0

Я сделаю это! – a432511

+0

Это не сработало. Благодарю за то, что вы дали ему шанс. – a432511

Смежные вопросы