iOS CPU使用率在这里猛增

时间:2013-12-08 16:41:50

标签: ios objective-c loops xcode5 cpu-usage

我的应用程序存在问题,在收集数据时CPU占用率超过100%。我已经完成了所有我能想到的在处理过程中所做的事情,这样它就不会锁定设备,并且仍然可以继续获取所需的数据。

我们的客户有一些大型数据库,因此下载整个数据库是不可能的。我们也使用REMObjects作为中间件,所以我必须使用它。我所做的是弄清楚用户需要什么数据,并设置一系列调用来收集这些信息。我认为我的问题在于数据库在一次通话中最多只能处理1500个项目。

以下是发送到服务器的示例查询字符串。

SELECT COMMUNICATIONID, PHONEEXTENTION, PHONEDESC, PHONETIMETOCALL, PHONENUMBER 
FROM PHONE WHERE COMMUNICATIONID IN (3761, 3793, 5530, 4957, 4320, 1914, 3715, 6199, 5548,
5580, 5994, 5867, 1437, 4943, 6217, 3765, 2442, 227, 4084, 977, 6822, 5680, 263, 4502, 
327, 6112, 136, 7053, 5571, 6958, 6799, 5525, 6530, 4779, 604, 2182, 6198, 3792, 6071, 
4383, 5866, 7444, 1309, 226, 4083, 5916, 1295, 626, 1249, 1950, 2141, 3369, 326, 135, 
6780, 5411, 5938, 4424, 6034, 649, 6179, 5861, 4778, 5479, 2181, 6197, 3791, 5815, 6070, 
6420, 7935, 4542, 4319, 6679, 4942, 4082, 4974, 5533, 5788, 5597, 976, 3764, 1917, 6202, 
134, 6779, 3768, 5410, 5665, 7880, 7052, 6033, 5492, 6815, 3118, 4218, 5110, 6529, 6115, 
6069, 348, 4318, 4382, 1498, 6406, 4941, 7443, 2376, 4623, 5755, 5532, 6201, 6392, 625, 
7270, 4977, 6396, 6524, 5664, 7051, 725, 6032, 6701, 6160, 5491, 5937, 6273, 1875, 6114, 
5477, 6528, 5573, 4936, 6705, 2180, 3758, 5527, 5368, 5814, 7328, 7424, 429, 5991, 1434, 
6391, 6200, 7283, 5868, 5900, 228, 4085, 6109, 1106, 5791, 692, 6095, 7210, 2893, 1188, 
6814, 4217, 5572, 3757, 5813, 3694, 796, 605, 6486, 128, 4144, 5722, 5754, 1915, 5676, 
5549, 5581, 4976, 5917, 5822, 2174, 6158, 1633, 4566, 5267, 4885, 4503, 1874, 6113, 5476, 
4425, 4871, 5526, 6531, 7886, 1496, 5194, 127, 4780, 5721)

该字符串由以下方法创建,然后将其异步发送到服务器。我知道这个方法存在一个问题,但我没有时间回过头来设计一个更好的解决方案。我正在使用respondsToSelector和performSelector来处理基于我们从中收集详细信息的表的其他方法。

