Frage

Der Code hier ist eine modale Ansicht, die vom RootViewController gestartet wurde und ein Video mit einem Miniaturfilmstrip unter dem Film angezeigt und dann an den Film zeitgesteuert wurde.

Es funktioniert alles, aber es gibt ein Speicherleck / mangelnde Veröffentlichung, den ich einfach nicht sehen kann, um drei Tage lang zu suchen, um es zu reparieren. Es ist an der Zeit, um Hilfe zu bitten ...

Wenn ich das nsnotificationCenter deaktiviere, indem ich es kommentiere (hervorgehoben in der .m), habe ich keine Probleme in Bezug auf das Gedächtnis und behalte den zeitgesteuerten Text. Aber ich habe auch keine Miniaturansichten. Ich habe versucht, einzufügen [[NSNotificationCenter alloc] removeObserver:self]; An zahlreichen Punkten, um zu sehen, ob dies für mich loswerden wird. Aber leider ohne Erfolg.

Ich habe auch versucht, den "Hintergrundtimer" zu veröffentlichen, aber es ist nicht übermäßig beeindruckt, wenn ich versuche zu kompilieren und zu rennen.

Im Wesentlichen gibt es das erste Mal, dass ich die modale Sichtweise lade, keine Probleme und alles scheint großartig zu sein - wenn ich sie mit dem schließe -(IBAction)close:(id)sender; Es scheint, dass etwas nicht veröffentlicht wird, da ich das nächste Mal die gleiche Seite starte. Die Speicherverwendung steigt um etwa 30% (ungefähr die Menge, die von der Miniaturansichtserzeugung verwendet wird) und erhöht sich bei jeder Wiederaufnahme der Major-Menge um ungefähr die gleiche Menge Modale Ansicht.

Bitte beachten Sie, dass ich ein Neuling dafür bin und der Fehler für diejenigen von Ihnen, die wissen, wahrscheinlich einen blutigen dummen. Aber im Interesse, dieses Projekt durchzuführen, werde ich gerne jeden Missbrauch nehmen, wenn Sie Lust auf mich werfen.

Hier ist der Code:

.h

#import <UIKit/UIKit.h>
#import <MediaPlayer/MPMoviePlayerController.h>
#import "ImageViewWithTime.h"
#import "CommentView.h"

@interface SirloinVideoViewController_iPad : UIViewController {
    UIView *landscapeView;
    UIView *viewForMovie;
    MPMoviePlayerController *player;
    UILabel *onScreenDisplayLabel;
    UIScrollView *myScrollView;
    NSMutableArray *keyframeTimes;
    NSArray *shoutOutTexts;
    NSArray *shoutOutTimes;
    NSTimer *backgroundTimer;
    UIView *instructions;
}

-(IBAction)close:(id)sender;
-(IBAction)textInstructions:(id)sender;

@property (nonatomic, retain) IBOutlet UIView *instructions;
@property (nonatomic, retain) NSTimer *theTimer;
@property (nonatomic, retain) NSTimer *backgroundTimer;
@property (nonatomic, retain) IBOutlet UIView *viewForMovie;
@property (nonatomic, retain) MPMoviePlayerController *player;
@property (nonatomic, retain) IBOutlet UILabel *onScreenDisplayLabel;
@property (nonatomic, retain) IBOutlet UIScrollView *myScrollView;
@property (nonatomic, retain) NSMutableArray *keyframeTimes;

-(NSURL *)movieURL;
- (void) playerThumbnailImageRequestDidFinish:(NSNotification*)notification;
- (ImageViewWithTime *)makeThumbnailImageViewFromImage:(UIImage *)image andTimeCode:(NSNumber *)timecode;
- (void)handleTapFrom:(UITapGestureRecognizer *)recognizer;
@end

.m

#import "SirloinVideoViewController_iPad.h"
#import "SirloinTextViewController.h"

@implementation SirloinVideoViewController_iPad
@synthesize theTimer, backgroundTimer, viewForMovie, player,
   onScreenDisplayLabel, myScrollView, keyframeTimes, instructions; 

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {

    }
    return self;
    [nibNameOrNil release];
    [nibBundleOrNil release];
}

