简体   繁体   English

iOS CPU使用率飙升

[英]iOS CPU usage skyrockets here

I have an issue with my app where it spikes up to over 100% CPU usage when gathering data. 我的应用存在问题,在收集数据时,它的CPU使用率会飙升至100%以上。 I have done everything I can think of to reign in the processing so that it doesn't lock the device up, and still continues to get the data that's needed. 我已经尽我所能在处理中发挥作用,以使它不会锁定设备,并且仍然继续获取所需的数据。

Our customers have some big databases, so downloading the entire database is out the question. 我们的客户有一些大型数据库,因此下载整个数据库是不可能的。 We are also using REMObjects as middleware, so I have to work with that. 我们还使用REMObjects作为中间件,因此我必须使用它。 What I did was figure out what data was needed for the user, and setup a series of calls to gather that information. 我所做的就是弄清楚用户需要什么数据,并设置了一系列调用来收集该信息。 I think that where my problem lies is in the fact that the database can only process up to 1500 items in a call. 我认为我的问题出在哪里,因为数据库在一个呼叫中最多只能处理1500个项目。

Here is a sample query string that is getting sent to the server. 这是发送到服务器的示例查询字符串。

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)

That string is created by the following method, which then sends it asynchronously to the server. 该字符串由以下方法创建,然后将其异步发送到服务器。 There is one issue in this method that I know is a problem, but I haven't had the time to circle back to it and devise a better solution. 我知道这种方法存在一个问题,但是我还没有时间来讨论这个问题并提出更好的解决方案。 I am using respondsToSelector and performSelector to process additional methods based on the table that we're gathering details from. 我正在使用responsesToSelector和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];
    }
}

Any help here will be greatly appreciated! 在这里的任何帮助将不胜感激! Thanks for taking the time to look. 感谢您抽出宝贵的时间查看。

Yes. 是。 Basically the golden rule is: do not optimize prematurely . 基本上,黄金法则是: 不要过早优化

But anyhow. 但是无论如何。 My first guess would be: replace NSString query with NSMutableString query . 我的第一个猜测是:将NSString query替换为NSMutableString query Because you are creating 1500 NSString objects on the heap with always increasing length, just to throw them away in the next loop. 因为您要在堆上创建1500个NSString对象,并且长度总是增加,所以只需要在下一个循环中将它们丢弃即可。 An NSMutalbeString keeps up the memory for a much longer time when appending - and you are always talking to the same object. 追加时,NSMutalbeString将内存保留更长的时间-并且您始终在与同一个对象通信。 '(Then use appendFormat without the re-assignment instead of stringByAppendingFormat with assignment!) “(然后使用appendFormat没有重新分配,而不是stringByAppendingFormat分配对象!)

Look here: Is NSMutableString's -appendString: method an efficient way to build up a large string? 看这里: NSMutableString的-appendString:方法是构建大字符串的有效方法吗?

Having your CPU climb to over 100% is not necessarily a bad thing. 使您的CPU爬升到100%以上不一定是一件坏事。 On a device with multiple cores (iPhone 4s and later) the CPU utilization is 100% times the number of cores. 在具有多个核心的设备(iPhone 4s和更高版本)上,CPU利用率是核心数量的100%倍。 So on a 4s max is 200%, and on a 5s it's 400%. 因此,在4秒钟内最大值为200%,在5秒钟内最大值为400%。

Doing a bunch of processing in a loop maximizes CPU usage on that thread, since the code runs at full speed until it's done. 循环执行一堆处理可最大程度地提高该线程上的CPU使用率,因为代码会以全速运行,直到完成为止。 That's normal and appropriate. 这是正常且适当的。

Are you seeing laggy UI performance? 您是否看到过时的UI性能? That's what you should use as your gauge that you need to improve things. 这就是您需要用来改进的标准。

My suggestion would be to rewrite your code to run on a GCD background queue. 我的建议是重写您的代码以在GCD后台队列上运行。 First try default priority (the same priority as the UI.) On a multi-core machine, that will tie up one of the cores. 首先尝试使用默认优先级(与UI相同的优先级)。在多核计算机上,这将占用其中一个内核。 On an iPhone 4, though, it might make the UI laggy. 但是,在iPhone 4上,这可能会使UI变得迟钝。 In that case you could switch to the next-lower priority, which would make it take longer, but give a higher priority to the UI. 在这种情况下,您可以切换到下一个较低的优先级,这将花费更长的时间,但为UI赋予了更高的优先级。

You could then optimize your code if needed. 然后,您可以根据需要优化代码。 User defaults is not the most efficient way to handle state data in a loop. 用户默认值不是在循环中处理状态数据的最有效方法。 You might try removing the user defaults calls and switch to saving data in an instance variable or, in a data container singleton if you need to pass info between objects. 您可以尝试删除用户默认调用,并切换到将数据保存到实例变量中,或者如果需要在对象之间传递信息,则保存到数据容器单例中。 Also NSNotificationCenter has more overhead than delegate calls, block calls, or simple method calls. 同样,NSNotificationCenter比委托调用,块调用或简单方法调用具有更多的开销。

However, I would not worry about those things until you determine that optimization is needed. 但是,在您确定需要优化之前,我不会担心这些事情。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM