我正在使用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,并且正在寻找指导。
谢谢!
编辑:请参阅下面的视图控制器实现。
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
方法完成并调用回调之前将其关闭。
编辑:我想我找到了问题。如果您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;
}
}
我想我找到了问题。如果您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;
}
}
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句