- (IBAction)close:(id)sender{
    [self.parentViewController dismissModalViewControllerAnimated:YES];
    [player stop];
    [player release];
    [theTimer invalidate];
    [theTimer release];
    [backgroundTimer invalidate];
    [SirloinVideoViewController_iPad release];
}

-(IBAction)textInstructions:(id)sender {

    SirloinTextViewController *vController = [[SirloinTextViewController alloc] initWithNibName:nil bundle:nil];
    [self presentModalViewController:vController animated:YES];
    [vController release];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    keyframeTimes = [[NSMutableArray alloc] init];
    shoutOutTexts = [[NSArray 
                      arrayWithObjects:
                      @"1. XXXXXXXXXXXX",
                      @"2. XXXXXXXXXXXX",
                      @"3. XXXXXXXXXXXX",
                      @"4. XXXXXXXXXXXX",
                      @"5. XXXXXXXXXXXX",
                      @"6. XXXXXXXXXXXX"
                      @"7. XXXXXXXXXXXX",
                      @"8. XXXXXXXXXXXX",                     
                      @"9. XXXXXXXXXXXX",                    
                      @"10. XXXXXXXXXXXX",                      
                      @"11. XXXXXXXXXXXX",                      
                      @"12. XXXXXXXXXXXX",                      
                      @"13. XXXXXXXXXXXX",
                      @"14. XXXXXXXXXXXX",                     
                      @"15. XXXXXXXXXXXX",
                      nil] retain];

    shoutOutTimes = [[NSArray 
                      arrayWithObjects:
                      [[NSNumber alloc] initWithInt: 1], 
                      [[NSNumber alloc] initWithInt: 73],
                      [[NSNumber alloc] initWithInt: 109],
                      [[NSNumber alloc] initWithInt: 131],
                      [[NSNumber alloc] initWithInt: 205],
                      [[NSNumber alloc] initWithInt: 250],
                      [[NSNumber alloc] initWithInt: 337],
                      [[NSNumber alloc] initWithInt: 378],
                      [[NSNumber alloc] initWithInt: 402],
                      [[NSNumber alloc] initWithInt: 420],
                      [[NSNumber alloc] initWithInt: 448],
                      [[NSNumber alloc] initWithInt: 507],
                      [[NSNumber alloc] initWithInt: 531],
                      [[NSNumber alloc] initWithInt: 574],
                      nil] retain];

    self.player = [[MPMoviePlayerController alloc] init];
    self.player.contentURL = [self movieURL];

    self.player.view.frame = self.viewForMovie.bounds;
    self.player.view.autoresizingMask = 
    UIViewAutoresizingFlexibleWidth |
    UIViewAutoresizingFlexibleHeight;

    [self.viewForMovie addSubview:player.view];

    backgroundTimer = [NSTimer scheduledTimerWithTimeInterval:0.5f target:self selector:@selector(timerAction:) userInfo:nil repeats:YES];

    [self.view addSubview:self.myScrollView];

    //I am pretty sure that this is the culprit - Just not sure why...

    [[NSNotificationCenter defaultCenter] 
     addObserver:self
     selector:@selector(movieDurationAvailable:)
     name:MPMovieDurationAvailableNotification
     object:theTimer];

    //Could be wrong, but when commented out I don't have the memory issues
}

- (NSInteger)positionFromPlaybackTime:(NSTimeInterval)playbackTime
{
    NSInteger position = 0;
    for (NSNumber *startsAt in shoutOutTimes)
    {
        if (playbackTime > [startsAt floatValue])
        {
            ++position;
        }
    }
    return position;
}

-(NSURL *)movieURL
{
    NSBundle *bundle = [NSBundle mainBundle];
    NSString *moviePath = 
    [bundle 
     pathForResource:@"sirloin" 
     ofType:@"m4v"];

    if (moviePath) {
        return [NSURL fileURLWithPath:moviePath];
    } else {
        return nil;
    }
}

NSTimeInterval lastCheckAt = 0.0;

