iOS:bug list(一)

------对于某些view(tableview、pickView)控件,界面没有数据显示,判断下它的数据来源,如果是来自datasourc/delegate,一定要给datasourc/delegate赋值

------if(枚举量){……} 。把枚举量直接当成了判断条件,想当条件为该枚举量时,执行后面语句;可导致结果是if语句中代码总是执行或不执行。可能是枚举变量名字跟常量名字易混淆原因,此处思路已混乱,应该是用枚举变量做判断条件,并且是 (枚举变量 == 枚举量)。

------自定义控制器属性的setter方法,利用它控制 xib中view的初始界面,有时不好使。因为当控制器alloc,init后,立刻对它的属性进行赋值,这个时候控制器的view还没创建,一些对view的修改操作都是无效的。

------数据库存某列存储的是时间戳,值为一段时间差距,表示当天几点。结果显示出来时间不对。分析:其实表中存储的是一段相对时间,如果要显示出当天几点,可以用GMT时间0点加上它,变成一个绝对时间,然后NSDateFormatter显示的时候时区必须设置为GMT。或者用本地当天零点对应的时间戳转加上他,NSDateFormatter默认显示即可。

------受实际业务影响,if判断条件考虑不周全,漏掉另一种情况。

BOOL haveLunch;
    lunchTime = employeeSchedule.LunchBeginTime;
    haveLunch = NO;
    for (int j = 1; j < 6; j++) {
        breakBeginTime = [[employeeSchedule valueForKeyPath:[NSString stringWithFormat:@"Break%dBeginTime",j]]doubleValue];
        if (!haveLunch) {
            if (lunchTime < breakBeginTime) {
                // 插入lunch 记录
                haveLunch = YES;
            }
        }
        // 此处插入 break 记录
    }

     因为lunch一般在break之间,所以插入lunch记录的时候,只考虑lunch在break中间情况。当lunch在最后,就会出现缺少lunch记录的bug。一写到if语句,一定要考虑到所有情况,为真、为假、以及一些边界/特殊情况

------程序只是做了个简单的tableview demo,每次向上滑动tableview 时程序都会crash掉,报EXC_BAD_ACCESS错误。网上查到原因:

    ViewController *viewController = [[ViewController alloc]initWithNibName:@"VC" bundle:nil];
    [self.window addSubview:viewController.view];
    //[self.window setRootViewController:viewController];
    [self.window makeKeyAndVisible];
    return YES;

之前addSubview只是retain了TableViewController 的View而没有retain整个controller,所以在执行完- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions函数后,tableViewController就被释放掉了。而我在向上滑动tableview 的时候,tableview会向tableViewController发送重画tablecell的消息,因为tableViewController已经不存在,所以导致程序crash掉了。改成setRootViewController:viewController即可。

------字符串排序,比较的字符串跟最终显示的字符串略有不同,漏掉空格。导致排序不正确

  NSString *name1 = [obj1.FirstName stringByAppendingString:[NSString stringWithFormat:@" %@",obj1.LastName]];
  NSString *name2 = [obj2.FirstName stringByAppendingString:[NSString stringWithFormat:@" %@",obj2.LastName]];
  //名字显示为"Some Body",错误写法 [obj1.FirstName stringByAppendingString:obj1.LastName] 将导致名字为"SomeBody".
  return [name1 compare:name2 options:NSCaseInsensitiveSearch];

------误区:自定义视图的时候重写initwithframe方法,因为一些视图大小根据内容而定,所有写单例或者某些调用的地方都是用CGRectZero去创建,所以它的大小和父视图大小都是Zero,在自定义视图的后续操作中,要调节自定义视图的大小时,还想着调节superview的大小?还认为superview大小是Zero。其实在使用自定义视图的时候,只要它被addSubview到一个视图上,它的superview就是某个特定视图,大小已经改变。superview大小要从俩者同时考虑。其实正常思路是superview根据子视图内容,确定自己大小,然后子视图随着动态调整。

------自定义UITabelviewCell,其中cell的内容完全由xib定制,创建cell = [[[NSBundle mainBundle]loadNibNamed:@"BQFoodInfoCell" owner:self options:nil]lastObject];时候。错误:

