Utilizzo una GPUImage e molte istanze GPUImageView. Lo scopo è quello di visualizzare l'immagine originale, sovrapporre più sezioni di immagini filtrate e infine animare lentamente i filtri di sezione sull'immagine originale. Immagina un'immagine con alcune barre seppia che si muovono per mostrare l'immagine normale e l'immagine seppia in sezioni.GPUImage e GPUImageView: l'app è stata chiusa a causa di un errore di memoria
ho avvolto questa funzionalità in una sottoclasse di UIView come mostrato di seguito:
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;
}
}
un'istanza di FilteredImageMaskView
viene aggiunto programmazione per una vista in un viewController. Quando quella viewController viene chiusa, l'assunto è che le risorse verranno smaltite: sono stato attento a evitare i cicli di conservazione. Quando guardo il consumo di memoria nel debugger su un dispositivo reale, la memoria si abbassa in modo appropriato quando viene scaricato il viewController. Tuttavia, se caricherò ripetutamente quel viewController per guardare l'immagine, quindi lo spegni, quindi lo ricarico di nuovo, alla fine incontrerò "App terminata a causa di un errore di memoria"
Se attendo qualche istante dopo aver chiuso ViewController, il gli errori di memoria sembrano meno frequenti, il che mi porta a credere che la memoria sia ancora rilasciata dopo che la viewController è stata chiusa ...? Ma ho anche visto l'errore dopo solo un paio di volte di apertura e chiusura non così rapida del viewController.
Devo utilizzare GPUImage e/o GPUImageView in modo inefficiente e sto cercando indicazioni.
Grazie!
MODIFICA: Vedere sotto per l'implementazione del controller di visualizzazione.
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);
}
});
}
}
}
Ora che sto cercando attraverso questa versione ridotta, non passando self
alla ImageManager
creare un ciclo di trattenere anche se mi riferisco è weak
mente al thread in background? Posso passarlo come riferimento debole direttamente dallo ViewImageViewController
? È certamente possibile che ViewImageViewController
venga eliminato prima del completamento del metodo fetchImageAsync
e che venga richiamata la richiamata.
MODIFICA: Penso di aver trovato il problema. Se si guarda lo ViewImageViewController
nella richiamata, creo un NSTimer e passo il self. Il mio sospetto è che si stia creando un ciclo di conservazione se viewController viene eliminato prima dell'esecuzione del timer. Questo spiegherebbe perché, se aspetto qualche secondo in più, non ottengo l'errore di memoria, perché il timer si attiva e il viewController dispone correttamente. Ecco la correzione (credo).
// 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;
}
}
Puoi fornire maggiori informazioni sul controller di visualizzazione? E 'in effetti stato deallocato?Se hai una perdita di memoria, il problema non sarebbe il codice che hai mostrato. – matt
Certamente. Aggiornerò l'OP. – a432511
@matt aggiunto per tua richiesta ... e potresti avere ragione ... vedi sopra – a432511