在解除模态视图时,无法形成对UIScrollView子类(EXC_BAD_INSTRUCTION)的弱引用

ala*_*nlo 0 objective-c uiscrollview uiscrollviewdelegate ios

大家好,我已经调试了这个问题很长一段时间但到目前为止没有运气.我在这里迷失了,并且不知道造成这次崩溃的原因以及如何解决它.如果有人能为我提供一些帮助,我将非常感激,非常感谢!

我准备了一个示例项目来在GitHub上演示这个问题.

方案如下:

  1. 有两个视图控制器,即根视图和模态视图,每个视图控制器都有一个自定义滚动视图(即类SubScorllView)作为子视图,模态视图有一个用于关闭模态视图的按钮.

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

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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

- (IBAction)didPressedCloseButton:(id)sender {

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

}
Run Code Online (Sandbox Code Playgroud)

该应用程序崩溃在以下部分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
}
Run Code Online (Sandbox Code Playgroud)

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

objc [6745]:无法形成类SubScrollView的实例(0x7fa803839000)的弱引用.该对象可能被过度释放,或者正在解除分配.

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

==========

更新:刚刚测试过,如果这种情况发生在带有模拟器的iOS 8上,它就不会像在iOS 9上那样崩溃.

cnc*_*ool 5

取消分配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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

@interface SuperScrollView ()

@property (nonatomic, weak) SuperScrollView * weakSelf;

@end
Run Code Online (Sandbox Code Playgroud)

在文件的顶部,并将其设置为安装程序

- (void)setup {

    super.delegate = self;
    self.weakSelf = self;
}
Run Code Online (Sandbox Code Playgroud)

然后,在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;
    }
} 
Run Code Online (Sandbox Code Playgroud)