1.xib中Placeholders的file‘s owner不用写成XXCell,然后将它和IBOutlet连接,这里完全用不上file's owner,只需要在xib的top-level里面创建XXCell,然后用它和IBOutlet连接。

2.创建cell的时候,owner:参数为nil,因为owner参数对应xib中file's owner,而xib中file's owner 没用上。

------在写自动适应的UIView.frame时候,根据效果图各控件所占像素X和总像素的比,确定控件位置,例如#define kWidth 200/640,在使用kWidth的时候,不起作用,值总为0,原因,200/640 = 0,它俩都是整数,写成200/640.0就好了.==>对于常数,整数写成n形式,浮点数必须写成n.m形式,即使m是0;

------UItableview的重用机制,初始化工作/属性设置分配:当reload tableview时候,注意它的重用机制(参考《tableview 使用》),一些控件的添加如果写在“cell alloc之外”,则每次reload都会添加一遍该控件“cell alloc”里面对于某个cell可能不会执行

------在一个控制器中,UItableview中有几个cell样式一样,所以用相同的xib创建cell,同时他们用的identifier也一样:cell样式一样,可以用同样的xib,但是别用同样的identifier,因为同样的identifier,它们重用的时候,可能cell1,重用了之前的cell2,有可能导致问题。

------遍历一个数组时候,例如for (int i = 0; i < count; i++),如果在for循环体里面有NSString *objc = [arr objectAtIndex:i]; + [arr removeObject:obj]操作,可能会导致程序crash。分析:在循环体里如果有移除操作,记得count -- 并且i-- ,否则会漏掉一个对象。当然,正常情况下,数组也是不能这样做遍历删除的

------遍历一个数组时候使用for(a in b)的方式,如果循环体里也有[b removeObject:a]的操作,也会出问题。类似上个问题,改写成for(int i = 0;i……)形式

------手写set方法的时候,为了提高效率,在方法前面做了判断if(_intanceVari == paraVari) return; 导致有时候界面不刷新(例如对一个cell的data属性赋值,当data某个属性改变时,reload tableview时候,setData方法直接return了,导致cell的界面还是没有改变)。

------setter/getter带来的问题:原来思路是通过一个dic,将不同配送点的配送时间保存起来,但是出现了,类似“锁”这种情况,

 - (NSDictionary *)deliveryDayHourDic{
    //  地址修改了,还能找到原来的dic;
    int shopID = [BQSetting sharedInstance].deliveryShopID;
    return _deliveryDayHourStoreDic[@(shopID)];
}
//  某下载方法中
self.deliveryDayHourDic = @{ @"day":dayArr,@"hour":timeArr };
[_deliveryDayHourStoreDic setObject:self.deliveryDayHourDic forKey:@(shopID)];
      解决办法,deliveryDayHourDic改成readonly 属性,下载方法中,用一个临时字典去坐setObject的参数。

------if(self == [super init])问题:个人书写习惯,一碰到if条件的书写,里面总是写上“==”。注意在写自定义初始化方法init等方法中,if条件判断是一个赋值表达式

------实现viewWillAppear等方法时候,总忘记写super viewWillAppear 方法,这样会导致一些基类一些操作无法进行

------init方法中死循环问题:A类比较通用,写为单例,A类中某个成员B类需要监听A的某个属性,以便实时刷新界面。在A的初始化方法中,创建并初始化B类,同时在B类的初始化方法中调用了kvo监听方法[A addObserver:B forKeyPath:@"property" options:NSKeyValueObservingOptionNew context:nil],其中A参数为它的单例。这样会造成死循环,导致程序一直不响应,直到几秒后崩溃避免在某子类的初始化方法中,调用其“父类”的单例,涉及到了“父类”的初始化,容易引起死循环解决办法:将kvo监听方法写在A的初始化方法中[self addObserver:B forKeyPath:@"property" options:NSKeyValueObservingOptionNew context:nil]。

------属性的定位:在修改地址的控制器中,属性A判断控制器是新建地址,还是更新地址,决定了界面的更新和接口访问,但在一个地址新加保存后,属性A没有及时转变,导致后面继续保持该地址更新出错。常见的属性作用有,(1)决定类的一些行为,外观,一般外部传入,在类生命周期中不变;(2)还有一些是决定类的状态,在类的生命周期中变来变去,经常用来判断等。该事例中,属性兼有(1)(2)的作用,即决定类的行为,也用来做一些判断,需要变换

