【问题标题】:Frustrating UIWebView Delegate Crash issue令人沮丧的 UIWebView 委托崩溃问题
【发布时间】:2012-01-24 22:22:57
【问题描述】:

我创建了一个运行完美的 ARC 应用程序。它有一个 UINavigationController,我用它来推送视图,一切运行良好。

我正在将应用程序转换为 iPad,并且我决定将其中一个视图显示为弹出框。 (我不喜欢 UIPopoverController 所以我创建了自己的基本弹出窗口)。它被添加到视图中,如下所示..

MyViewController *hotelinf = [[MyViewController alloc]
initWithNibName:@"MyViewController_iPad" bundle:nil];

[self.view addSubview:hotelinf.view];

视图被添加为子视图。我添加的视图包含一个 UIWebView,其中包含通常的委托方法,但是当视图尝试访问其中一个委托时,它只会崩溃。

*** -[MyViewController respondsToSelector:]: message sent to deallocated instance 0x7917b90

具体来说,它在这一行崩溃..

[self.webView loadHTMLString:stringResponse baseURL:nil];

我已经多次将视图(和 UINavigationControllers)显示为子视图,没有任何问题,尽管它们都没有包含 UIWebView 委托。我猜我必须设置我的 UIViewController istance 的委托,但我不确定如何。还值得注意的是,如果我在现有的 UINavigationController 中推送视图,它会调用并加载 HTML,这肯定意味着它与视图本身的代码无关。

任何帮助将不胜感激!这是代码(除了上面显示的控制器)..

.h

@interface MyViewController : UIViewController <UIWebViewDelegate, UIActionSheetDelegate> {

//Unrelated IBOutlets

}


@property (nonatomic, strong) UIWebView *webView;

@end

.m

@implementation MyViewController
@synthesize webView;

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.webView = [[UIWebView alloc]initWithFrame:CGRectMake(317,283,393,354)];
    self.webView.delegate = self;
    [self.view addSubview:self.webView];

    [NSThread detachNewThreadSelector:@selector(getHTMLString) toTarget:self withObject:nil];  
}

-(void)getHTMLString {

    @autoreleasepool {
        //Download a valid HTML String
        [self performSelectorOnMainThread:@selector(loadHTML) withObject:nil waitUntilDone:NO];
    }
}

- (void)viewDidDisappear:(BOOL)animated
{
    [super viewDidDisappear:animated];
    self.webView = nil;
}

-(void)loadHTML {


    self.webView.opaque = NO;
    self.webView.backgroundColor = [UIColor clearColor];
    if ([stringResponse isEqualToString:@""]) {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:@"Could not connect to XXXXX.com. Please verify you are connected to a working 3G/WIFI Network." delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
        [alert show];

    } else {
        //it crashes here only when loaded as a subview - the first access to the delegate
        [self.webView loadHTMLString:stringResponse baseURL:nil];
    }   
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
}


- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
}

- (void)webViewDidFinishLoad:(UIWebView *)webView {

        [self stopIndicator];
} 

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{

    if (error.code == NSURLErrorCancelled) return; // this is Error -999
    [self stopIndicator];

    // report the error inside the webview
    NSString* errorString = [NSString stringWithFormat:
                             @"<html><center><font size=+10              color='black' face='Helvetica'>An error occurred:<br>%@</font></center></html>",
                             error.localizedDescription];
    [self.webView loadHTMLString:errorString baseURL:nil];

    UIAlertView *alert = [[UIAlertView alloc]
                          initWithTitle:@"Cannot load URL."
                          message:@"You have a connection failure. Please    verify you are connected to a WIFI or 3G enabled Network."
                          delegate:self cancelButtonTitle:@"Ok"     otherButtonTitles:nil];
    [alert show];



}

@end

【问题讨论】:

    标签: iphone objective-c ios xcode delegates


    【解决方案1】:

    该问题与UIWebView 无关,而是与您的控制器类有关。确实,

    MyViewController *hotelinf = [[MyViewController alloc]
       initWithNibName:@"MyViewController_iPad" bundle:nil];
    
    [self.view addSubview:hotelinf.view];
    

    您正在分配控制器并将其分配给局部变量;然后将控制器的视图作为子视图添加到当前视图。这样做,该视图被保留,但控制器对象本身会发生什么?你放出来吗?或者它泄漏(因为它被分配给一个局部变量)?

    这可能解释了为什么稍后调用respondsToSelector 方法时,控制器已经被释放了......

    解决此问题的一种方法是在主控制器类中创建一个新属性或一个 ivar,并将 MyViewController 存储在其中。不要忘记在 dealloc 中释放它。

    我还建议另一件事。在:

    - (void)viewDidDisappear:(BOOL)animated
    {
        [super viewDidDisappear:animated];
        self.webView = nil;
    }
    

    在释放视图之前将 webView 委托设置为 nil:

    - (void)viewDidDisappear:(BOOL)animated
    {
        [super viewDidDisappear:animated];
        self.webView.delegate = nil;
        self.webView = nil;
    }
    

    我还可能会回顾一下您在viewDidDisappear 中发布 webView 的原因。另一方面,您将其分配到viewDidLoad。这种不对称是危险的,因为每当主视图消失(出于任何原因)时,webView 都会被移除,而当视图重新出现时,它就不再存在了。

    【讨论】:

    • 太好了,感谢您为我确认。有办法解决吗?
    • 请看我的编辑。您可以定义一个 ivar 来存储 UIWebView 指针,但也可以检查我突出显示的其他几点。
    • 顺便说一句,这是一个 ARC 项目 :)
    • 抱歉,当我发布回复时它没有显示其余的消息,感谢您的帮助。
    • 接受答案并给塞尔吉奥加分怎么样 :)
    【解决方案2】:

    最好添加所有委托方法。你还没有添加前两个。很可能,当发送消息 webViewDidStartLoad 时,您的代码崩溃了

    – webView:shouldStartLoadWithRequest:navigationType:
    – webViewDidStartLoad:
    – webViewDidFinishLoad:
    – webView:didFailLoadWithError:
    

    【讨论】:

    • 这是不正确的。所有这些方法都是可选的。原始海报的错误表明实例已被释放,而不是选择器未被识别。 sergio 的回答是正确的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-19
    • 2011-05-08
    • 2012-04-03
    • 1970-01-01
    • 2012-10-25
    相关资源
    最近更新 更多