zoukankan      html  css  js  c++  java
  • iOS常见bug

    前言:

    对这两年修复的bug做了个简单的总结。

    一、常见crash名词解释

    SIGSEGV

    一般情况下,SIGSEGV是由于内存地址不合法造成。因为无效的内存访问导致的,一般是指针指向不存在的地址所导致(Invalid memory reference);

    SIGBUS

    一般情况下,SIGBUS是因为内存地址没有对齐导致。

    因为总线出错(bus error)。地址一般是先校验地址对齐再校验其他的,校验地址对齐后会放入数据总线,这时有问题就会报SIGBUS的错误。

    SIGABRT

    异常终止条件,例如abort()。

    二、常见crash/bug分类整理

    1、3、6是修复crash过程中常遇到的crash类型。

    1.   crashName=NSInvalidArgumentException ,crashReason=data parameter is nil

    (1) [aMutableDictionary setObject:nil forKey:]; object can not be nil.
         [__NSDictionaryM removeObjectForKey:nil]: key cannot be nil
     (2) [aString hasSuffix:nil];  nil argument crash.
        [aString hasPrefix:nil];  nil argument crash.
     (3) aString = [NSMutableString stringWithString:nil];nil argument crash.
         aString = [[NSString alloc] initWithString:nil]; nil argument crash.
     (4) aURL = [NSURL fileURLWithPath:nil]; nil argument crash.
     (5) [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];data is nil crash
    (6) [aMutableArray addObject:nil], 添加nil crash。
    (7) UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
         pasteboard.string = nil;// setString参数nil则crash8if(controller.presentedViewController != nil) {
            [controller dismissViewControllerAnimated:NO completion:^{
                //controller.presentedViewController为nil则presentViewController:方法会crash         
                [controller presentViewController:self.imagePickerController animated:YES completion:nil]; 
            }];
        }else{
            [controller presentViewController:self.imagePickerController animated:YES completion:nil];
        }

    2.  野指针

           (1)  self.dataArr = (NSMutableArray *)mopayConsumeOrderList.list;

             [self.dataArr removeAllObjects];

             其中mopayConsumeOrderList.list是NSArray * 类型,

              运行后出现程序崩溃,原因是self.dataArr和mopayConsumeOrderList.list指向同一段内存,self.dataArr在运行多次之后mopayConsumeOrderList.list指向空时self.dataArr也变成野指针。

            改成:self.dataArr 自己new一个,即:

            self.dataArr = [NSMutableArray  arrayWithArray:self.mopayConsumeOrderList.list];

          (2)属性的内存管理语义设置的_unsafe_unretain,出现野指针;

          (3) ios9.0之前Notification未注销带来的僵尸对象问题。

    3.  数据类型错误带来的unrecognized selector crash

         例1:常见业务调用时传参数应为NSString,业务方传入的数据为Dictionary,崩溃.

         例2:  网络层中将NSData转换成Model,返回的数据和移动之家上定义的数据不一致,带来的崩溃。

         例3:  push 接收到的消息是string类型,当做NSNumber类型处理。

    4. 数组取值越界crash

    5. 关注新技术

     (1)2017年6月开始苹果热修复crash

     (2)ios10以后push使用UNUserNotificationCenter注册push,否则ios10、ios11都有可能crash。

     (3)ios10等如果没在plist里设置key-value描述语,否则相机崩溃(蓝牙、日历、麦克风、相机、相册、通讯录等)

            <key>NSPhotoLibraryUsageDescription</key>
             <string>APP需要您的允许,才能访问相册</string>

    6. 数据竞争带来的crash (SIGSEGV)

       例1:NSMutableDictionary类型用objectForKey取数据和setValueforKey:设置数据,有可能存在数据竞争,需要做保护。

      例2: NSMutableArray存在同样的问题。遍历数组期间,不要对数组进行remove和insert操作,因为数组有保护机制,容易崩溃。可以先拷贝一份出来,对拷贝的数组遍历,对真正需要改变的数组操作。

    7. selector不存在带来的crash (SIGSEGV)

     [self  respondsToSelector:@sel(aSel)]的判断,否则有可能带来的unrecognized selector crash。

    例1.   [toggle addTarget:target action:selector forControlEvents:UIControlEventValueChanged];  // selector是否存在

    例2.   [self performSelector:@selector(aSelector)];   // aSelector是否存在

    8.  存储类型必须是对象类型

               [self.dic setObject:[NSArray arrayWithArray: self.mpShopArr] forKey:@"shopArr"];

     [[NSUserDefaults standardUserDefaults] setObject:self.dic  forKey:[JVAccountManager sharedAccount].edper];崩溃

    由于self.mpShopArr里存的数据结构包含了很多类型,其中一个数据类型是int,不是object,导致第一句没报错、但第二句就报错了。

    最终存到[NSUserDefaults standardUserDefaults] 里的数据类型,无论包装多少层,所有的内容必须是对象类型。

    9. 内存泄漏(bug)

    (1)单例+RACObserve带来内存泄漏。

         [RACObserve(self.viewModel, modulesShouldRefresh) subscribeNext:^(NSNumber * modulesShouldRefresh) {

            @strongify(self);

            if ([modulesShouldRefresh boolValue] == YES) {

                [self setShowLoading:NO];

                [self.highFrequencyTitleView requestHeadInfoForce:@(YES)];

                [self refreshModules:YES];

            }  }];

          由于viewModel设成了不恰当的单例,对其RACObserve导致self(homeVC)不能释放,也就是当切换账号的时候,原来的homeVC没释放,新的homeVC又创建了一个,导致下拉刷新时,原来的没有释放的homeVC触发了接口,现在的homeVC也触发了接口,导致接口请求多次。所以,单例不能随便乱用,除非可以确定对象与APP同生共死生命周期没有问题。

    (2)performSelector: withObject: afterDelay: 必须有合适的时机cancelPreviousPerformRequestsWithTarget:selector:object:,否则会导致内存泄漏。

             可以用weak类型的dispatch_after避免内存泄漏:

                       __weak CRMHomeViewModel *weakSelf = self;

                        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

                            [weakSelf requestLiveData:YES];

                        });

    (3)    NSTimer会保留其目标对象,如果不加以注意,就会持有保留环,造成内存泄露。

            scheduledTimerWithTimeInterval: target:self selector: userInfo: repeats:,重复模式的计时器,必须手动调用[_aTimer invalidate]方法,才能令其停止。

        self持有timer,timer的target又是self, timer不释放,导致self也不能调用dealloc,所以timer调用invalidate的时机也不好把控。ios10以后,使用block来打破保留环,定义一个弱引用,令其指向self,然后使块捕获这个引用,而不直接去捕获普通的self变量。也就是说,self不会为计时器所保留。当块开始执行时,立刻生成strong引用,以保证实例在执行期间持续存活:

    __weak SLVTimerTestViewController *weakSelf = self; 
    _aTimer = [NSTimer scheduledTimerWithTimeInterval:5.0 repeats:YES block:^(NSTimer* timer){ 
      SLVTimerTestViewController *strongSelf = weakSelf;  
      [strongSelf timer_invoke]; 
    }];

    三、crash防护

    参考《Baymax:网易iOS App运行时Crash自动防护实践》,并进行了一些调研和实践,对以下5种常见crash进行了分析,并写出了对应的防护代码。写好的代码已经过初步测试。前进的一小步,记录下来。对应的4篇博客:

    1.  [crash详解与防护] unrecognized selector crash
    2.  [crash详解与防护] Container类型的crash和NSString类型的crash

    3.  [crash详解与防护] NSTimer crash

    4.  [crash详解与防护] NSNotification crash
  • 相关阅读:
    Effective Java 第三版——26. 不要使用原始类型
    Effective Java 第三版——25. 将源文件限制为单个顶级类
    Effective Java 第三版——24. 优先考虑静态成员类
    Effective Java 第三版——23. 优先使用类层次而不是标签类
    Effective Java 第三版——22. 接口仅用来定义类型
    Effective Java 第三版——21. 为后代设计接口
    Effective Java 第三版——20. 接口优于抽象类
    Effective Java 第三版——19. 如果使用继承则设计,并文档说明,否则不该使用
    Effective Java 第三版——18. 组合优于继承
    Effective Java 第三版——17. 最小化可变性
  • 原文地址:https://www.cnblogs.com/Xylophone/p/7429373.html
Copyright © 2011-2022 走看看