【问题标题】:Object deallocates immediately after it has been initialized对象在初始化后立即释放
【发布时间】:2013-12-12 22:40:51
【问题描述】:

我正在尝试制作一个播放 YouTube 视频的课程,但我遇到了一些问题。

这是我处理 YouTube 视频的课程:

// this is the only property declared in the .h file:
@property (strong, nonatomic) UIView * view

// the rest of this is the .m file:
#import "MyYouTube.h"
@interface MyYouTube()
@property (strong, nonatomic) NSDictionary * contentData;
@property (strong, nonatomic) UIWebView * webView;
@property (nonatomic) int videoOffset;
@end

@implementation MyYouTube
@synthesize view,contentData,webView,videoOffset;

- (MyYouTube*) initIntoView: (UIView*) passedView withContent: (NSDictionary*) contentDict {
    NSLog(@"YOUTUBE: begin init");
    self=[super init];
    videoOffset=0;
    view=[[UIView alloc] initWithFrame:passedView.bounds];
    [view setBackgroundColor:[UIColor blackColor]];
    [view setAutoresizesSubviews:YES];
    [view setAutoresizingMask:UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth];
    contentData=contentDict;

    NSString * compiledUrl=[[NSString alloc] initWithFormat:@"http://_xxx_.com/app/youtube.php?yt=%@",[contentData objectForKey:@"cnloc"]];
    NSURL * url=[[NSURL alloc] initWithString:compiledUrl];
    NSURLRequest * request=[[NSURLRequest alloc] initWithURL:url];

    webView=[[UIWebView alloc] initWithFrame:passedView.bounds];
    [webView loadRequest:request];
    [webView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
    [[webView scrollView] setScrollEnabled:NO];
    [[webView scrollView] setBounces:NO];
    [webView setMediaPlaybackRequiresUserAction:NO];
    [webView setDelegate:self];

    NSLog(@"YOUTUBE: self: %@",self);
    NSLog(@"YOUTUBE: delegate: %@",webView.delegate);

    [view addSubview:webView];
    NSLog(@"YOUTUBE: end init");
    return self;
}

-(void)webViewDidFinishLoad:(UIWebView*)myWebView {
    NSLog(@"YOUTUBE: send play command");
    [webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"playVideo(%d)", videoOffset]];
}

- (void) dealloc {
    NSLog(@"YOUTUBE: dealloc");
}
@end

这里是调用这个类的代码(这个代码位于appDelegate中):

NSLog(@"about to load youtube");
ytObj=[[MyYouTube alloc] initIntoView:mainView withContent:cn];
NSLog(@"loaded youtube");
[mainView addSubview:[ytObj view]];

仅供参考 mainView 和 ytObj 声明如下:

@property (nonatomic,strong) UIView * mainView;
@property (nonatomic,strong) MyYouTube * ytObj;

运行此代码时,应用程序崩溃,我在控制台中得到了这个:

about to load youtube
YOUTUBE: begin init
YOUTUBE: self: <MyYouTube: 0x16503d40>
YOUTUBE: delegate: <MyYouTube: 0x16503d40>
YOUTUBE: end init
YOUTUBE: dealloc
loaded youtube
*** -[MyYouTube respondsToSelector:]: message sent to deallocated instance 0x166f19f0

如果我将 UIWebView 委托设置为 nil,则应用不会崩溃,但正如预期的那样,YouTube 视频不会自动播放。

谁能给我解释一下:

  • 为什么对象在被释放后立即释放 初始化了吗?

  • 为什么respondsToSelector: 消息被发送到 MyYouTube 以外的实例?

  • 如何让 YouTube 视频自动播放而不会导致应用崩溃?

非常感谢。


编辑

完全是我的错 - ytObj 是一个强大的属性 - 我忘了提到这一点。我已经添加了代码来反映这一点。


编辑 2

我在dealloc方法上加了一个断点,这就是调用栈:

0 -[MyYouTube dealloc]
1 objc-object::sidetable_release(bool)
2 -[MyAppDelegate playYouTube:]

这里的最后一个条目 (playYouTube:) 是包含上面原始帖子中我的应用程序委托中的代码的方法。所以,还有一个问题:

  • objc-object::sidetable_release(bool) 是什么,它有什么作用,它为什么要释放我的 YouTube 对象?