- (void)timerAction: theTimer 
{
    int count = [shoutOutTimes count];

    NSInteger position = [self positionFromPlaybackTime:self.player.currentPlaybackTime];

   NSLog(@"position is at %d", position);
    if (position > 0)
    {
        --position;
    }
    if (position < count) 
    {
        NSNumber *timeObj = [shoutOutTimes objectAtIndex:position];
        int time = [timeObj intValue];

        NSLog(@"shout scheduled for %d", time);
        NSLog(@"last check was at %g", lastCheckAt);
        NSLog(@"current playback time is %g", self.player.currentPlaybackTime);

        if (lastCheckAt < time && self.player.currentPlaybackTime >= time)
        {
            NSString *shoutString = [shoutOutTexts objectAtIndex:position];

            NSLog(@"shouting: %@", shoutString);

            CommentView *cview = [[CommentView alloc] initWithText:shoutString];
            [self.instructions addSubview:cview];
            [shoutString release];
        }
    }
    lastCheckAt = self.player.currentPlaybackTime;
}

// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    return YES;
}

-(void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath {
    [[NSNotificationCenter defaultCenter] removeObserver:MPMovieDurationAvailableNotification];
    [[NSNotificationCenter defaultCenter] removeObserver:MPMoviePlayerThumbnailImageRequestDidFinishNotification];
    [keyPath release];
}

- (void) movieDurationAvailable:(NSNotification*)notification {
    float duration = [self.player duration];

    [[NSNotificationCenter defaultCenter] 
     addObserver:self 
     selector:@selector(playerThumbnailImageRequestDidFinish:)
     name:MPMoviePlayerThumbnailImageRequestDidFinishNotification
     object:nil];

    NSMutableArray *times = [[NSMutableArray alloc] init];
    for(int i = 0; i < 20; i++) {
        float playbackTime = i * duration/20;
        [times addObject:[NSNumber numberWithInt:playbackTime]];
    }
    [self.player 
     requestThumbnailImagesAtTimes:times 
     timeOption: MPMovieTimeOptionExact];
}

- (void) playerThumbnailImageRequestDidFinish:(NSNotification*)notification {
    NSDictionary *userInfo = [notification userInfo];
    NSNumber *timecode = 
    [userInfo objectForKey: MPMoviePlayerThumbnailTimeKey]; 
    UIImage *image = 
    [userInfo objectForKey: MPMoviePlayerThumbnailImageKey];
    ImageViewWithTime *imageView = 
    [self makeThumbnailImageViewFromImage:image andTimeCode:timecode];

    [myScrollView addSubview:imageView];

    UITapGestureRecognizer *tapRecognizer = 
    [[UITapGestureRecognizer alloc] 
     initWithTarget:self action:@selector(handleTapFrom:)];
    [tapRecognizer setNumberOfTapsRequired:1];

    [imageView addGestureRecognizer:tapRecognizer];

    [tapRecognizer release];
    [image release];
    [imageView release];
}

- (void)handleTapFrom:(UITapGestureRecognizer *)recognizer {
    ImageViewWithTime *imageView = (ImageViewWithTime *) recognizer.view;
    self.player.currentPlaybackTime = [imageView.time floatValue];
}

- (ImageViewWithTime *)makeThumbnailImageViewFromImage:(UIImage *)image andTimeCode:(NSNumber *)timecode {
    float timeslice = self.player.duration / 3.0;
    int pos = [timecode intValue] / (int)timeslice;

    float width = 75 * 
    ((float)image.size.width / (float)image.size.height);

    self.myScrollView.contentSize = 
    CGSizeMake((width + 2) * 13, 75);

    ImageViewWithTime *imageView = 
    [[ImageViewWithTime alloc] initWithImage:image];
    [imageView setUserInteractionEnabled:YES];

    [imageView setFrame:CGRectMake(pos * width + 2, 0, width, 75.0f)];

    imageView.time = [[NSNumber alloc] initWithFloat:(pos * timeslice)];
    return imageView;

    [myScrollView release];
}

- (void)dealloc {
    [player release];
    [viewForMovie release];
    [onScreenDisplayLabel release];
    [keyframeTimes release];
    [instructions release];
    [shoutOutTexts release];
    [shoutOutTimes release];
    [super dealloc];
}

@end

Diese App ist bereits stark mit UIWebView (was einfach nur sux) verwendet, also versuche ich, die Dinge richtig zu machen und es richtig zu machen.

War es hilfreich?

Lösung

Sie haben ein paar mehr Probleme als eines Leck.

Der erste

ist in deinem initWithNibName:bundle: Da du dort nichts Nützliches tust: Lass es ganz los! (Außerdem veröffentlichen Sie keine Argumente, die an Ihre Methoden übergeben werden! Zum Glück haben Sie diese Veröffentlichungen in Zeilen platziert, die nicht erreichbar sind, dh nach der Rückkehrerklärung ...)

Nächste Methode, nächste Probleme

  1. Warum senden Sie? release zu einem Klassenobjekt? Nicht! Das ist falsch an viele Ebenen.
  2. Sie haben beschlossen, Eigenschaften für Ihre Timer zu erstellen. Das ist per se nichts Schlimmes. Aber warum verwenden Sie dann die Ivars direkt hier? Ich würde Sie nachdrücklich ermutigen, umzusetzen setTheTimer: und setBackgroundTimer: Um die Invalidierung zu bewältigen und ordnungsgemäß freizugeben und einfach tun self.theTimer = nil; self.backgroundTimer = nil; hier. Das würde die Asymmetrie auch beim Umgang mit diesen Dingen beheben. (Übrigens: Thetimer ist nicht eine solche Ein toller Name für einen Ivar ... besonders wenn es gibt Ein weiterer Ivar, das ist ein Timer!)

textInstructions: Sieht unbekannt aus, aber ...

viewDidLoad hat noch einige Probleme

  1. Es läuft einen MPMovieplayerController:
    Das @property wird es behalten, also müssen Sie das ausgleichen alloc hier.
  2. backgroundTimer hat eine entsprechende @property Dies wird als beibehalten: Sie verstoßen hier gegen diesen API -Vertrag, da Sie den Timer nur dem IVAR zuweisen. Verwenden self.backgroundTimer = ... stattdessen.
  3. Aus all dem Code, den Sie gepostet haben, scheint es mir, dass Passieren theTimer Als letzte Argument in Ihrem Anruf zu -[NSNotificationCenter addObserver:selector:name:object:] ist eine ausgefallene Art, hereinzugeben nil als dieser Parameter. Welches ist irgendwie gut, weil normalerweise NSTimer Posts nicht zu viele MPMovieDurationAvailableNotifications. In der Tat, wie ich nicht sehen kann theTimer verwendet werden, außer in close:: Könnte es sein, dass dies nur ein nutzloser Überrest ist, bevor Sie die eingeführt haben backgroundTimer Ivar/@Property? (Nun, es gibt ein weiteres Vorkommen einer Variablen dieses Namens, aber sie sollte von einer großen Fett -Compiler -Warnung begleitet werden ...)
  4. Implementieren Sie in irgendeiner Weise viewDidUnload? Wenn ja, tut es:
    1. self.player = nil;?
    2. [shoutOutTexts release], shoutOutTexts = nil;?
    3. [shoutOutTimes release], shoutOutTimes = nil;?
    4. self.keyframeTimes = nil;?
    5. [[NSNotificationCenter defaultCenter] removeObserver:self name: MPMovieDurationAvailableNotification object:nil];?
    6. self.backgroundTimer = nil;? (Annahme, setBackgroundTimer: Veröffentlichungen und ungültig der alte Wert)
  5. Aktualisieren Ich habe diesen beim ersten Versuch verpasst: Du leckst 15 NSNumbers hier. Verwenden [NSNumber numberWithInt:] anstelle von alloc/init im Setup von shoutOutTimes.

Eine kleine Bemerkung auf movieURL, was Sie in den folgenden Einzeiler verwandeln können:

-(NSURL*)movieURL {
    return [[NSBundle mainBundle] URLForResource:@"sirloin" withExtension:@"m4v"];
}

Und dann das

NSTimeInterval lastCheckAt = 0.0; innerhalb des globalen Umfangs. Aus Ihrer Verwendung davon: Ivar plz?!? Eins?

Weitere Probleme später. Ich muss mir zuerst etwas zu essen bringen.


Zweiter Teil

Jetzt gehen wir in timerAction:

Das erste Problem ist nicht zu schwer, insbesondere in diesem bestimmten Kontext -, aber Sie sollten sich bewusst sein -[NSArray count] Gibt ein zurück NSUInteger und das das U ist kein Tippfehler, sondern eine Bezeichnung, dass dieser Wert nicht signiert ist. Sie werden sicherlich nicht auf Probleme mit der Unterzeichnung in dieser App stoßen und tun bei anderen Gelegenheiten selten, aber Wenn Sie tun es, sie machen wirklich funky Fehler aus und Sie sollten sich der Auswirkungen bewusst sein ...
Das eigentliche Problem mit dieser Methode ist jedoch, dass Sie es sind auslaufen CommentView per Iteration während zur gleichen Zeit - Überabstimmung NSString. Die Tatsache, dass Sie in erster Linie String -Literale verwendet haben (die niemals mitgeteilt werden) (dh wenn Sie ShoutoutTimes initialisiert haben), spart hier Ihren Hintern völlig.

Next Up: removeObserver:forKeyPath:

Du Ja wirklich Sollte diese wirklich schlechte Angewohnheit, Parameter freizusetzen, loswerden, die an Ihre Methoden übergeben werden!

Abgesehen davon, dass Sie die Gesamtheit dieser Methode loswerden!

Zuallererst removeObserver:forKeyPath: ist eine Methode aus der NSKeyValueObserving Informelles Protokoll und spielt eine völlig andere Rolle als das, was Sie (Ab-) verwenden, um es hier zu erreichen. Zweitens ist es eine dieser Methoden, bei denen es unerlässlich ist, sich durchzusetzen super wenn - auf jeden Fall - Sie Ja wirklich müssen es außer Kraft setzen. (Nun, außer als Sie überschrieben waren addObserver:forKeyPath:options:context: Auch und es sollte auch sein, dass Sie es nicht tun sollten, wenn Sie nicht so unangemessen sind, dass Sie wirklich wissen, was Sie sind, wenn Sie nicht sind, wenn Sie immer geplant werden- On-Use-Kvo.)

movieDurationAvailable:

Wie Evan sagte, du austritt times hier. Gehen Sie für seinen Vorschlag oder - stattdessen - machen Sie es NSMutableArray *times = [NSMutableArray array]; Und du bist hier fertig.

playerThumbnailImageRequestDidFinish:

Sie besitzen nicht image, Veröffentlichen Sie es also nicht!
Persönlich würde ich die Ansicht beenden (dh das Erkenntnis hinzufügen und solche Dinge tun), bevor ich sie in die Aussichts-hierarchie hinzufüge, aber das ist völlig eine Frage des Geschmacks ...

makeThumbnailImageViewFromImage:andTimeCode:

... leckt an NSNumber (verwenden [NSNumber numberWithFloat:(pos * timeslice)] anstelle von alloc/initWithFloat:-dance) und verhindert, dass Sie aufgrund von Übervertrieben abstürzen myScrollView nach der bedingungslosen Rückgabeerklärung, die ihr direkt vorausgeht (Puh!). Während wir gerade dabei sind: Benennen Sie diese Methode in beide newThumbnailImageView... Damit Sie diesen Code in einem Jahr oder so erneut überprüft werden, wissen Sie das sofort [imageView release]; unten playerThumbnailImageRequestDidFinish: Es ist wirklich notwendig, ohne die Implementierung dieser Methode anzusehen.
Alternativ könnten Sie es umbenennen thumbnailImageView... und ändern Sie die Rückgabeerklärung in return [imageView autorelease];. Bonus: eine Linie weniger in playerThumbnailImageRequestDidFinish: als die [imageView release]; Dann wird es veraltet.

dealloc

Hinzufügen [[NSNotificationCenter defaultCenter] removeObserver:self]; ganz oben.

Der Rest sieht gut aus. (Obwohl ich es seltsam finde, dass der Ivar landscapeView wird niemals/nirgendwo außer seiner Erklärung erwähnt.)

Zusammenfassung

Lesen Sie die Abschnitte Speicherverwaltungsregeln und Autorelease Nochmals aus dem "Speicherverwaltungs -Programmierhandbuch" von Apple ". Sie sind reines Gold!

Andere Tipps

Du ließ nie frei times in movieDurationAvailable::

NSMutableArray *times = [[NSMutableArray alloc] init];

Du solltest benutzen autorelease Wenn Sie es an die Methode weitergeben:

[self.player requestThumbnailImagesAtTimes:[times autorelease] timeOption: MPMovieTimeOptionExact];
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top