iOS总结-最近遇到的问题及解决办法

最近在Bugly上发现线上APP存在不少崩溃问题,经过分析和定位,解决了几个比较棘手的问题,总结如下。

多线程问题

我们在APP中封装了一个记录业务日志的单例对象,主要代码如下:

+ (instancetype)shareManager
{
    static ATLogManager *manager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [[self alloc]init];
    });
    return manager;
}

- (FMDatabaseQueue *)logDBQueen
{
    if(!_logDBQueen){
        NSLog(@"创建_logDBQueen");
        NSString *docsDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
        NSString *logPath = [docsDir stringByAppendingPathComponent:@"ATLogs"];
        NSFileManager *fm =[NSFileManager defaultManager];
        NSError *error;
        if (![fm fileExistsAtPath:logPath]) {
            [fm createDirectoryAtPath:logPath withIntermediateDirectories:NO attributes:nil error:&error];
            //禁止iCloud备份
            NSURL *downloadsUrl = [NSURL fileURLWithPath:logPath];
            NSDictionary *dic = [downloadsUrl resourceValuesForKeys:@[NSURLIsExcludedFromBackupKey] error:nil];
            if(!dic || ![[dic objectForKey:NSURLIsExcludedFromBackupKey] boolValue]){
                [downloadsUrl setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:nil];
            }
        }
        NSString *dbPath = [logPath stringByAppendingPathComponent:@"ATLog.sqlite"];
        _logDBQueen = [FMDatabaseQueue databaseQueueWithPath:dbPath];
    }
    return _logDBQueen;
}

+ (BOOL)insertLogTableWithType:(NSString * )columnType moreHit:(NSString *)moreHit
{
    ATLogManager *manager = [ATLogManager shareManager];
    [manager createTable:kFindSelectColumnDate];
    
    //insert code
}

之前在用的时候都是在主线程中同步调用相关方法进行记录的,自从上个版本把部分触发比较频繁的记录改成异步的调用:

dispatch_async(dispatch_get_global_queue(0, 0), ^{
   [ATLogManager insertLogTableWithType:@"2" moreHit:@"1"];
});

之后发现线上出现了比较多的崩溃,崩溃在了[FMDatabaseQueue inDatabase:]方法中。

fmdb_crash_issue.png

起初以为是FMDatabaseQueue的问题或者我们用的FMDB的版本较低的问题。但是FMDatabaseQueue本身是线程安全的,在多线程中使用同一个FMDatabaseQueue对象是没有问题的。FMDB这个第三方库,这么多用户,用了这么多年,应该是不会存在这种问题的。程序崩溃在这里,而且是SEGV_ACCERR类型的崩溃,只能是在FMDatabaseQueue使用过程中被释放了造成的。

在我们封装的记录业务日志的单例对象中,有一个FMDatabaseQueue对象是采用懒加载的方式创建的,经过一番排查,发现这个FMDatabaseQueue对象竟然被创建了多次。这下子就豁然开朗了,在多线程并发的情况下,第一次创建的FMDatabaseQueue对象正在执行相关代码时,第二次紧接着调用了懒加载的方法创建了一个新的FMDatabaseQueue对象把第一个创建的给覆盖掉了,这时第一次创建的FMDatabaseQueue对象就被释放了,这个时候就存在很多不安全的隐患了,比如我们这里就是导致访问了一个不合法的内存地址。

解决办法就是把懒加载的方法给移除,单例创建时就把对应的FMDatabaseQueue对象给创建了,保证FMDatabaseQueue只会创建一个。

这个事情给我了一个比较大的教训,在使用单例的时候,一定要小心通过懒加载方式创建的属性,在多线程下一定要注意懒加载方法被调用多次。在单例的初始化方法中最好将它的属性都一并给初始化了,这样能有效保障线程安全。

preferredMaxLayoutWidth

在iOS10.2上某个页面总是点击进去就闪退,经过排查发现在计算某个UITableViewCell的高度时出现的crash,cell本身没有任何特别的,如果把cell上多行的UILabel给注释掉,就没有问题了。最后在UITableView-FDTemplateLayoutCell的issue278找到了答案:多行UILabel如果不设置preferredMaxLayoutWidth,走到fittingHeight = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;会在iOS10.2的机器上卡死奔溃,应该属于系统问题。

block

开发过程有个在多线程中处理数据时出现了数组越界的问题,经检查发现是我们犯了一个关于block的低级错误:

//不加__block打印i=0(捕获后外部的修改不影响内部使用);加__block打印i=1(外部的修改对block内部生效)
    for(__block NSInteger i = 0; i < 1; i++){
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                NSLog(@"i=%ld",i);
            });
        });
    }

UIWebSelectSinglePicker的crash问题

另一个数组越界问题,但是查看奔溃堆栈信息,发现内容是包含UIWebSelectSinglePicker、UIPickerView、UIPickerTableView等关键字,据此猜测是我们项目中嵌入的H5页面调用系统的选择视图引起的崩溃。查资料发现,已经有人遇到这种情况了,重现步骤:H5唤起系统的UIPickerView,没有数据源的情况下,上下滑动一下再点击确定选择。详情可参考这篇文章:UIWebSelectSinglePicker的crash问题

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,743评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,296评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,285评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,485评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,581评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,821评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,960评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,719评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,186评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,516评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,650评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,329评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,936评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,757评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,991评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,370评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,527评论 2 349

推荐阅读更多精彩内容

  • OC语言基础 1.类与对象 类方法 OC的类方法只有2种:静态方法和实例方法两种 在OC中,只要方法声明在@int...
    奇异果好补阅读 4,259评论 0 11
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,092评论 1 32
  • 面试题参考1 : 面试题[http://www.cocoachina.com/ios/20150803/12872...
    江河_ios阅读 1,721评论 0 4
  • 宁静致远_c785阅读 143评论 1 1
  • 你有没有过这样的感觉,好像有件什么重要的事没做,但就是想不起来,很焦虑。第二天,老板晨会的时候问你,你五雷轰顶,突...
    Tiego_Wang阅读 517评论 0 1