2012-06-08 2 views
7

Этот код дает мне EXC_BAD_ACCESS, почему?Почему этот код дает EXC_BAD_ACCESS (используя IMP)

NSMutableDictionary *d = [[NSMutableDictionary alloc] init]; 
IMP imp= [d methodForSelector:@selector(setObject:forKey:) ]; 
imp(d, @selector(setObject:forKey:), @"obj", @"key"); 

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

Некоторые примечания: ARC включена, Xcode 4.3.2, проект использует Objective-C++ в качестве языка по умолчанию от/компилятором, этот код находится в самом начале проекта

спасибо ребята

+0

Я не могу воспроизвести. Если вы поместите это в новый проект, вы все равно получите ошибку? – Chuck

+0

Да, что я нашел ... ARC - проблема. Попробуйте создать новый проект iOS с включенной поддержкой ARC. Затем скопируйте и вставьте код где-нибудь (я положил его в приложение didFinishLaunchingWithOptions :) – subzero

ответ

20

Вам нужно правильно направить указатель на функцию или ARC не знает, что он должен делать. IMP - это общий указатель на функцию, который принимает идентификатор, селектор и переменное число других неопределенных аргументов и возвращает идентификатор. Реализация метода, который вы пытаетесь вызвать, принимает идентификатор, селектор, за которым следуют ровно два идентификатора и имеет тип возврата void. Вы можете это исправить, изменив на следующий код:

NSMutableDictionary* dict = [[NSMutableDictionary alloc] init]; 
void (*imp)(id, SEL, id, id) = (void(*)(id,SEL,id,id))[dict methodForSelector:@selector(setObject:forKey:)]; 
if(imp) imp(dict, @selector(setObject:forKey:), @"obj", @"key"); 

Вы всегда должны проверить, что вы на самом деле есть указатель на функцию обратно перед разыменования его, так как это будет также крах. Приведенный выше код будет работать даже в среде ARC. Кроме того, даже если вы не используете ARC, вы всегда должны указывать свои указатели на фактический прототип, а не IMP. Вы никогда не должны использовать IMP. Другие места, которые могут вызвать серьезные проблемы, - это если метод возвращает структуру или метод принимает параметры с плавающей запятой и т. Д.

Хорошая привычка: всегда указывайте свои указатели на функции или делайте typedefs для них, если вы найдете синтаксис синтаксиса указателя функции ,

+0

Спасибо Джейсону. Он работал как шарм !. Вопрос: почему это может вызвать серьезную проблему со структурами или методами с параметрами с плавающей запятой ?, если бы вы могли указать мне на какой-то документ, я буду счастливее. Я просто нашел тему «времени исполнения» интересной. – subzero

+1

Он связан с вызывающими соглашениями функций в C и как передаются параметры. Поскольку компилятор ничего не знает о параметрах IMP за параметрами 'self' и' _cmd', он просто вставляет другие параметры в стек и ожидает, что вызываемая функция будет знать, что с ними делать. Однако, если вызываемая функция ожидает поплавок, она почти наверняка будет выглядеть в GPR (для ARM) или в другом определенном fp регистре, а не в стеке и т. Д. То же самое и для типов возврата, структуры обрабатываются иначе, чем указатели. –

1

Проблема заключается в том, что IMP имеет тип возврата «id», который ARC будет пытаться управлять. Вам нужно указать указатель на функцию, чтобы иметь возвращаемый тип void (в соответствии с методом, который вы вызываете):

NSMutableDictionary *d = [[NSMutableDictionary alloc] init]; 
    IMP imp= [d methodForSelector: @selector(setObject:forKey:)]; 
    void (*func)(__strong id,SEL,...) = (void (*)(__strong id, SEL, ...))imp; 
    func(d, @selector(setObject:forKey:), @"obj", @"key"); 
+0

'void (*) (id, SEL, ...)' не является правильным типом для указателя функции. Это 'void (*) (id, SEL, id, id)'. Хотя он может работать в этом конкретном случае на некоторых архитектурах с определенными типами ('id'), он в целом неправильный. – newacct

+0

Это действительно сработало. Благодарю. – Felipe

Смежные вопросы