文档章节

iOS 降低线上版本Crash率

我爱水果
 我爱水果
发布于 2017/08/22 17:03
字数 721
阅读 41
收藏 0
ios

IOS 防止Crash 组件WTSafeGuard

##背景 由于Object-C本身的不安全性,导致很容易产生Crash。在这些Crash,很多我们可以利用自定义手段,进行避免。这样可以降低线上版本的Crash率,提升用户 体验。WTSafeGuard 避免APP Crash 组件,目前能做到的还很有限。

UIKit Called on Non-Main Thread

UIKit不是线程安全的,执行UIKit操作如果不在主线程很可能造成程序Crash。所以我们对Hook,UIView 的setNeedsLayout,layoutIfNeeded,layoutSubviews,setNeedsUpdateConstraints方法。如果执行以上函数没有在主队列,通过强行将执行代码,在主队列执行。

- (void)wt_safe_setNeedsLayout
{
    if(![NSThread isMainThread]){
        dispatch_async(dispatch_get_main_queue(), ^{
            NSAssert(false, @"wt_safe_setNeedsLayout failed");
            [self wt_safe_setNeedsLayout];
        });
    }else{
        [self wt_safe_setNeedsLayout];
    }
}

- (void)wt_safe_layoutIfNeeded
{
    if(![NSThread isMainThread]){
        dispatch_async(dispatch_get_main_queue(), ^{
            NSAssert(false, @"wt_safe_layoutIfNeeded failed");
            [self wt_safe_layoutIfNeeded];
        });
    }else{
        [self wt_safe_layoutIfNeeded];
    }
}

- (void)wt_safe_layoutSubviews
{
    if(![NSThread isMainThread]){
        dispatch_async(dispatch_get_main_queue(), ^{
            NSAssert(false, @"wt_safe_layoutSubviews failed");
            [self wt_safe_layoutSubviews];
        });
    }else{
        [self wt_safe_layoutSubviews];
    }
}

- (void)wt_safe_setNeedsUpdateConstraints
{
    if(![NSThread isMainThread]){
        dispatch_async(dispatch_get_main_queue(), ^{
            NSAssert(false, @"wt_safe_setNeedsUpdateConstraints failed");
            [self wt_safe_setNeedsUpdateConstraints];
        });
    }else{
        [self wt_safe_setNeedsUpdateConstraints];
    }
}

##避免 Foundation 类Carsh ###NSString

+ (instancetype)stringWithUTF8String:(const char *)bytes
- (instancetype)initWithString:(NSString *)aString
- (instancetype)initWithUTF8String:(const char *)nullTerminatedCString
- (instancetype)initWithFormat:(NSString *)format locale:(id)locale arguments:(va_list)argList
- (NSString *)stringByAppendingString:(NSString *)aString
- (unichar)characterAtIndex:(NSUInteger)index
- (void)getCharacters:(unichar *)buffer range:(NSRange)range
- (NSRange)rangeOfCharacterFromSet:(NSCharacterSet  *)searchSet 
                            options:(NSStringCompareOptions)mask
                              range:(NSRange)searchRange
                                    
- (NSRange)rangeOfString:(NSString *)searchString
                options:(NSStringCompareOptions)mask
                  range:(NSRange)searchRange
                 locale:(NSLocale *)locale
- (NSString *)substringFromIndex:(NSUInteger)from
- (NSString *)substringWithRange:(NSRange)range
- (NSString *)substringToIndex:(NSUInteger)to
- (void)getLineStart:(NSUInteger *)startPtr   
                 end:(NSUInteger *)lineEndPtr                                         
                 contentsEnd:(NSUInteger *)contentsEndPtr
                   forRange:(NSRange)range

NSAttributedString

hook 方法:对传入参数range 进行check,如果range有问题,直接返回nil

- (NSAttributedString *)attributedSubstringFromRange:(NSRange)range;

NSFileManager

- (nullable NSDirectoryEnumerator<NSURL *> *)enumeratorAtURL:(NSURL *)url includingPropertiesForKeys:(nullable NSArray<NSURLResourceKey> *)keys options:(NSDirectoryEnumerationOptions)mask errorHandler:(nullable BOOL (^)(NSURL *url, NSError *error))handler

###NSIndexPath

- (void)getIndexes:(NSUInteger *)indexes range:(NSRange)positionRang

###NSJSONSerialization

+ (NSData *)dataWithJSONObject:(id)obj options:(NSJSONWritingOptions)opt error:(NSError **)error

NSDictionary

hook 方法:

+ (id)sharedKeySetForKeys:(NSArray<KeyType <NSCopying>> *)keys
- (instancetype)initWithObjects:(const ObjectType _Nonnull [_Nullable])objects forKeys:(const KeyType <NSCopying> _Nonnull [_Nullable])keys
- (instancetype)initWithObjects:(const ObjectType _Nonnull [_Nullable])objects forKeys:(const KeyType <NSCopying> _Nonnull [_Nullable])keys count:(NSUInteger)cnt

NSMutableDictionary

hook 方法:

+ (NSMutableDictionary<KeyType, ObjectType> *)dictionaryWithSharedKeySet:(id)keyset
- (void)setObject:(ObjectType)anObject forKey:(KeyType <NSCopying>)aKey;
- (void)removeObjectForKey:(KeyType)aKey;

###NSSet

- (instancetype)WT_initWithObjects:(const id [])objects count:(NSUInteger)cnt
- (void)addObject:(id)object;
- (void)makeObjectsPerformSelector:(SEL)aSelector
- (void)makeObjectsPerformSelector:(SEL)aSelector
                        withObject:(id)argument

###NSMutableSet

- (void)addObject:(id)anObject

