Цель C: [MyObject alloc] теперь вылетает под iOS SDK 4.1
Я работаю над существующей кодовой базой большого размера, и после обновления iOS SDK до 4.1 теперь я вижу очень странное поведение. Суть дела в том, что конкретный класс больше не будет выделяться - он выбрасывает неправильный доступ в obj_msgSend и, похоже, является объектом Class в стеке, который objc_msgSend не нравится - хотя на самом деле он не равен NULL.
Исходная неудачная строка выглядела так:
tileProjection = [[RMFractalTileProjection alloc] initFromProjection:proj tileSideLength:sideLength maxZoom:18];
Я разобрал это, чтобы изолировать проблему:
RMFractalTileProjection *p = [RMFractalTileProjection alloc]; // <- this crashes
p = [p initFromProjection:proj tileSideLength:sideLength maxZoom:18];
tileProjection = p;
Затем я попробовал это:
Class c = NSClassFromString(@"RMFractalTileProjection");
assert(c);
NSLog( @"RMFractalTileProjection class(ptr) %p", c ); // <- prints an address OK
NSLog( @"RMFractalTileProjection class(obj) %@", c ); // <- crashes
В отладчике кажется, что объект Class разумный, но NSLog падает, когда пытается его напечатать.
Стоит отметить одну вещь: рассматриваемый класс объявлен, как показано ниже, и я не уверен, что протокол вызывает проблемы. Поскольку эта конкретная часть представляет собой большой кусок открытого исходного кода, очень трудно удалить это требование протокола, чтобы увидеть, если это имеет значение.
@interface RMFractalTileProjection : NSObject<RMMercatorToTileProjection>
{
...
}
Любая помощь в этом очень ценится - это выставочный стопор.
Спасибо
3 ответа
ОК, наконец-то нашел это. Как предположил Джереми, это оказалось обычным потрясением памяти.
Трудность, с которой я столкнулся, заключалась в том, что топтался не сам объект Class, а структура метакласса класса - который является обычным объектом Class, но на один уровень выше, на который ссылается указатель класса isa. Вот почему класс выглядел хорошо для меня, когда я проверял его в отладчике - мне нужно проследить указатель isa и сбросить память на один уровень вверх, чтобы найти это. К счастью для меня, класс был только подклассом NSObject - если бы он был глубоко подклассом, найти его было бы гораздо сложнее. Я получил свою первую подсказку после прикуса пули, обратного инжиниринга objc_msgSend, точно выяснив, что было в кадре стека, и следуя всем указателям. Да, трудный путь:)
Пост Мэтта Галлагара (и другие, которые я нашел по ссылкам) помог мне пройти через этот лабиринт - спасибо, ребята!
Сгорел много времени на этом, но с другой стороны, я узнал чертовски много о внутренностях Objective C за последние полтора дня:)
Это на самом деле не ответ, а некоторые идеи для продвижения вперед.
Единственные причины, которые приходят на ум в данный момент, - это повреждение памяти и проблема с ссылками. Возможно, вы как-то связываете две версии класса.
Предполагая, что это класс, нет ничего плохого в том, чтобы заставить его аварийно завершить работу в alloc. Там нет + инициализировать или что-нибудь.
Вопросы, которые я бы задавал себе и пытался ответить:
что произойдет, если я переименую класс?
что произойдет, если я создам новый идентичный класс с другим именем?
указатель, который передается в obj_msgSend: это разумно? это указывает на что-то, что выглядит как класс?
Вы когда-нибудь делали подкласс класса и используете ли вы инициализацию в подклассе?
указатель всегда один и тот же? Если это так, вы можете посмотреть, на что он указывает, и посмотреть, изменится ли он во время выполнения.
что произойдет, если вы отправите себя в класс?
Спасибо за эти предложения JeremyP - всегда хорошо иметь свежие предложения после того, как вы весь день бьетесь головой о клавиатуру!
Ваше предложение о создании идентичного класса с тем же именем, похоже, решило проблему. Я понятия не имею, почему, и я чувствую, что мне нужно понять, что здесь происходит. Вы правы, это звучит как какая-то проблема с компоновщиком, но я до сих пор не знаю, что могло вызвать такую серьезную ошибку во время выполнения и даже не выдать предупреждение во время сборки.
Число рейнольдса указатель выглядит разумным, но что-то внутри класса в конце концов разыменовывается как нулевой указатель внутри objc_msgSend. Иногда, после того, как я изменил код и перестроил, вместо этого я получаю нулевой указатель. Такое поведение, очевидно, наводит на мысль о недетерминированности, например о том, как стучит память
Я опубликую свои выводы.