Как UIView конвертирует точки между представлениями? - PullRequest
12 голосов
/ 17 мая 2011

Кто-нибудь знает алгоритм, используемый методами convertPoint:toView: и convertPoint:fromView: UIView? В частности, знаете ли вы, преобразуют ли они сначала точку в координаты окна, а затем в систему координат целевого представления, или они могут использовать кратчайший путь, поскольку представление «из» является предком представления «в»?

Я спрашиваю, потому что у меня есть цикл, который проверяет точку на возможном большом количестве видов, и мне интересно, было бы более эффективно преобразовать эту точку в координаты окна один раз перед циклом (что также имеет преимущество не нужно передавать точку зрения в цикл и все, что она вызывает, зная, что я могу передать nil для аргумента fromView) или сохранять точку относительно родительского представления.

Разница может быть незначительной, но, поскольку она не имеет большого значения для моего кода, я предпочел бы работать с системой, чем с ней.

1 Ответ

13 голосов
/ 17 мая 2011

Ну, я не намного ближе к знанию алгоритма, но теперь у меня есть представление о том, что он делает , а не . Прошлой ночью у меня было немного времени, чтобы подготовить тестовую программу для сравнения методов, и результаты оказались довольно удивительными.

Самым быстрым методом было предварительное преобразование точки в координаты окна, а затем преобразование в каждое целевое представление. Что было удивительно, так это насколько быстрее это было: примерно в 25-27 раз быстрее! И вдобавок ко всему, выполнение двухэтапного преобразования каждый раз (сначала в координаты окна, а затем в отдельный вызов для преобразования в целевое представление) было все еще более чем в 10 раз быстрее, чем одиночный вызов convertPoint:fromView: с ноль посмотреть аргумент. Я действительно не могу объяснить это. Я мог бы ожидать, что один метод будет в два раза быстрее, но на самом деле не должно быть быстрее сделать два преобразования, чем сделать один напрямую! (Я также пробовал тесты в разных порядках, чтобы убедиться, что это не влияет на время).

Вот результаты (для 1 миллиона конверсий, запущенных на iPad 1, iOS 4.3.3):

convert from window point: 0.204297
two-step convert through window coordinates: 0.390832
convert from subview point: 5.020129

Я опубликую приведенный ниже код для проверки, но если эти результаты верны, я могу только предположить, что код преобразования высоко оптимизирован для преобразования в и из координат окна / экрана и использует некоторый (гораздо более медленный) метод преобразовывать напрямую между двумя системами координат представления, если задан аргумент, отличный от нуля. Возможно, есть условия, в которых несоответствие не так велико (например, когда происходят преобразования, не связанные с идентичностью), но для того, что я ожидаю, это общий случай, кажется, что было бы намного лучше сделать это -переходное преобразование сначала в координаты окна, а затем в другое представление.

Опять же, вероятно, есть несколько обстоятельств, когда это окажет заметное влияние на программу, но об этом следует помнить, если вам когда-либо придется совершать многократные вызовы convertPoint:toView: или convertPoint:fromView:.

Вот мой код, если кто-нибудь захочет проверить его на наличие ошибок или запустить его самостоятельно:

@implementation TestConvertPointViewController

- (NSTimeInterval) timeForBlock:(void (^)())block
{
    CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
    block();
    return CFAbsoluteTimeGetCurrent() - startTime;
}

- (void) millionTimes:(void (^)(NSUInteger))block
{
    for (NSUInteger i = 0; i < 1000000; i++) {
        block(i);
    }
}

- (void)loadView
{
    UIView* rootView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
    UIView* subview = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 250, 500)];
    UIView* subSubview = [[UIView alloc] initWithFrame:CGRectMake(20, 10, 130, 190)];
    UIView* subSubSubview = [[UIView alloc] initWithFrame:CGRectMake(50, 10, 50, 100)];
    [subSubview addSubview:subSubSubview];
    [subview addSubview:subSubview];
    [rootView addSubview:subview];
    self.view = rootView;
}

- (void)test
{
    UIView* subview = [[self.view subviews] objectAtIndex:0];
    UIView* subSubview = [[subview subviews] objectAtIndex:0];
    UIView* subSubSubview = [[subSubview subviews] objectAtIndex:0];

    CGPoint testPoint = CGPointMake(10.0, 30.0);
    NSTimeInterval time;

    time = [self timeForBlock:^{ [self millionTimes: ^(NSUInteger i){
        [subSubSubview convertPoint:testPoint fromView:nil];
    } ]; } ];
    NSLog(@"convert from window point: %f", time);

    time = [self timeForBlock:^{ [self millionTimes: ^(NSUInteger i){
        CGPoint rootPoint = [subview convertPoint:testPoint toView:nil];
        [subSubSubview convertPoint:rootPoint fromView:nil];
    } ]; } ];
    NSLog(@"two-step convert through window coordinates: %f", time);

    time = [self timeForBlock:^{ [self millionTimes: ^(NSUInteger i){
        [subSubSubview convertPoint:testPoint fromView:subview];
    } ]; } ];
    NSLog(@"convert from subview point: %f", time);
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self test];
}

@end

Я также протестировал его с 100 подподвидовами, каждый раз выбирая другое на случай, если произойдет какое-то кэширование, и единственное отличие, вероятно, может быть объяснено дополнительными издержками поиска в массиве.

Итак, я не уверен, что с этим делать, но я знаю, как я собираюсь использовать convertPoint:fromView: в будущем! : -)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...