关闭模态视图时无法形成对 UIScrollView sub class (EXC_BAD_INSTRUCTION) 的弱引用

Cannot form weak reference to UIScrollView sub class (EXC_BAD_INSTRUCTION) when dismissing modal view

大家好,我调试这个问题已经有一段时间了,但到目前为止还没有成功。我在这里很迷路,不知道导致这次崩溃的原因以及如何解决它。如果有人能为此提供帮助,我将不胜感激,非常感谢!

我准备了一个示例项目来演示 GitHub here 中的问题。

场景如下:

  1. 有两个view controller,分别是root view和modal view,各自有一个自定义的scroll view(class即SubScorllView)作为子view,modal视图有一个用于关闭模态视图的按钮。

  2. 滚动视图是 UIScrollView 的子class,每个都有相应的委托协议,它们的 class 层次结构如下:

UIScrollView
∟ SuperScrollView
.....∟ SubScrollView

应用程序以非常简单的方式启动和运行,在 AppDelegate 的 didFinishLaunchingWithOptions:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.backgroundColor = [UIColor blackColor];

    RootViewController * rootVC = [[RootViewController alloc] init];
    self.navVC = [[UINavigationController alloc] initWithRootViewController:rootVC];
    self.navVC.navigationBarHidden = TRUE;

    self.window.rootViewController = self.navVC;
    [self.window makeKeyAndVisible];

    ModalViewController *modalVC = [[ModalViewController alloc] init];
    [self.navVC presentViewController:modalVC animated:YES completion:nil];

    return YES;
}

并且视图是从xib文件中加载的,滚动视图的委托也设置在里面,并且有一些关于启动和设置滚动视图子委托的方法的覆盖classes .

当我通过单击模态视图中的 "Close" 按钮关闭模态视图时出现问题,当单击该按钮时,会发生以下情况:

- (IBAction)didPressedCloseButton:(id)sender {

    self.subScrollView.delegate = nil;
    [self dismissViewControllerAnimated:YES completion:nil];

}

应用程序在 SuperScrollView 中的以下部分崩溃:

- (void)setDelegate:(id<SuperScrollViewDelegate>)delegate {

    _superScrollViewDelegate = delegate;

    // trigger UIScrollView to re-examine delegate for selectors it responds
    super.delegate = nil;
    super.delegate = self;  // app crashes at this line
}

控制台中出现以下错误消息:

objc[6745]: Cannot form weak reference to instance (0x7fa803839000) of class SubScrollView. It is possible that this object was over-released, or is in the process of deallocation.

我不明白为什么应用程序会崩溃并给出上述错误消息,或者我应该如何解决它。我尝试使用错误消息进行搜索,但似乎该消息主要与文本视图等其他 classes 相关,而其他一些人通过在解除分配之前将滚动视图的委托设置为 nil 来解决它,但它不起作用就我而言。

==========

更新: 刚刚用模拟器测试了这种情况是否发生在 iOS 8 上,它根本不会像在 iOS 9 上那样崩溃。

当 SuperScrollView 被释放时,setDelegate 被隐式调用。在 iOS 9 中,你不能将委托设置为 self,因为 self 正在被释放(不知道为什么这在 iOS 8 中有效)。要解决此问题,您可以先检查传入的委托参数是否不为 nil,然后才将 super.delegate 设置为 self:

- (void)setDelegate:(id<SuperScrollViewDelegate>)delegate {

    _superScrollViewDelegate = delegate;

    // trigger UIScrollView to re-examine delegate for selectors it responds
    super.delegate = nil;

    if(delegate)
    {
        super.delegate = self;
    }
}

如果出于某种原因您需要支持自响应 UIScrollView 委托方法,即使 _superScrollViewDelegate 为 nil,您也可以创建一个参数

@interface SuperScrollView ()

@property (nonatomic, weak) SuperScrollView * weakSelf;

@end

在文件的顶部,并在设置中设置

- (void)setup {

    super.delegate = self;
    self.weakSelf = self;
}

然后,在setDelegate中,检查weakSelf不为nil。如果 weakSelf 为 nil,则 self 处于释放过程中,您不应将其设置为 super.delegate:

- (void)setDelegate:(id<SuperScrollViewDelegate>)delegate {

    _superScrollViewDelegate = delegate;

    // trigger UIScrollView to re-examine delegate for selectors it responds
    super.delegate = nil;

    if(self.weakSelf)
    {
        super.delegate = self;
    }
} 

super.delegate = self ,super这里是UIScrollView,super.delegateUIScrollViewDelegate类型,self是[=12类型=],所以你把UIScrollView的delegate设置成scroll view,这没有意义,通常controller应该是UIScrollView的delegate。

当您关闭模态视图控制器时,它正处于释放过程中。 super.delegate = self;,这里self是一个滚动视图,它是self.view的子视图,属于模态视图控制器。所以 self 也在解除分配。

我在Swift遇到了同样的问题,cncool的回答帮助了我。 以下(考虑在父 class 的实例中)解决了我的问题:

deinit {
    self.scrollView.delegate = nil
}