是对PRESViewController的ARC内存管理:动画:完成:在iOS 6.x中被破坏了吗?

时间:2012-12-07 13:44:17

标签: ios ios5 ios6 modalviewcontroller

我的视图控制器通过presentViewController:animated:completion:方法显示视图。视图很好。

然后我忽略了这个视图并重新呈现它并得到以下崩溃:

*** -[WebBrowser isKindOfClass:]: message sent to deallocated instance 0x1f640ac0

我的代码正在使用ARC。这是我的WebBrowser类的代码,一个简单的嵌入式浏览器。

WebBrowser.h:

@interface WebBrowser : ITViewController <UIWebViewDelegate, UIAlertViewDelegate>

@property (nonatomic, strong) NSString *URL;
@property (nonatomic, weak) IBOutlet UIWebView *webView;
@property (nonatomic, weak) IBOutlet UIActivityIndicatorView *spinner;

- (id)initWithURL:(NSString *)URL;
- (IBAction)dismissView:(id)sender;

@end

WebBrowser.m:

@implementation WebBrowser

- (id)initWithURL:(NSString *)URL_ {
    self = [super initWithNibName:@"MyNib" bundle:nil];
    if (self) {
        self.URL = URL_;
    }
    return self;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    self.webView.delegate = self;

    if (self.URL) {
        [self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:self.URL]]];
    }
}

- (IBAction)dismissView:(id)sender {
    self.URL = nil;
    [self.webView stopLoading];
    self.webView.delegate = nil;
    [self dismissViewControllerAnimated:YES completion:NULL];
}

// + some non-related web view delegate stuff 

@end

最后,我将在父视图控制器中呈现视图:

WebBrowser *browser = [[WebBrowser alloc] initWithURL:URL];
[self presentViewController:browser animated:YES completion:NULL];

我正在运行iOS 6并使用ARC进行编译。

首先我认为这个bug与ARC有关。这是我原来的帖子:

原始邮寄

我注意到我的iOS 6.x应用程序在显示模态视图控制器时崩溃,并在以前版本的iOS工作正常时发布它。

责怪我没有使用ARC(这是我在这个项目上的下一个重要步骤),但是,例如,当使用以下代码显示Game Center排行榜时,请执行以下步骤:

  1. 显示排行榜
  2. 关闭排行榜
  3. 再次显示排行榜(PRECISION UPDATE:运行下面显示的showLeaderboard,即显示 GKLeaderboardViewController实例
  4. 然后,发生以下错误

    *** -[GKLeaderboardViewController isKindOfClass:]: message sent to deallocated instance 0x17467120
    

    这是我的代码:

    - (void)showLeaderboard {
        if ([[GKLocalPlayer localPlayer] isAuthenticated]) {
            GKLeaderboardViewController *lb = [[GKLeaderboardViewController alloc] init];
            lb.category = ...;
            lb.leaderboardDelegate = self;
            self.modalPresentationStyle = UIModalPresentationCurrentContext;
            [self presentModalViewController:lb animated:YES];
            [lb release];
        } else {
            ...
        }
    }
    
    - (void)leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController{
        [self dismissModalViewControllerAnimated:YES];
    }
    

    事实证明,删除[lb release]指令解决了我的问题,并再次证明iOS 5.x没有发生此类崩溃。

    Game Center成就视图控制器或我的任何其他自定义视图控制器与presentModalViewController:一起显示也是如此。

    似乎用新presentModalViewController:替换已弃用的presentViewController:animated:completion:指令并不能解决问题。

2 个答案:

答案 0 :(得分:1)

我至少看到一个可能的问题:

[self.webView stopLoading];
self.webView.delegate = nil;

通常,在调用delegate之前将nil设置为stopLoading会更安全。

此外,使用dismissViewControllerAnimated它的方式肯定更安全,也就是说,在呈现控制器上调用它。虽然文档声明调用被传递给呈现控制器,但是在方法内部释放的对象上调用方法并不是一个好主意。

- (IBAction)dismissView:(id)sender {
    // pass the event to the presenting controller
    // the presenting controller should call [self dismissViewControllerAnimated:YES completion:NULL];
}

- (void)viewWillDissapear {
  [super viewWillDissapear];

   //no need to do this, it's done automatically in ARC
   //self.URL = nil;

   self.webView.delegate = nil;
   [self.webView stopLoading];
}

答案 1 :(得分:1)

修改

根据Apple的文档,您应该从父(显示)viewcontroller中解除viewcontroller:http://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/ModalViewControllers/ModalViewControllers.html#//apple_ref/doc/uid/TP40007457-CH111-SW14

“当解雇一个呈现的视图控制器时,首选方法是让呈现视图控制器忽略它。换句话说,只要有可能,呈现视图控制器的同一个视图控制器也应该负责解雇它“。

另外,您能否清楚哪些视图控制器正在呈现哪些视图控制器,以及多少视图控制器相互叠加。来自apple docs:

“如果您连续呈现多个视图控制器,从而构建一堆呈现的视图控制器,则在堆栈中较低的视图控制器上调用此方法会解除其直接子视图控制器和堆栈上该子节点上方的所有视图控制器当发生这种情况时,只有最顶层的视图以动画方式被删除;任何中间视图控制器都只是从堆栈中删除。最顶层的视图使用其模态过渡样式被解除,这可能与使用的样式不同。其他视图控制器在堆栈中较低。“

因此,如果您认为它可能与来自您的webview的委托方法有任何关系,您应该取消订阅委托属性中的视图/停止在viewWillUnload中加载webview,而不是在解雇IBAction中,因为赢了不一定被称为。

我对此进行了编辑,使其更加完整/清晰

在呈现视图之前,您需要将浏览器视图设置为具有强属性的实例变量。在解雇之后将其设置为零。

首先为Modals创建一个委托协议:

@protocol ModalViewDelegate
/**
 Delegation callback for when a modal has been dismissed
*/
- (void)modalDidDismiss:(UIViewController*)viewController;
@end

在您的演示视图控制器界面中,订阅协议:

@interface PresentingViewController <ModalViewDelegate>
@property (nonatomic,strong) WebBrowser *browserView;
@end

在展示视图时的实施中:

WebBrowser *browser = [[WebBrowser alloc] initWithURL:URL];
browser.delegate = self
self.browserView = browser;
[self presentViewController:browser animated:YES completion:NULL];

在您的WebBrowser界面中:

@interface WebBrowser : ITViewController <UIWebViewDelegate, UIAlertViewDelegate>

@property (nonatomic, strong) NSString *URL;
@property (nonatomic, weak) IBOutlet UIWebView *webView;
@property (nonatomic, weak) IBOutlet UIActivityIndicatorView *spinner;

@property (nonatomic, weak) id <ModalViewDelegate> delegate;

- (id)initWithURL:(NSString *)URL;
- (IBAction)dismissView:(id)sender;

@end

在您的WebBrowser实现中:

- (IBAction)dismissView:(id)sender {
    self.URL = nil;
    [self.webView stopLoading];
    self.webView.delegate = nil;
    [self.delegate didDismissBrowser:self];
}

回到父视图控制器:

- (void)didDismissBrowser:(WebBrowser*)browser
{
    if (browser == self.browserView)
    {
        [self dismissViewControllerAnimated:YES completion:NULL];
        self.browserView = nil;
    }
}