2015-05-01 18 views
6

Tôi đang sử dụng GPUImage và nhiều phiên bản GPUImageView. Mục đích là để hiển thị hình ảnh gốc, lớp một vài lát của hình ảnh được lọc trên đầu trang, và cuối cùng animate các bộ lọc slice từ từ trên hình ảnh ban đầu. Hãy tưởng tượng một hình ảnh với một số thanh Sepia lăn qua để hiển thị hình ảnh bình thường và hình ảnh màu nâu đỏ trong các phần.GPUImage và GPUImageView: Ứng dụng bị chấm dứt do lỗi bộ nhớ

tôi quấn chức năng này trong một lớp con của UIView như bên dưới:

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; 
    } 
} 

Một thể hiện của FilteredImageMaskView được thêm theo chương trình vào một cái nhìn trong một viewController. Khi viewController đó được loại bỏ, giả định là các tài nguyên sẽ được xử lý - tôi đã cẩn thận để tránh giữ lại chu kỳ. Khi tôi xem mức tiêu thụ bộ nhớ trong trình gỡ rối trên thiết bị thực, bộ nhớ sẽ giảm một cách thích hợp khi viewController bị loại bỏ. Tuy nhiên, nếu tôi liên tục tải viewController đó để xem hình ảnh, sau đó loại bỏ nó, sau đó tải lại nó, tôi cuối cùng sẽ gặp phải "Ứng dụng bị chấm dứt do lỗi bộ nhớ"

Nếu tôi đợi một lát sau khi loại bỏ viewController, lỗi bộ nhớ có vẻ ít thường xuyên hơn dẫn tôi tin rằng bộ nhớ vẫn đang được phát hành sau khi viewController được miễn nhiệm ...? Nhưng tôi cũng đã nhìn thấy lỗi chỉ sau một vài lần không mở và đóng cửa nhanh chóng của viewController.

Tôi phải đang sử dụng GPUImage và/hoặc GPUImageView không hiệu quả và tôi đang tìm kiếm hướng dẫn.

Cảm ơn!

EDIT: Xem bên dưới để thực hiện bộ điều khiển chế độ xem.

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); 
       } 
      }); 
     } 
    } 
} 

Bây giờ tôi đang tìm kiếm thông qua phiên bản rút gọn này, không đi qua self đến ImageManager tạo ra một chu kỳ giữ lại mặc dù tôi tham khảo nó weak ly để thread nền? Tôi có thể chuyển nó như là một tham chiếu yếu ngay từ ViewImageViewController không? Chắc chắn rằng ViewImageViewController bị loại bỏ trước khi phương thức fetchImageAsync hoàn tất và gọi lại được gọi.

EDIT: Tôi nghĩ rằng tôi đã tìm thấy sự cố. Nếu bạn nhìn vào số ViewImageViewController trong hàm gọi lại, tôi tạo một NSTimer và tự chuyển. Nghi ngờ của tôi là tạo ra một chu kỳ giữ lại nếu viewController được loại bỏ trước khi bộ đếm thời gian thực hiện. Điều đó sẽ giải thích tại sao nếu tôi đợi thêm vài giây nữa, tôi không nhận được lỗi bộ nhớ - bởi vì bộ đếm thời gian đã kích hoạt và viewController được xử lý đúng cách. Đây là bản sửa lỗi (tôi nghĩ).

// 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

Bạn có thể cung cấp thêm thông tin về bộ điều khiển chế độ xem không? Có thực tế là đang được deallocated?Nếu bạn bị rò rỉ bộ nhớ, vấn đề sẽ không phải là mã mà bạn đã hiển thị. – matt

+0

Chắc chắn. Tôi sẽ cập nhật OP. – a432511

+0

@matt được thêm vào theo yêu cầu của bạn ... và bạn có thể đúng ... xem ở trên – a432511

Trả lời

2

Tôi nghĩ rằng tôi đã tìm thấy sự cố. Nếu bạn nhìn vào số ViewImageViewController trong hàm gọi lại, tôi tạo một NSTimer và tự chuyển. Nghi ngờ của tôi là tạo ra một chu kỳ giữ lại nếu viewController được loại bỏ trước khi bộ đếm thời gian thực hiện. Điều đó sẽ giải thích tại sao nếu tôi đợi thêm vài giây nữa, tôi không nhận được lỗi bộ nhớ - bởi vì bộ đếm thời gian đã kích hoạt và viewController được xử lý đúng cách. Đây là bản sửa lỗi (tôi nghĩ).

// 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

Rất có thể. Xem thảo luận của tôi về bộ đếm thời gian và giữ lại chu kỳ - bắt đầu khoảng 2/3 đường đi qua phần này: http://www.apeth.com/iOSBook/ch12.html#_unusual_memory_management_situations Khi tôi trích dẫn tài liệu, "Đối tượng mục tiêu được giữ lại bởi bộ hẹn giờ và được giải phóng khi bộ hẹn giờ bị vô hiệu. " Ví dụ của tôi là một bộ đếm thời gian lặp lại; ví dụ về một bộ đếm thời gian dài một lần mà không bao giờ có cơ hội cháy đã không xảy ra với tôi! Tôi sẽ tự ghi chú để bao gồm điều đó trong bất kỳ ấn bản nào trong tương lai của cuốn sách. – matt

+0

@matt thực sự nó xuất hiện đây là vấn đề. Bây giờ mọi thứ đã được xử lý đúng cách kể từ khi triển khai mã ở trên. Đôi khi nó có một chiếc lược răng tốt và một số kiên nhẫn để tìm những pesky giữ lại: -D – a432511

+0

Vâng, nó sẽ tìm kiếm ở đúng nơi. Bạn đang tìm kiếm ở chỗ sai; công cụ GPUImage của bạn là tổng số cá trích đỏ, và tôi là người đã nói với bạn như vậy và giúp bạn tìm đúng nơi. Nhưng tôi đã nói với bạn rằng miễn phí, do đó, cuối cùng bạn đã ném đi 100 điểm của đại diện của riêng bạn, không có mục đích! – matt

0

Bộ lọcImageMaskView được tham chiếu mạnh mẽ trong khối processImageWithCompletionHandler, có khả năng tạo thành chu trình lưu giữ. Cố gắng sử dụng bản thân yếu thay vì trong khối

+0

Tôi sẽ chụp ảnh! – a432511

+0

Điều này không hoạt động. Cảm ơn bạn đã cho nó một shot mặc dù. – a432511

Các vấn đề liên quan