【问题标题】:iPad App running slow and after some time crashediPad App运行缓慢,一段时间后崩溃
【发布时间】:2014-09-28 02:07:44
【问题描述】:

在一个按钮上点击大约 60 多次我的 iPad 应用程序变慢,然后最终崩溃

在启动时,我的应用程序运行良好,一段时间后它变得越来越慢......

响应动作大约需要8到10秒,最后崩溃 我不知道为什么这一切会发生。 我在 uiviewcontroller 上使用了一个集合视图,它的所有内容视图都是在自定义单元类中创建的。

使用 Instruments 在 iPad 2 上进行测试后,拍摄了这些屏幕截图。

现在我该怎么办.??你看到这里有什么问题吗...???

这是 UICollectionView 单元格中的加号按钮,用于执行操作,按钮点击时间随机增加,达到 300000+ 毫秒

注意--->主线程显示0.0毫秒

这是什么意思....????

请指引我正确的方向

编辑

这是我在点击按钮时在这些方法中所做的代码

 // 'purchasedProduct' is NSMutuableDictionary    

-(void)btnPlus:(id)sender event:(id)event
{
    indexPaths   = [[NSMutableArray alloc]init];        
    NSSet *touches = [event allTouches];
    UITouch *touch = [touches anyObject];
    CGPoint currentTouchPosition = [touch locationInView:myCollection];
    btnIndex = [myCollection indexPathForItemAtPoint: currentTouchPosition];
    MyCell *cell = (MyCell*)[myCollection cellForItemAtIndexPath:btnIndex];
    NSString *newCode = [productID objectAtIndex:btnIndex.row];
    newQty = cell.cellQty.text;
    newQty = [NSString stringWithFormat:@"%d",[newQty integerValue] + 1];
    BOOL currentCellHasUpdates = purchasedProduct[newCode] != nil;
    if (currentCellHasUpdates)
    {
        // Object Already Exist.......
        [purchasedProduct setObject:newQty  forKey:newCode];
        prdID = newCode;
        [self UpdateProduct];
    }
    else
    {
        // create new object........
        [purchasedProduct setObject:newQty  forKey:newCode];
        prdID = newCode;
        [self BuyProduct];
    }
    [indexPaths addObject:btnIndex];
    [myCollection reloadItemsAtIndexPaths:indexPaths]; // I think problem is here.
}

CellForItemAtIndexPath 方法是

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
MyCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"CellID" forIndexPath:indexPath];
[[cell cellBtnPlus] addTarget:self action:@selector( btnPlus:event:) forControlEvents:UIControlEventTouchUpInside];

[[cell cellBtnMinus] addTarget:self action:@selector(btnMinus:event:) forControlEvents:UIControlEventTouchUpInside];

BOOL currentCellHasUpdates = purchasedProduct[[productID objectAtIndex:indexPath.row]] != nil;
if (currentCellHasUpdates)
{
    cell.cellQty.text = [purchasedProduct objectForKey: [productID objectAtIndex:indexPath.row]];
    cell.cellQty.textColor = [UIColor blueColor];
}
else
{
    cell.cellQty.textColor = [UIColor lightGrayColor];
    cell.cellQty.text = @"0";
}

image = nil;    
NSString *imageName =[[productID objectAtIndex:indexPath.row] stringByAppendingString:@".jpg"];
getImagePath = [documentsDirectory stringByAppendingPathComponent:imageName];
image = [UIImage imageWithData:[NSData dataWithContentsOfFile:getImagePath]];

if (image.size.width == image.size.height)
{
    //set newSize
}
if (image.size.width < image.size.height)
{
    //calculate newSize
}
else
{
    //calculate newSize
}

UIGraphicsBeginImageContext( newSize );
[image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
if (image.size.width == image.size.height)
{
    //set imgRect
}
if (image.size.width < image.size.height)
{
    //Calculations for imgRect
}
if (image.size.width > image.size.height)
{
    //Calculations for imgRect
}
cell.myImageView.frame = imgRect;
cell.myImageView.image = image;

cell.lbl1.text = [[desc objectAtIndex:indexPath.row]  capitalizedString];
cell.lbl2.text = [@"Box Qty:" stringByAppendingString:[boxQty objectAtIndex:indexPath.row]];
cell.lbl4.text = [[code objectAtIndex:indexPath.row] uppercaseString];
cell.lbl5.text = [@"Pack Qty:" stringByAppendingString:[packQty objectAtIndex:indexPath.row]];

BOOL isStarProduct = starProducts[[productID objectAtIndex:indexPath.row]] != nil;
if (isStarProduct)
{
    cell.lbl6.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"star.png"]];
}
else
{
    cell.lbl6.backgroundColor = [UIColor clearColor];
}