【问题讨论】:

  • 只是出于好奇,您能否在dealloc 中放置一个断点并查看它是从哪里调用的(查看调用堆栈)?
  • @alex-i 我刚刚为你添加了调用堆栈。

标签: ios objective-c uiwebview youtube


【解决方案1】:

为什么对象在初始化后立即释放?

因为没有人拥有它。

您将其设置为视图的委托,但 UIWebView 的委托属性是 assign 属性。该视图不拥有其委托的所有权。这意味着当ytObj 超出范围时(可能之前,取决于优化),没有人拥有它,所以它消失了。

编辑

您还需要确保在释放 ytObj 时,将仍然是委托的任何视图的委托属性设置为 nil。否则,视图将继续尝试向释放的对象发送消息。

如何让 YouTube 视频自动播放而不会导致应用崩溃?

您需要确保ytObj 的存在时间与它作为委托的视图一样长,并且当它被释放时,它的视图的委托设置为 nil。


另一个小问题。您的 -init 函数应该在调用 self = [super init] 后测试 self 不为零。如果 self 为 nil,它不应该运行任何其余的初始化代码。

【讨论】:

  • 完全是我的错 - ytObj 是一个强大的属性 - 我忘了提到这一点。我添加了代码来反映这一点。
  • 在对象被释放时将委托设置为 nil 似乎有效。我不明白为什么 youtube 对象在被释放后工作正常,这对我来说毫无意义。但是非常感谢您的帮助-我将对此进行更多测试,如果它继续有效,我会将您标记为正确的。再次感谢,这个问题已经让我发疯了快 2 周了!
  • @Jimmery 我认为这是因为它不是同一个实例。不知何故,我认为创建 ytObj 的代码和与之相关的视图被执行了不止一次。第二次执行时,对第一个 ytObj 的强引用消失了,但视图仍然存在并尝试向其(现在不存在的)委托发送消息。
【解决方案2】:

1)(在您编辑之前,我对您的第一个问题的第一个回答)

发生这种情况是因为您使用 ARC(自动引用计数)并且您没有在您创建的对象中保留本地“ytObj”变量((不是“self.ytObj”属性)它进入。一旦创建本地“ytObj”变量的函数完成并返回,对象就会自动解除分配。

改变这个:

ytObj=[[MyYouTube alloc] initIntoView:mainView withContent:cn];

到这里:

self.ytObj=[[MyYouTube alloc] initIntoView:mainView withContent:cn];

对于“最佳实践”,我还建议不要在您的应用程序委托中做太多工作和/或代码。应用程序委托旨在接收特定于应用程序的消息(如“应用程序正在暂停”、“应用程序再次变为活动状态”等),您应该在子类视图控制器中执行此类操作。

2)

您看到的“respondsToSelector:”错误是因为您的 YouTube 对象已被自动解除锁定。如果它仍然存在,您将不会看到该消息。

3)

如果您在 Stackoverflow 上搜索,您会发现其他解释如何进行自动播放的问题和答案...like how about this one?

【讨论】:

  • 完全是我的错 - ytObj 是一个强大的属性 - 我忘了提到这一点。我添加了代码来反映这一点。
  • 在分配和初始化时使用“self.ytObj”,@Jimmery
  • 我刚刚这样做了,没有任何区别。 :(
  • 因为使用 ARC 实例变量(ytObj 是)自动为strong,除非另有声明。此答案中的两个作业都创建了强引用。问题在于,当 ytObj 被释放时,视图的委托未设置为 nil(这就是日志消息中指针值不同的原因)。
【解决方案3】:

对象ytObj 因为在任何地方都没有强引用,所以它只存在于定义的范围内。大多数时候,委托被声明为弱属性。 None 保持对该对象的强引用,因此 ARC 将其释放。
ytObj 创建一个强大的属性,您将看到一切正常。

【讨论】:

  • 没错!实际上,您只有对ytObj.view 的强引用,而不是对象本身!
  • 完全是我的错 - ytObj 是一个强大的属性 - 我忘了提到这一点。我添加了代码来反映这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多