UIScrollView _delegateScrollViewAnimationEnd 引起的崩溃处理
来源:互联网 发布:淘宝正品阿迪达斯店 编辑:程序博客网 时间:2024/05/08 08:20
我们先看一下崩溃堆栈:
0x01c9709f libobjc.A.dylib`objc_msgSend + 19 0x00c3656b UIKit`-[UIScrollView(UIScrollViewInternal) _delegateScrollViewAnimationEnded] + 62 0x00c3665a UIKit`-[UIScrollView(UIScrollViewInternal) _scrollViewAnimationEnded:finished:] + 149 0x00c366da UIKit`-[UIScrollView(UIScrollViewInternal) animator:stopAnimation:fraction:] + 62 0x00ca1a50 UIKit`-[UIAnimator stopAnimation:] + 519 0x00ca2120 UIKit`-[UIAnimator(Static) _advanceAnimationsOfType:withTimestamp:] + 385 0x00ca1c58 UIKit`-[UIAnimator(Static) _LCDHeartbeatCallback:] + 67 0x003562d2 QuartzCore`CA::Display::DisplayLink::dispatch(unsigned long long, unsigned long long) + 110 0x0035675f QuartzCore`CA::Display::TimerDisplayLink::callback(__CFRunLoopTimer*, void*) + 161 0x01e7c376 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 22 0x01e7be06 CoreFoundation`__CFRunLoopDoTimer + 534 0x01e63a82 CoreFoundation`__CFRunLoopRun + 1810 0x01e62f44 CoreFoundation`CFRunLoopRunSpecific + 276 0x01e62e1b CoreFoundation`CFRunLoopRunInMode + 123 0x02a157e3 GraphicsServices`GSEventRunModal + 88 0x02a15668 GraphicsServices`GSEventRun + 104 0x00bc9ffc UIKit`UIApplicationMain + 1211 0x00002c6d ZHCRM`main(argc=1, argv=0xbffff3cc) + 141 at main.m:16 0x00002b95 ZHCRM`start + 53
崩溃的主要原因是因为 scrollview.delegate 在没做完滚动动画的时候释放了, 野指针调用导致。
stackoverflow 上有人讨论过这个问题点击查看。
网上一般的解决方法都是确保处理scrollview delegate 消息的对象在释放前把scrollview.delegate设置为nil。
@implement MyTableViewController- (void)dealloc { self.tableview.delegate = nil;}
这种方法可以解决崩溃, 但是不够收敛,需要改动的地方很多, 而且也不能确保新的开发人员不会再引起。
接下来给大家介绍一种统一的处理方式。
主要思路:
添加一个DelegateWrapper包装对象,确保UIScrollView在释放前,uiscrollview.delegate 不为nil
对象关系就会变成这样
UITableView -> DelegateWrapper -> DelegateHandler(ViewController)
生命周期管理
然后我们通过 weak 指针可以解决 DelegateHandler的释放问题。
@interface DelegateWrapper : NSObject@property (weak, nonatomic) id delegate;@end
有了这个对象之后, 设置给 UIScrollerView.delegate 就变成我们的包装对象了。
- (void)setDelegate:(id)delegate{ DelegateWrapper *wrapper = [DelegateWrapper new]; wrapper.delegate = delegate; scrollerview.delegate = wrapper; ...}
通过动态绑定技术,可以很方便的把DelegateWrapper对象嵌入到scrollview 里面,来控制 wrapper 的生命周期。
消息转发
接下来Delegate 还要处理scrollview 发过来的delegate 消息,把它转到真正的delegate对象上。
- (BOOL)respondsToSelector:(SEL)aSelector{ if (!_delegate) { NSLog(@"call selector:%s", sel_getName(aSelector)); return NO; } return [_delegate respondsToSelector:aSelector];}- (id)forwardingTargetForSelector:(SEL)aSelector{ if (_delegate) { return _delegate; } return [super forwardingTargetForSelector:aSelector];}
好了, 现在有了这些消息转发机制后, 真正的delegate handler可以收到消息了。
调用收敛
现在基本的流程和原理都介绍完了, 接下来的事情就是怎样在一个地方处理了, 以后就都不会有这个问题。
入口点 [UIScrollView setDelegate:]
根据Object C 语言的动态性,使用MethodSwizzing 技术可以很好的把 [UIScrollView setDelegate:] 的方法替换成我们的。这样只要在程序初始化后调用一下,以后就不用担心了。
+ (void)hookMethedClass:(Class)class hookSEL:(SEL)hookSEL originalSEL:(SEL)originalSEL myselfSEL:(SEL)mySelfSEL{ Method hookMethod = class_getInstanceMethod(class, hookSEL); Method mySelfMethod = class_getInstanceMethod([MethodsHooker class], mySelfSEL); IMP hookMethodIMP = method_getImplementation(hookMethod); class_addMethod(class, originalSEL, hookMethodIMP, method_getTypeEncoding(hookMethod)); IMP hookMethodMySelfIMP = method_getImplementation(mySelfMethod); class_replaceMethod(class, hookSEL, hookMethodMySelfIMP, method_getTypeEncoding(hookMethod));}+ (void)hookUIScrollViewSetDelegate{ [MethodsHooker hookMethedClass:NSClassFromString(@"UIScrollView") hookSEL:@selector(setDelegate:) originalSEL:@selector(originalScrollViewSetDelegate:) myselfSEL:@selector(myselfScrollViewSetDelegate:)];}- (void)myselfScrollViewSetDelegate:(id)delegate{ WeakDelegate *weakDelegateObject = [[WeakDelegate alloc] init]; weakDelegateObject.delegate = delegate; objc_setAssociatedObject(self, "weak_delegate_handler", weakDelegateObject, OBJC_ASSOCIATION_RETAIN); [self originalScrollViewSetDelegate:weakDelegateObject];}- (void)originalScrollViewSetDelegate:(id)delegate{}
现在只要在didFinishLunch 后调用一下 hookUIScrollViewSetDelegate 这个方法就可以了。
- UIScrollView _delegateScrollViewAnimationEnd 引起的崩溃处理
- Yes,搞定---关于CreateRemoteThread引起对象进程崩溃的处理
- QSqlQuery::next() 引起的崩溃
- 一起 select 引起的崩溃
- 指针引起的崩溃分析
- Android资源引起的程序崩溃
- Android资源引起的程序崩溃
- 格式化输出引起的程序崩溃
- 虫趣:除0引起的崩溃
- A2W引起的栈溢出崩溃
- 空指针引起的程序崩溃
- 格式化输出引起的程序崩溃
- class_create 和 device_create 引起的内核崩溃
- debug : 日志函数引起的崩溃
- Android Lolipop AssetAtlasService引起的系统崩溃
- 如何解决ChemDraw引起的系统崩溃
- 数据库-FMDB误用引起的崩溃问题
- 解决TextView引起的莫名崩溃
- 海选女主角
- “Network”游戏棋人机对弈的设计与实现(二)—判断步骤是否合法
- 一个简单的JSP测试页面
- 终结者:详解Nginx(一)
- spring security3的filter链
- UIScrollView _delegateScrollViewAnimationEnd 引起的崩溃处理
- 动态规划算法Dynamic Programming
- Java偏向锁
- 一个开源的页面传输压缩算法
- opencv+VS+X64/win32切换
- GRE数学题巧解
- 【C/C++学院】0730-网站以及后门/结构体对齐、结构体面试分析/深拷贝与浅拷贝/队列/字符串封装
- JS 枚举相关方法
- SAT文章阅读模拟一篇