return cell;
}

我认为问题在于 CollectionView 中特定索引路径上的重新加载 导致问题,因为它需要 91% 的时间来执行。我说的对吗..???

编辑 2

-(void)BuyProduct
{
newDate = [NSDate date];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
currentDate = [formatter stringFromDate:newDate];

database = [dBName UTF8String];
if (sqlite3_open(database, &dbConnection) == SQLITE_OK)
{
    sqliteQuery = [NSString stringWithFormat:@"INSERT INTO ShopingCart"
                   " (fld1 , fld2, fld3, fld4, fld5, fld6, fld7, fld8, fld9, fld10, fld11, fld12, fld13, fld14, fld15, fld16)"
                   " VALUES (\'%d\', \"%@\", \'%d\', \"%@\", \"%@\", \"%@\", \"%f\", \"%f\", \"%@\", \"%@\","
                   " \"%@\", \"%f\", \"%.4f\", \'%d\', \'%d\', \'%d\')",
                   1,
                   currentDate,
                   91,
                   customerID,
                   [productID objectAtIndex:btnIndex.row],
                   [costPrice objectAtIndex:btnIndex.row],
                   [[price objectAtIndex:btnIndex.row] floatValue] * [newQty integerValue],
                   [[salePrice objectAtIndex:btnIndex.row] floatValue] * [newQty integerValue],
                   [qty objectAtIndex:btnIndex.row],
                   newQty,
                   [taxRate objectAtIndex:btnIndex.row],
                   [[price objectAtIndex:btnIndex.row] floatValue] * [newQty integerValue],
                   [[price objectAtIndex:btnIndex.row] floatValue] * [newQty integerValue] + [taxAmount floatValue],
                   0,
                   0,
                   0];
    if (sqlite3_prepare_v2(dbConnection, [sqliteQuery UTF8String], -1, &sQLStatement, NULL) == SQLITE_OK)
    {
        if (sqlite3_step(sQLStatement) == SQLITE_DONE)
        {
            NSLog(@"Inserted into Shopping Cart");
        }
        sqlite3_finalize(sQLStatement);
    }

    sqliteQuery = [NSString stringWithFormat:@"SELECT SUM(NetPrice) As TotalAmount"
                   " FROM ShopingCart WHERE CustomerID = \'%@\'",customerID];
    if (sqlite3_prepare_v2(dbConnection, [sqliteQuery UTF8String], -1, &sQLStatement, NULL) == SQLITE_OK)
    {
        if (sqlite3_step(sQLStatement) == SQLITE_ROW)
        {
            temp = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(sQLStatement, 0)];
        }
        sqlite3_finalize(sQLStatement);
    }
    ordAmount.text = [NSString stringWithFormat:@"Total £ %.2f",[temp floatValue]];
}
else
{
    NSLog(@"Error %s ", sqlite3_errmsg(dbConnection));
}
sqlite3_close(dbConnection);
}

我应该/可以在哪里改进我的代码?

【问题讨论】:

  • 您的问题是 cellForRow 方法。包括该代码。 Instruments 明确强调它是主要问题
  • @DanielGalasko 你的意思是 cellForItemAtIndexPath 方法??
  • @DanielGalasko 请检查此方法。我已经更新了帖子
  • 好的,我正在发布答案,不会太久
  • 完成,如果有帮助请告诉我

标签: ios iphone ipad profiling instruments


【解决方案1】:

您的仪器清楚地表明reloadItemsAtIndexPaths: 是导致性能不佳的罪魁祸首。这意味着集合视图需要很长时间才能使单元格出列。

看看你的 cellForRowAtIndexPath: 方法,你的 for 循环似乎是一个有吸引力的选择,因为你的性能受到了影响。对于每个单元格,您正在迭代一系列产品,希望满足以下条件

if ([[productID objectAtIndex:indexPath.row] isEqualToString:[updatedCodes objectAtIndex:i]])
{
    cell.cellQty.text = [updatedQty objectAtIndex:i];
    cell.cellQty.textColor = [UIColor blueColor];
}

一旦此条件为真,您将更新单元格,但您并没有跳出循环!

这意味着对于每个单元格,您都在遍历整个数组!您应该做的是在 if 中放置一个 break 语句以跳出循环。

