Как UIView конвертирует точки между представлениями?

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

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

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

Ответ 1

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

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

Вот результаты (для 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

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

Опять же, есть, вероятно, несколько обстоятельств, когда это будет иметь заметное влияние на программу, но что-то нужно иметь в виду, если вам приходится много повторять вызовы 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 sub-sub-subviews, каждый раз выбирая другой, в случае, если происходит некоторое кеширование, и единственное различие, вероятно, можно было бы учитывать при дополнительных издержках поиска массива.

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