3

I am trying to make a class that plays YouTube videos, but I am having several problems with it.

Here is my class that handles YouTube videos:

// 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

Here is the code that calls this class (this code is located in the appDelegate):

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

FYI mainView and ytObj are declared as this:

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

When this code is run, the app crashes and I get this in the console:

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

If I set the UIWebView delegate to nil then the app doesn't crash, but, as expected, the YouTube video doesn't autoplay.

Can anyone explain to me:

  • Why does the object deallocates immediately after it has been initialized?

  • Why the respondsToSelector: message is sent to an instance other than the MyYouTube one?

  • How can I get the YouTube video to autoplay without the app crashing?

Many thanks.


EDIT

Totally my bad - ytObj is a strong property - I forgot to mention this. Ive added the code to reflect this.


EDIT 2

Ive added a breakpoint on the dealloc method, and this is the call stack:

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

The last entry here (playYouTube:) is the method that contains the code in my app delegate that is in the original post above. So, one more question:

  • What is objc-object::sidetable_release(bool), what does it do, and why is it releasing my YouTube object?
Jimmery
  • 9,783
  • 25
  • 83
  • 157

3 Answers3

2

Why does the object deallocates immediately after it has been initialised?

Because nothing owns it.

You set it as the delegate to a view, but the delegate property of UIWebView is an assign property. The view doesn't take ownership of its delegate. This means that when ytObj goes out of scope (possibly before, depending on optimisation) nothing owns it, so it goes away.

EDIT

You also need to make sure that when a ytObj is deallocated, you set the delegate property of any views it is still a delegate of to nil. Otherwise the views will continue to try to send messages to the deallocated object.

How can I get the YouTube video to autoplay without the app crashing?

You need to make sure that ytObj lasts as long as the view of which it is the delegate and when it is deallocated, it's view's delegate is set to nil.


Another minor issue. Your -init function should test that self is not nil after invoking self = [super init]. It shouldn't run any of the rest of the initialisation code if self is nil.

JeremyP
  • 84,577
  • 15
  • 123
  • 161
  • Totally my bad - ytObj is a strong property - I forgot to mention this. Ive added the code to reflect this. – Jimmery Nov 27 '13 at 10:45
  • Setting the delegate to nil when the object gets deallocated seems to be working. I dont understand why the youtube object is working fine after being deallocated, this makes no sense to me. But many thanks for your help - I am going to test this out a bit more, and if it continues to work I will mark you as correct. Thanks again, this problem has been driving me mad for almost 2 weeks now! – Jimmery Nov 27 '13 at 11:23
  • 1
    @Jimmery I think it's because it's not the same instance. Somehow I think the code that creates ytObj and a view to go with it is being executed more than once. The second time it is executed, the strong reference to the first ytObj goes away but the view is still there and tries to send a message to its (now non-existent) delegate. – JeremyP Nov 27 '13 at 16:42
1

1) (my first answer to your first question, before your edit)

This is happening because you're using ARC (automated reference counting) and you are not keeping your local "ytObj" variable ((which is NOT the "self.ytObj" property) around in the object that you created it in. As soon as the function that created the local "ytObj" variable finishes up and returns, the object is automagically dealloc'd.

Change this:

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

to this:

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

And for "best practices", I'd also suggest not doing so much work and/or code in your application delegate. App delegates are meant to receive application specific messages (like "application is suspending", "application is becoming active again", etc.), and you should do stuff like this in a subclassed view controller.

2)

The "respondsToSelector:" error you're seeing is because your YouTube object has been automagically dealloc'd. If it were still living, you wouldn't see that message.

3)

If you search here on Stackoverflow, you'll find other questions and answers that explain how to do autoplaying... like how about this one?

Community
  • 1
  • 1
Michael Dautermann
  • 88,797
  • 17
  • 166
  • 215
  • Totally my bad - ytObj is a strong property - I forgot to mention this. Ive added the code to reflect this. – Jimmery Nov 27 '13 at 10:46
  • use "`self.ytObj`" when alloc & init'ing, @Jimmery – Michael Dautermann Nov 27 '13 at 10:48
  • Ive just done that, and it has made no difference. :( – Jimmery Nov 27 '13 at 10:50
  • 1
    Because with ARC instance variables (which ytObj is) are automatically `strong` unless declared otherwise. The two assignments in this answer both create strong references. The problem is because the delegate of the view is not set to nil when ytObj is deallocated (tut's why the pointer value is different in the log message). – JeremyP Nov 27 '13 at 10:55
0

The object ytObj since is not strong referenced anywhere, it exists only inside the scope where is defined. Delegates most of time are declared as weak properties. None is keeping a strong reference to this object, thus ARC releases it.
Create a strong properties to ytObj and you will see everything working fine.

Andrea
  • 26,120
  • 10
  • 85
  • 131
  • 1
    Exactly! In fact, you have a strong reference only to the `ytObj.view`, but not the object itself! – Deboroh88 Nov 27 '13 at 10:41
  • Totally my bad - ytObj is a strong property - I forgot to mention this. Ive added the code to reflect this. – Jimmery Nov 27 '13 at 10:46