话虽如此,我认为我们可以变得更好。鉴于您有一个 productID 数组和一个 updatedCodes 数组,您确实应该考虑将它们与 Dictionary 进行匹配。字典是通过特定键获取对象的好方法。将数组视为字典,其中键是数组索引。因此,当您在索引 0 处获取对象时,实际上是在获取键为 0 的对象。因此,在您的示例中,productID 自然可能是您的键,值将是产品。如果您知道其对应的 productID,这将允许您立即获取任何产品。

因此,您需要一本包含所有产品的字典。然后,您将需要一个包含所有更新代码的字典。您可以让集合视图显示字典中的所有产品(NSDictionary 具有方法“allValues”),或者您可以拥有一个产品数组。

所以你需要的基本概念是:

  1. 在集合视图中显示的所有产品的数组(我们称之为 productsArray)。
  2. 由 productID 键入的产品字典 - NSDictionary *productsKeyedByID = [NSDictionary dictionaryWitObjects:productsArray forKeys:[productsArray valueForKey:@"productID"]];\\where productID is a property on the product object
  3. 更新产品的字典。这将在项目更新时进行修改。

这种方法背后的动机是允许您根据特定的 productID 快速访问产品或更新的产品。这意味着对于数组中的每一个产品,您都可以立即检查它是否存在于更新后的产品字典中。

所以在你的 cellForRowAtIndexPath: 你可以有

MyCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"CellID" forIndexPath:indexPath];
[[cell cellBtnPlus] addTarget:self action:@selector( btnPlus:event:) forControlEvents:UIControlEventTouchUpInside];

[[cell cellBtnMinus] addTarget:self action:@selector(btnMinus:event:) forControlEvents:UIControlEventTouchUpInside];

NSString *currentCellProductId = productsArray[indexPath.row];
//using the modern syntax - you could also use the objectForKey: method.
BOOL currentCellHasUpdates = updatedCodesKeyedByID[currentCellProductId] != nil;
if (currentCellHasUpdates) {
    cell.cellQty.text = updatedCodesKeyedByID[currentCellProductId].quantity;
    cell.cellQty.textColor = [UIColor blueColor];
} else {
    cell.cellQty.textColor = [UIColor lightGrayColor];
    cell.cellQty.text = @"0";
}

现在,在您处理按钮点击时,您还可以利用字典的强大功能:

-(void)btnPlus:(id)sender event:(id)event
{
    NSSet *touches = [event allTouches];
    UITouch *touch = [touches anyObject];
    CGPoint currentTouchPosition = [touch locationInView:myCollection];
    btnIndex = [myCollection indexPathForItemAtPoint: currentTouchPosition];
    MyCell *cell = (MyCell*)[myCollection cellForItemAtIndexPath:btnIndex];
    NSString *newCode = [productID objectAtIndex:btnIndex.row];
    newQty = cell.cellQty.text;
    if (updatedCodesKeyedByID[newCode] != nil)
    {
        //NSLog(@"Object Already Exist...");
         id codeToUpdate = updatedCodesKeyedByID[newCode];
         codeToUpdate.quantity++;
         [self UpdateProduct]; // Open DB SQlite DB Connection and update Table Record and Close Connection.
      } 
      else
      {
           updatedCodesKeyedByID[newCode] = //create new object
           [self BuyProduct]; // Open DB SQlite DB Connection and update Table Record and Close Connection.
      }
 } 

注意您可能需要考虑更改您的设计,使其只有一个包含产品的字典,并且每个产品都有一个关联的数量。未设置时默认为 0。这样你只需要检查一个字典,它似乎完成了你在这篇文章中所做的事情;即更新商品的数量。

编辑将此讨论移至聊天室后,难题的最后一部分是在 performBatchUpdates 块内调用 UICollectionView 的 reloadItemsAtIndexPaths:。

【讨论】:

  • 我已经用字典替换了数组。在模拟器 Time Profiler 中进行测试,但它看起来就像在随机增加时间之前一样。我现在没有我的 iPad,所以在模拟器中测试。
  • 如果你注释掉你的图像代码?好像你正在做很多复杂的图像绘制。您是否删除了单元格中的 for 循环?
  • 我也不确定你在 UpdateProduct 和 SellProduct 方法中做了什么
  • 在这两种方法中,我只是打开 SQlite3 数据库连接并更新数据库记录,然后最后关闭数据库连接。
  • 我希望你异步地进行这个调用......另外,你应该考虑熟悉 Core Data。它的 Apples 内置对象持久性框架,非常适合存储和检索对象(由 SQL 支持)。再次,在 cellForRow 中取出您的图像代码,看看是否有影响
猜你喜欢
  • 1970-01-01
  • 2019-01-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-08
  • 1970-01-01
  • 2019-03-12
  • 1970-01-01
相关资源
最近更新 更多