优雅解决 iOS 8 UIScrollView delegate EXC_BAD_ACCESS

栏目: IOS · 发布时间: 6年前

内容简介:从近期 Crash 上报记录来看,有相当一部分 EXC_BAD_ACCESS 和 UIScrollView 有关,而且都是在 iOS 8 上才会出现,通过排查发现是因为在 iOS 8 的 UIScrollView 的 delegate 是 assign 属性,在某些场景下 scrollView 的 delegate 被释放而 scrollView 继续访问其 delegate 则会出现 EXC_BAD_ACCESS 的问题。解决的方法也很简单,在 viewController 的 dealloc 方法将对

背景

从近期 Crash 上报记录来看,有相当一部分 EXC_BAD_ACCESS 和 UIScrollView 有关,而且都是在 iOS 8 上才会出现,通过排查发现是因为在 iOS 8 的 UIScrollView 的 delegate 是 assign 属性,在某些场景下 scrollView 的 delegate 被释放而 scrollView 继续访问其 delegate 则会出现 EXC_BAD_ACCESS 的问题。

解决的方法也很简单,在 viewController 的 dealloc 方法将对应的 scrollView 方法置空即可

- (void)dealloc {
    self.scrollView.delegate = nil;
}

但是由于项目历史原因,要逐一排查并修改成本比较大,利用 hook 来解决这个问题的想法油然而生,基本思路是当一个对象成为 UIScrollView 的 delegate 时,去监听这个 delegate 对象的释放,一旦 delegate 对象被释放,则手动把 scrollView 的 delegate 设置为 nil (有点像我们手动实现 weak)

DelegateCleaner

由于 hook 对象的 -(void)dealloc 方法比较复杂,并且有一定的危险性,这里采用了一种宿主的方法,利用 objc_setAssociatedObject 给 delegate 添加一个 DelegateCleaner 对象,这个对象有以下功能:

1、记录把该对象设置为 delegate 的 所有 scrollView

2、当对象被释放的时候,对其 delegate 是该对象的 scrollView 调用 scrollView.delegate = nil

因为 AssociatedObject 机制,当 delegate dealloc 的时候,DelegateCleaner 对象也会释放,调用 -(void)dealloc 方法,所以我们可以实现以上功能。

ReleaseDelegateCleaner 用到了 NSPointerArray 来记录 scrollView(功能等同于 weak 数组) , 这样一来当 scrollView 被释放掉,我们就无需要去对其 delegate 置 nil。

另外在对 scrollView 的 delegate 置 nil 的时候,是利用设置 Ivar 的直接方式,避免和其他第三方库 hook setDelegate 的方法造成冲突。

ReleaseDelegateCleaner 的具体实现如下:

@interface ReleaseDelegateCleaner : NSObject
@property (nonatomic, strong) NSPointerArray *scrollViews;
@end

@implementation ReleaseDelegateCleaner

- (void)dealloc {
    [self cleanScrollViewsDelegate];
}

- (void)cleanScrollViewsDelegate {
    [self.scrollViews.allObjects enumerateObjectsUsingBlock:^(UIScrollView *scrollView, NSUInteger idx, BOOL * _Nonnull stop) {
        object_setIvarValue(scrollView, "_delegate", nil);
    }];
}

- (void)recordDelegatedScrollView:(UIScrollView *)scrollView {
    NSUInteger index = [self.scrollViews.allObjects indexOfObject:scrollView];
    if (index == NSNotFound) {
        [self.scrollViews addPointer:(__bridge void *)(scrollView)];
    }
}

- (void)removeDelegatedScrollView:(UIScrollView *)scrollView {
    NSUInteger index = [self.scrollViews.allObjects indexOfObject:scrollView];
    if (index != NSNotFound) {
        [self.scrollViews removePointerAtIndex:index];
    }
}

- (void)setScrollViews:(NSMutableSet *)scrollViews {
    objc_setAssociatedObject(self, @selector(scrollViews), scrollViews, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSPointerArray *)scrollViews {
    NSPointerArray *scrollViews = objc_getAssociatedObject(self, _cmd);
    if (!scrollViews) {
        scrollViews = [NSPointerArray weakObjectsPointerArray];
        [self setScrollViews:scrollViews];
    }
    return scrollViews;
}

为 delegate 关联 DelegateCleaner 对象

我们要找一个时机对 scrollview 的 delegate 进行 AssociatedObject 关联,这个很容易想到就是 setDelegate: 方法,另外当 delegate 置 nil 的时候,需要判断当前的 delegate 是否记录了 scrollView,这里一样直接取 Ivar 从而避免触发 scrollView 的 getter 方法。

@implementation UIScrollView (iOS8Safe)

+ (void)load {
    if (IOS_VERSION < 9.0) {
        SwizzleMethod([UIScrollView class], @selector(setDelegate:), @selector(safe_setDelegate:));
    }
}

- (void)safe_setDelegate:(id<UIScrollViewDelegate>)delegate {
        if (delegate) {
            [[(NSObject *)delegate iOS8DelegateCleaner] recordDelegatedScrollView:self];
        } else {
            id _delegate = object_getIvarValue(self, "_delegate");
            [[(NSObject *)_delegate iOS8DelegateCleaner] removeDelegatedScrollView:self];
        }
    [self safe_setDelegate:delegate];
}

@end

除此之外,利用这一方法也可以对 UICollectionView 和 UITableView 的 setDataSource 做防护处理,完整的源码详见:

GitHub : UIScrollView-iOS8Safe


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Web Data Mining

Web Data Mining

Bing Liu / Springer / 2006-12-28 / USD 59.95

Web mining aims to discover useful information and knowledge from the Web hyperlink structure, page contents, and usage data. Although Web mining uses many conventional data mining techniques, it is n......一起来看看 《Web Data Mining》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具