###NSMutableString

- (void)setString:(NSString *)aString
- (void)appendString:(NSString *)aString
- (void)deleteCharactersInRange:(NSRange)range
- (void)insertString:(NSString *)aString atIndex:(NSUInteger)loc
- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)aString
-  (NSUInteger)replaceOccurrencesOfString:(NSString *)target
                                     withString:(NSString *)replacement
                                        options:(NSStringCompareOptions)options
                                          range:(NSRange)searchRange

###NSURL

+ (NSURL *)fileURLWithPath:(NSString *)path
+ (NSURL *)fileURLWithPath:(NSString *)path isDirectory:(BOOL)isDir
+ (NSURL *)fileURLWithPathComponents:(NSArray<NSString *> *)components
+ (NSURL *)fileURLWithPath:(NSString *)path
                      isDirectory:(BOOL)isDir
                    relativeToURL:(NSURL *)baseURL
- (instancetype)initWithString:(NSString *)URLString relativeToURL:(NSURL *)baseURL
- (instancetype)initFileURLWithPath:(NSString *)path
- (instancetype)initFileURLWithPath:(NSString *)path
                             relativeToURL:(NSURL *)baseURL
- (instancetype)initFileURLWithPath:(NSString *)path
                               isDirectory:(BOOL)isDir
                             relativeToURL:(NSURL *)baseURL
  1. KVO
  2. 容器越界(NSArray, NSDictionary,...)
  3. unrecognized selector crash (这个很多时候是由于class使用错误导致)
  4. NSTimer 导致crash

KVO Crash

项目中KVO crash 占比很高, 主要原因为,添加删除不对称导致。 解决方法为,添加Map进行缓存。 不过这个方案,目前还有缺陷。

unrecognized selector crash

这个就比较简单了,直接上代码:

    [NSObject jr_swizzleMethod:@selector(forwardingTargetForSelector:) withMethod:@selector(WT_safeForwardingTargetForSelector:) error:&error];
    
    - (id)WT_safeForwardingTargetForSelector:(SEL)aSelector
{
    NSMethodSignature *signature = [self methodSignatureForSelector:aSelector];
    if ([self respondsToSelector:aSelector] || signature) {
        return [self WT_safeForwardingTargetForSelector:aSelector];
    }
    
    return [WTSafeGuard createFakeForwardTargetObject:self selector:aSelector];
}

© 著作权归作者所有

我爱水果
粉丝 0
博文 19
码字总数 30442
作品 0
杭州
私信 提问
100%移植阿里云移动测试技术_竟仅需1周?!——移动测试专有云(3)——内容详解

一、自动化测试服务 Android兼容性测试 Android兼容性测试旨在帮助解决Android应用在不同真机机型上的各类兼容性问题,包括 Crash/ANR分析、6项性能分析、UI检测、3个版本的覆盖安装检测等。...

乐乎无趣
2017/11/02
5
0
iOS开发笔记(九)— 数据库、Crash、内存问题分析

前言 分享iOS开发中遇到的问题,和相关的一些思考,本次内容包括:UIKit的iOS11问题、数据库问题定位、线上Crash处理、内存问题分析。 正文 1、iOS 11的UITabbar的高度异常 问题描述:iOS 1...

落影loyinglin
2018/08/26
0
0
获取iOS设备上崩溃日志(Crash Log)的方法

获取iOS设备上崩溃日志(Crash Log)的方法:http://www.weste.net/2013/5-20/91509.html iOS怎么分析AppStore上面的Crash Log:http://m.blog.csdn.net/blog/tianjf0514/45201549 iOS Cra......

智能小松鼠
2015/08/25
1K
0
React Native VS Flutter评测

React Native VS Flutter评测 编辑于 11:34

纪洪波
2018/06/27
0
0
DiDi-opensource/DoraemonKit

DoraemonKit /'dɔ:ra:'emɔn/,简称,中文名 ,意味着能够像哆啦A梦一样提供给他的主人各种各样的工具。

DiDi-opensource
02/18
0
0

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周四乱弹 —— 当你简历注水但还是找到了工作

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @花间小酌 :#今日歌曲推荐# 分享成龙的单曲《男儿当自强》。 《男儿当自强》- 成龙 手机党少年们想听歌,请使劲儿戳(这里) @hxg2016 :刚在...

小小编辑
今天
2.9K
22
靠写代码赚钱的一些门路

作者 @mezod 译者 @josephchang10 如今,通过自己的代码去赚钱变得越来越简单,不过对很多人来说依然还是很难,因为他们不知道有哪些门路。 今天给大家分享一个精彩的 GitHub 库,这个库整理...

高级农民工
昨天
5
0
用好项目管理工具,人人都可以成为项目经理

现在市面上的项目管理工具越来越多了,但是大多数都是一些协同工具或轻量项目管理工具。如果是多团队、跨部门使用或者企业级的项目管理,从管理思想到工具运用,需要适应企业的业务流程体系,...

cs平台
昨天
12
0
只需一步,在Spring Boot中统一Restful API返回值格式与统一处理异常

统一返回值 在前后端分离大行其道的今天,有一个统一的返回值格式不仅能使我们的接口看起来更漂亮,而且还可以使前端可以统一处理很多东西,避免很多问题的产生。 比较通用的返回值格式如下:...

晓月寒丶
昨天
69
0
区块链应用到供应链上的好处和实际案例

区块链可以解决供应链中的很多问题,例如记录以及追踪产品。那么使用区块链应用到各产品供应链上到底有什么好处?猎头悬赏平台解优人才网小编给大家做个简单的分享: 使用区块链的最突出的优...

猎头悬赏平台
昨天
32
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部