gpt4 book ai didi

ios - Xcode-iOS内存泄漏使我发疯,我认为是NSNotificationCenter,但希望新鲜的眼睛能看到我看不到的东西

转载 作者:行者123 更新时间:2023-11-28 18:08:02 34 4
gpt4 key购买 nike

此处的代码是从RootViewController启动的模式视图,用于在影片下方显示带有缩略图胶片的视频,然后将定时指令绑定到该影片。

一切正常,但是内存泄漏/缺少发行版让我看不到,花了三天时间对其进行修复,现在是时候寻求帮助了...

如果我通过注释掉它来禁用NSNotificationCenter(在.m中突出显示),那么我对内存没有任何问题,并且保留了定时文本。但是我也没有任何缩略图。我尝试在许多地方插入[[NSNotificationCenter alloc] removeObserver:self];来查看是否可以摆脱它。但是a,无济于事。

我也尝试过发布“ backgroundTimer”,但是当我尝试编译和运行时并没有给我留下深刻的印象。

本质上,第一次加载模式视图时,没有任何问题,而且看起来都很好-但是,如果用-(IBAction)close:(id)sender;关闭它,则下次下次启动同一页面时似乎没有释放任何东西。每次重新启动模式视图时,内存使用量都会增加约30%(大约是缩略图生成所使用的量),并且增加的量大致相同。

请记住,我是新手,这个错误很可能会使您知道的人变得愚蠢。但是为了完成这个项目,我很乐意接受您喜欢对我的任何虐待。

这是代码:

。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


这个应用程序已经大量使用UIWebView(只是简单的辅助功能)在外面了,所以我正在尝试正确并正确地做它。

最佳答案

您的问题确实多于一个泄漏。

第一个

在您的initWithNibName:bundle:中,因为您在那里没有做任何有用的事情:完全摆脱它! (此外:不要释放传递给您的方法的参数!幸运的是,您已将这些释放放置在无法访问的行中,即,在return语句之后...)

下一个方法,下一个问题


为什么将release发送到类对象?别!在许多层面上都是错误的。
您已决定为计时器创建属性。本身没有什么不好。但是,为什么要在这里直接使用ivars?我强烈建议您实现setTheTimer:setBackgroundTimer:来处理失效并正确释放,只需在此处执行self.theTimer = nil; self.backgroundTimer = nil;即可。这也将解决不对称性。 (顺便说一句:对于ivar,theTimer并不是一个好名字……尤其是当另一个ivar是一个计时器时!)


textInstructions:看起来并不令人怀疑,但是...

viewDidLoad还有其他问题


它泄漏了MPMoviePlayerController:
@property将保留它,因此您需要在此处平衡alloc
backgroundTimer有一个相应的@property声明为保留:您在这里违反了此API约定,因为您仅将计时器分配给了ivar。请改用self.backgroundTimer = ...
从您发布的所有代码中,在我看来,将theTimer作为对-[NSNotificationCenter addObserver:selector:name:object:]的调用中的最后一个参数传递是将nil作为该参数传递的一种不错的方式。这很好,因为通常NSTimer不会发布太多的MPMovieDurationAvailableNotification。实际上,除了在theTimer中我看不到使用close:之外:难道这只是您引入backgroundTimer ivar / @ property之前的无用残留? (还有另一个同名变量,但是应该伴随着一个大的胖编译器警告...)
您是否以任何方式实现viewDidUnload?如果是这样,请执行以下操作:


self.player = nil;
[shoutOutTexts release], shoutOutTexts = nil;
[shoutOutTimes release], shoutOutTimes = nil;
self.keyframeTimes = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self name: MPMovieDurationAvailableNotification object:nil];
self.backgroundTimer = nil;? (假定setBackgroundTimer:释放旧值并使之无效)

更新我第一次错过了这个:您在这里泄漏了15个NSNumber。在[NSNumber numberWithInt:]的设置中使用shoutOutTimes代替alloc / init。


关于movieURL的小注释,您可以将其转换为以下单线:

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


然后这个

NSTimeInterval lastCheckAt = 0.0;在全球范围内。从您的用法来看:ivar PLZ?!?一个?

以后会有更多问题。我得先给自己吃点东西。



第二部分

现在让我们进入 timerAction:

第一个问题不太严重,尤其是在这种特定情况下,但您应该意识到 -[NSArray count]返回 NSUInteger,并且U不是错字,而是表示该值是无符号的。您当然不会在此应用程序中遇到签名问题,并且很少在其他情况下遇到这种情况,但是当您这样做时,它们会弥补真正的时髦错误,因此您应该意识到其中的含义...
但是,此方法的真正问题在于,每次迭代泄漏一个 CommentView,而同时—过度释放了一个 NSString。实际上,您首先使用的是字符串文字(永远不会取消分配),(即在初始化shoutOutTimes时)完全可以节省您的屁股。

接下来: removeObserver:forKeyPath:

您真的应该摆脱释放参数的坏习惯,这些习惯会传递给您的方法!

话虽如此,请摆脱这种方法的全部!

首先, removeObserver:forKeyPath:NSKeyValueObserving非正式协议中的一种方法,与您在其中使用(滥用)该协议所完成的工作完全不同。
其次,它是其中的一种方法,如果您真的需要覆盖它,则必须通过它调用 super。 (嗯,除了,当您也重写 addObserver:forKeyPath:options:context:时,它应该不说您不应该做那件事,除非您真的知道自己在做什么-如果您曾计划使用KVO。)

movieDurationAvailable:

就像Evan所说的,您在这里泄漏 times。寻求他的建议,或者改为- NSMutableArray *times = [NSMutableArray array];,您就完成了。

playerThumbnailImageRequestDidFinish:

您不拥有 image,所以不要释放它!
就个人而言,在将其添加到视图层次结构之前,我会完成视图的设置(即添加识别器并执行类似的操作),但这完全是个问题……

makeThumbnailImageViewFromImage:andTimeCode:

...泄漏了一个 NSNumber(使用 [NSNumber numberWithFloat:(pos * timeslice)]而不是 alloc/initWithFloat: -dance),并防止了由于紧随其后的无条件return语句(phe!)而过度释放 myScrollView而导致崩溃。
当我们使用它时:将此方法重命名为 newThumbnailImageView...,以便在一年左右的时间重新访问此代码时,您立即知道 [imageView release];底部的 playerThumbnailImageRequestDidFinish:确实是必要的,而无需看一下这个方法的实现。
或者,您可以将其重命名为 thumbnailImageView...并将return语句更改为 return [imageView autorelease];。奖励:随着 playerThumbnailImageRequestDidFinish:的过时, [imageView release];中的一行减少了。

dealloc

在最顶部添加 [[NSNotificationCenter defaultCenter] removeObserver:self];

其余的看起来还可以。 (尽管我觉得很奇怪,但除了声明之外,从未/没有提到过ivar landscapeView。)

摘要

再次阅读Apple的《内存管理编程指南》中的 Memory Management RulesAutorelease部分。他们是纯金!

关于ios - Xcode-iOS内存泄漏使我发疯,我认为是NSNotificationCenter,但希望新鲜的眼睛能看到我看不到的东西,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5843665/

34 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com