iOS上的函数/方法级别分析

时间:2016-03-10 11:51:32

标签: ios performance profiling

有没有解决方案在iOS上使用最近的Xcode(7.x)进行功能/方法级别的分析?

我的意思是等同于GCC's -pg,它使编译器将性能记录到您的代码中。当Apple仍在使用GCC时,我确实在iOS上成功使用了-pggprof,但似乎没有与clang等效的内容。

我需要找出谁在某种情况下微阻挡我的主线程(应用程序仍然会做出反应,但阻塞会使滚动抖动)。遗憾的是,Instruments的工具无法胜任任务:取样(停止应用程序并记录堆栈跟踪)对于我的需求来说太粗糙/不精确。

所以我需要一种方法来找出哪个方法在什么时间和多长时间运行,正好。唯一真正的方法是让编译器注入必要的代码(这是-pg正在做的事情),或者是对Objective-C运行时的一些创造性的黑客攻击。有没有解决方案?

1 个答案:

答案 0 :(得分:0)

@MikeDunlavey关于设置计时器的建议对我来说是正确的解决方案。真正的计时器在我的情况下不起作用,所以我使用了具有高优先级的后台线程和NSCondition(像pthread_cond_timedwait这样的低级机制也可以工作)。每次runloop迭代时,都会触发条件。如果主线程花费的时间太长,则条件将中止。

这是我用过的非常粗糙的代码。由于我的问题是在非常繁忙的创业公司,这足以让我发现我的问题;如果你想在“普通”运行时使用它来检测问题,你需要增强它以处理runloop的等待部分。

但是,我无法解决这个解决方案的所有问题;滚动仍然有点紧张,但它比以前更好。

- (void)startWatchdog
{
    static NSDate * lastIteration;
    static BOOL hasHandled = NO;
    NSCondition * condition = [[NSCondition alloc] init];

    lastIteration = [NSDate date];

    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(NULL,
        kCFRunLoopBeforeTimers | kCFRunLoopBeforeWaiting, true, 0,
        ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
            NSDate * now = [NSDate date];

            if (activity == kCFRunLoopBeforeTimers) {
                if (!hasHandled) {
                    [condition lock];
                    [condition signal];
                    [condition unlock];
                }

                lastIteration = now;
                hasHandled = NO;

            } else {
                // About to wait for events. We might want tell the watchdog method
                // that it's OK that there's going to be a timeout.
                [condition lock];
                [condition signal];
                [condition unlock];
                hasHandled = YES;
            }
        }
    );

    NSThread * watchdog = [[NSThread alloc] initWithTarget:self selector:@selector(watchdog:) object:condition];
    [watchdog setThreadPriority:1];
    [watchdog start];

    CFRunLoopRef runloop = [[NSRunLoop currentRunLoop] getCFRunLoop];
    CFRunLoopAddObserver(runloop, observer, kCFRunLoopCommonModes);
    CFRunLoopAddObserver(runloop, observer, (CFStringRef)UITrackingRunLoopMode);
}

- (void)watchdog:(NSCondition *)condition
{
    @autoreleasepool {
        while (1) {
            BOOL wasHandled;
            NSDate * start = [NSDate date];

            [condition lock];
            wasHandled = [condition waitUntilDate:[start dateByAddingTimeInterval:0.2]];
            [condition unlock];

            if (!wasHandled) {
                NSLog(@"-- Timeout: %f", [[NSDate date] timeIntervalSinceDate:start]);
            }
        }
    }
}