------关于使用数组/字典某key做存储及判断问题(1)若使用数组A做存储,需要存什么就把数组赋给A,有没有存储的判断条件是if(nil != A);若使用字典做存储,需要存什么就把对象set给key,有没有存储的判断条件是if(nil != [dic objectForKey:x]);但它们俩个使用的前提条件不一样-->字典A不需要创建并初始化,字典必须要创建并初始化,否则会导致数组A判断条件不好使,或者字典无法存储对象

------关于动画导致的赋值不对问题:当动画触发事件非常频繁的时候,有可能出现这种情况:进入该方法,开始动画,准备动画结束赋值A1,这时事件又被触发,进入该方法正在动画,赋值1处将值改为A2;之后动画结束后,//赋值2又将值改为A1。实际最终显示应该为A2。解决办法:赋值与动画分割开来,进入该方法,即赋值,之后开始执行动画效果。

    if (_isAnimating) {
        //赋值1
        return;
    }
    _isAnimating=YES;
    [UIView animateWithDuration:0.2 animations:^{
        // 动画      
    } completion:^(BOOL finished) {
        // 赋值2
        _isAnimating=NO;
    }];
------关于cell复用编辑的数据丢失的bug:在某个tableview中,某个cell显示初始数据,之后对cell某些属性编辑后,滑动tableview,导致某cell出屏幕,复用回来的cell数据恢复初始数据;
     BQEditAddressCell *cell = [tableView dequeueReusableCellWithIdentifier:kEditAddressCellIdentifier];
    if (nil == cell) {
        cell = [[[NSBundle mainBundle]loadNibNamed:@"BQEditAddressCell" owner:self options:nil]lastObject];
        [tableview registerNib:[UINib nibWithNibName:@"BQEditAddressCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:kEditAddressCellIdentifier];
    }
    cell.selectionStyle = UITableViewCellSelectionStyleNone;
    cell.displayLab.text = [_itemArray objectAtIndex:indexPath.row];
    cell.contentField.delegate = self;
    switch (indexPath.row) {
        case 0:
            if (_addNewAddress) {
                cell.contentField.placeholder = kEditCellPlaceHolderName;
            }else{
                if (0 == [cell.contentField.text length]) { // 首次           //line:s
                   cell.contentField.text = _userAddress.acceptName;
                }
                
            }
            cell.contentField.keyboardType = UIKeyboardTypeDefault;
            break;

          改动1:在方法首次定义一个static BOOL firstTime = YES;在line:s处判断firstTime的值,并且首次赋值后修改firstTime = NO;错误:static的生存周期(堆)跟控制器的生存周期(栈)没关系,导致以后进入该控制器,cell的初始数据都显示不出来

          改动2:用cell上属性值判断,为初始值肯定是cell第一次加载,不为初始值则是复用回来。正确

          改动3:tableview滑动之后,cell复用回来,还是没有保存原来的编辑值,因为上述例子tableviewCell这种复用方式,导致第一个cell的identify没有,第一个cell没法复用,所有滑动后创建了一个新的cell。将registerNib方法移到viewDidLoad等方法中

          改动4(特殊情况,适用页面cell比较少,限制cell从页面上端滑出多个):上述例子中所有的cell共用一个identify,但必须得保证不同cell的text不混淆。所有复用的时候,可以只允许一个cell滑动消失--->复写scrollViewDidScroll

- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    if (scrollView.contentOffset.y > _tableview.rowHeight *1.5) {
        scrollView.contentOffset = CGPointMake(scrollView.contentOffset.x,_tableview.rowHeight *1.5);
    }
}

------关于cell reloadData之后,值无法清楚/丢失情况:在cellForRowAtIndexPath的方法中,if条件的else一定要写,因为cell可以复用,所有if+else这段代码处理的有可能是“初始的cell” 也可能是“带数据的cell”,所以一定要写else 情况,要不然就会出现一些与预期不一样的情况。

if (0 < [self.couponTextField  length]) {
    cell.XX.text = self.couponTextField;
}else{
    // do nothing
}


你可能感兴趣的:(iOS:bug list(一))