- (void)processRequest
{
    if( requestQueue.count == 0 )
        return;

    if( processingQueue.count > 3 )
        return;

    Request *request = requestQueue[0];
    [requestQueue removeObjectAtIndex:0];
    DADataTable *source = request.source;
    NSString *destTableName = request.destTableName;
    NSString *sourceKey = request.sourceKey;
    NSString *query = request.query;
    NSArray *destKeys = request.destKeys;
    NSString *originMethodName = request.originMethodName;
    NSArray *destinationMethods = request.destinationMethods;
    NSString *message = request.loadingMessage;

    [[NSNotificationCenter defaultCenter] postNotificationName:@"GATHERINGDATA" object:nil];


    // Cycle through the rows in the source table and extract the keys we need.
    // originMethodName is needed because some tables require additional checks
    // to determine what kind of key we are working with
    // sourceKey is the string that holds the key we're looking for, which is
    // used on tables that don't need specific filtering
    NSSet *set = [self getSourceSet:source originMethodName:originMethodName sourceKey:sourceKey];

    // getLists takes the set generated by getSourceSet and converts the list of
    // ids into a comma separated list of items suitable for passing into a query
    // Currently there is a 1400 item limit per list to keep from exceeding the server
    // limit, which is currently 1500
    NSArray *lists = [self getLists:set];

    NSString *msg = @"Loading Data";
    NSLog(@"%@", message);
    for( NSString *tList in lists ) {
        if( tList.length == 0 ) {
             NSLog(@"%@ not needed", originMethodName);
             continue;
        }

        query = [query stringByAppendingFormat:@"%@)", tList];

        NSLog(@"%@: %@", destTableName, query);
        DAAsyncRequest __block *r = [fda beginGetDataTableWithSQL:query withBlock:^(DADataTable *table){
             DADataTable *destination = [tables valueForKey:destTableName];
             if( tables.count == 0 ) destination = table;
             else if( [destination rowCount] > 0 )
                //dispatch_async(queue, ^(){
                [destination mergeTable:table withPrimaryKeys:destKeys];
                //});

             else
                destination = table;

             [[NSUserDefaults standardUserDefaults] setValue:msg forKey:@"LoadingMessage"];
             [[NSNotificationCenter defaultCenter] postNotificationName:InitialViewControllerNotificationLoadingUpdate object:nil];
             [[NSNotificationCenter defaultCenter] postNotificationName:@"UpdateStatus" object:nil];

             //dispatch_async(queue, ^(){
             [tables setValue:destination forKey:destTableName];
             //});

             for( NSString *method in destinationMethods) {
                SEL tMethod = NSSelectorFromString(method);
                if( [self respondsToSelector:tMethod]) {
                    [self performSelector:tMethod withObject:table];
                }
             }


             if( [self busy] &&
                 [[source name] isEqualToString:DataAccessTableCustomer])
             {
                [[NSUserDefaults standardUserDefaults] setValue:nil forKey:@"FETCHINGJOB"];
                [[NSNotificationCenter defaultCenter] postNotificationName:@"JOBFETCHED" object:nil];
             }
             if( [[[NSUserDefaults standardUserDefaults] valueForKey:@"FETCHINGCONTACT"] isEqualToString:@"YES"] &&
                ([[source name] isEqualToString:DataAccessTablePerson] ||
                 [[source name] isEqualToString:DataAccessTableOrganization] ||
                 [[source name] isEqualToString:DataAccessTableOrganizationAddress]))
            {
                 [[NSUserDefaults standardUserDefaults] setValue:nil forKey:@"FETCHINGCONTACT"];
                 [[NSNotificationCenter defaultCenter] postNotificationName:@"CONTACTFETCHED" object:nil];
            }
            [processingQueue removeObject:r];
            [[NSNotificationCenter defaultCenter] postNotificationName:@"PROCESSREQUEST" object:nil];
        }];
        [processingQueue addObject:r];
    }
}

任何帮助都将非常感谢!感谢您抽出时间来寻找。

2 个答案:

答案 0 :(得分:2)

是。基本上黄金法则是:do not optimize prematurely

但无论如何。我的第一个猜测是:将NSString query替换为NSMutableString query。因为您在堆上创建了1500个NSString对象,并且总是增加长度,所以只需要在下一个循环中将它们丢弃。 NSMutalbeString在追加时会将内存保持更长的时间 - 而且您总是在与同一个对象进行通话。 '(然后使用appendFormat而无需重新分配,而不是使用stringByAppendingFormat进行分配!)

请看这里:Is NSMutableString's -appendString: method an efficient way to build up a large string?

答案 1 :(得分:1)

让CPU爬升到100%以上并不一定是件坏事。在具有多个内核(iPhone 4s和更高版本)的设备上,CPU利用率是内核数量的100%。所以4s最大值是200%,而5s则是400%。

在循环中进行一堆处理可以最大化该线程上的CPU使用率,因为代码会全速运行直到完成。这是正常和恰当的。

您是否看到了滞后的UI性能?这就是你应该用来衡量你需要改进的东西。

我的建议是重写代码以在GCD后台队列上运行。首先尝试默认优先级(与UI相同的优先级。)在多核计算机上,这将占用其中一个核心。但是,在iPhone 4上,它可能会使UI变得迟钝。在这种情况下,您可以切换到下一个较低的优先级,这将花费更长的时间,但为UI提供更高的优先级。

然后,您可以根据需要优化代码。用户默认值不是处理循环中状态数据的最有效方法。您可以尝试删除用户默认调用并切换到在实例变量中保存数据,或者如果需要在对象之间传递信息,则在数据容器单例中切换。此外,NSNotificationCenter比委托调用,块调用或简单方法调用具有更多开销。

但是,在您确定需要优化之前,我不会担心这些事情。