Цель C для оптимизации цикла с использованием SEL и IMP

Согласно этой статье Циклы в объекте C можно оптимизировать с помощью ВЫБОР И ИМП. Я немного поигрался с этой идеей, а сегодня попробовал несколько тестов. Однако то, что работает для одного класса, не работает для другого. Кроме того, хотелось бы знать, как именно происходит ускорение? Избегая objC_mesgSent?

Вопрос 1 Как это:

Cell *cell;
for (NSMutableArray *row in self.data) {
    for (Data *d in row){
        cell = [[Cell alloc] initWithRect:tmp_frame];
        [self addSubview:cell.view];
        [self.cells addObject:cell];

Хуже этого:

SEL addCellSel = @selector(addObject:);
IMP addCellImp = [self.cells methodForSelector:addCellSel];
Cell *cell;
for (NSMutableArray *row in self.data) {
    for (Data *d in row){
        cell = [[Cell alloc] initWithRect:tmp_frame];
        [self addSubview:cell.view];
        addCellImp(self.cells,addCellSel,cell);

Вопрос 2. Почему это не удается? (Обратите внимание: self - это класс, унаследованный от UIView)

SEL addViewSel = @selector(addSubview:);
IMP addViewImp = [self methodForSelector:addViewSel];
Cell *cell;
for (NSMutableArray *row in self.data) {
    for (Data *d in row){
        cell = [[Cell alloc] initWithRect:tmp_frame];
        addViewImp(self,addViewSel,cell.view);
        [self.cells addObject:cell];

Ошибка:

Завершение работы приложения из-за неперехваченного исключения «NSInvalidArgumentException», причина: «- [SimpleGridView addSubview:]: нераспознанный селектор отправлен в экземпляр 0xaa3c400»

Сообщает мне, что метод addSubview не найден в моем классе SimpleGridView. Однако когда я попробовал:

if ([self respondsToSelector:addViewSel]){
    NSLog(@"self respondsToSelector(AddViewSel)");
    addViewImp(self,addViewSel,cell.view);
} else {
    NSLog(@"self does not respond to selector (addViewSel");
   [self addSubview:cell.view];  
}

Я все еще получаю ту же ошибку!

Вопрос 3. Почему я не могу установить селектор и реализацию для метода Class Init / new следующим образом:

iContactsGridCell *cell;
SEL initCellSel = @selector(initWithRect:);
IMP initCellImp = [iContactsGridCell methodForSelector:initCellSel];
for (NSMutableArray *row in self.data) {
    for (Data *d in row){
        cell = initCellImp([iContactsGridCell new],initCellSel,tmp_frame);

Для справки: класс iContactsGridCell наследуется от класса Cell, который определяет и реализует

- (id) initWithRect:(CGRect)frame;

Кроме того, кастинг не помог (та же ошибка, что и нераспознанный селектор)

iContactsGridCell *cell;
SEL initCellSel = @selector(initWithRect:);
IMP initCellImp = [Cell methodForSelector:initCellSel];
for (NSMutableArray *row in self.data) {
    for (Data *d in row){
        cell = (iContactsGridCell *)initCellImp([Cell new],initCellSel,tmp_frame);

Пробуем разные комбинации, например:

IMP initCellImp = [Cell methodForSelector:initCellSel];

Or

cell = initCellImp([iContactsGridCell class],initCellSel,tmp_frame);

Вывести точно такую ​​же ошибку. Итак, скажите, что мне не хватает, чем это полезно и возможно ли иметь IMP / SEL для метода инициализации класса? Кроме того, будет ли указатель на функцию C быть быстрее по сравнению с этим, или все вышеперечисленное - это просто оболочка указателя на целевую функцию C? Спасибо ! PS: Прошу прощения, если это слишком много вопросов сразу.


person Ælex    schedule 21.12.2012    source источник
comment
Почему бы тогда не перейти на чистый C?   -  person Ramy Al Zuhouri    schedule 21.12.2012
comment
@RamyAlZuhouri Assembly. Или скомпилированный вручную необработанный машинный код. (Серьезно: это микрооптимизация.)   -  person    schedule 21.12.2012
comment
Для записи вы должны преобразовать IMP в правильный прототип функции, прежде чем вызывать его.   -  person Lily Ballard    schedule 21.12.2012
comment
Кроме того, я нахожу весьма сомнительным, что сообщение, отправляемое в этом цикле for, является узким местом, что означает, что вы пытаетесь преждевременно оптимизировать то, что в этом не нуждается.   -  person Lily Ballard    schedule 21.12.2012
comment
@KevinBallard Запуск этого кода в Instruments говорит мне, что [Cell initWithRect]; это узкое место. Я попытался оптимизировать метод инициализации, но я надеялся, что в этом цикле есть возможности для улучшения, поскольку сеток может быть сотни, и этот метод часто вызывается. Как я уже сказал, я раздумывал над идеей посмотреть, помогут ли IMP & SEL. Итак, как мне использовать IMP в этом случае?   -  person Ælex    schedule 21.12.2012
comment
@RamyAlZuhouri Я серьезно обдумал это, но на этом построен целый проект, так что теперь уже слишком поздно.   -  person Ælex    schedule 21.12.2012
comment
@Alex: То, что -[Cell initWithRect:] является узким местом, не имеет никакого отношения к тому, что отправка сообщения является узким местом. Это будет отображаться как objc_mgsSend().   -  person Lily Ballard    schedule 21.12.2012
comment
@KevinBallard Значит, я мало что могу сделать в этом цикле? Тем не менее, почему приведенный выше код не работает? Из любопытства, как правильно использовать IMP для унаследованного метода и как правильно использовать его для метода инициализации?   -  person Ælex    schedule 21.12.2012
comment
@Alex: вы создаете GridView. чтобы избежать слишком большого количества вызовов drawRect, вы должны иметь какое-то повторное использование ячеек.   -  person vikingosegundo    schedule 21.12.2012
comment
@vikingosegundo, это был следующий шаг :)   -  person Ælex    schedule 21.12.2012
comment
@Alex: это должен быть первый шаг.   -  person vikingosegundo    schedule 21.12.2012
comment
@vikingosegundo спасибо за подсказку, но это не ответ на вопрос :)   -  person Ælex    schedule 21.12.2012
comment
@Alex: Я разместил это как ответ?   -  person vikingosegundo    schedule 21.12.2012


Ответы (2)


  1. Да, он избегает objc_msgSend(), который (что более важно) должен выполнять поиск во время выполнения для правильного IMP на основе класса и селектора получателя, даже если в этом случае класс и селектор получателя каждый раз одинаковы, поэтому теоретически мы могли бы просто найти его один раз и использовать повторно.

  2. Из того, что вы описали, трудно понять, что не так. Одна из возможностей заключается в том, что класс объекта динамически обрабатывает методы, так что у него нет конкретной реализации для данного селектора, но при вызове он действительно может обрабатывать его (например, с forwardInvocation:). Это используется во многих местах, например прокси-объекты или объект уведомления, который отправляет полученные данные нескольким целевым объектам. И такой класс мог бы сказать YES respondsToSelector:, поскольку он отвечает на него. Я не уверен, что здесь происходит именно это.

  3. [iContactsGridCell methodForSelector:initCellSel] будет искать метод класса с этим именем, потому что вы вызываете methodForSelector: на iContactsGridCell, объект класса. Чтобы получить метод экземпляра с таким именем, вам нужно будет либо вызвать methodForSelector: в экземпляре iContactsGridCell, либо использовать метод instanceMethodForSelector: в классе.

person newacct    schedule 22.12.2012
comment
Спасибо за обстоятельный ответ. У меня было ощущение, что это не удается не только из-за отсутствия экземпляра, но и из-за того, что я пробовал динамическую ассоциацию с методом init. Это могло бы объяснить, почему он работает с экземплярами, но не с методами init / alloc? - person Ælex; 22.12.2012

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

Т.е. переход к прямым вызовам функций - пустая трата вашего времени.

Непонятно, почему код дает сбой. Вам нужно будет проверить возвращаемое значение methodForSelector:, чтобы убедиться, что оно имеет смысл. Также обратите внимание, что вызов IMP подобным образом неверен; он должен быть приведен к конкретному виду указателя на функцию, которым он является на самом деле. Т.е. void (*impPtr)(id, SEL, CGRect) ... или что-то в этом роде.

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

person bbum    schedule 21.12.2012