Показаны сообщения с ярлыком objective c. Показать все сообщения
Показаны сообщения с ярлыком objective c. Показать все сообщения

среда, 18 мая 2011 г.

C++ объекты как мемберы Objective C классов

Я намучался изрядно с плюсовыми объектами, которые объявлял как обычные мемберы Objective C классов. Проблема в том, что вы не можете быть уверены, когда будет вызван деструктор такого мембера в момент прекращения жизни Objective C объекта, и будет вообще ли вызван этот деструктор.

К счастью есть простоё и надёжное решение.

Используйте обычный указатель или std::auto_ptr. Теперь вы сможете контролировать момент вызова конструктора и момент вызова деструктора. Если вам нужно будет уничтожать C++ объект при уничтожении его владельца (Objective C объекта), то самое подходящее место для этого - метод dealloc (аналог деструктора в Objective C классах).

Как выяснилось, я не напрасно предпочёл в качестве мемберов использовать указатели вместо обычных объектов. На http://rsdn.ru/?/forum/apple.os/ мне помогли разобраться с этой ситуацией (привели цитату из официальной документации)

Objective-C classes cannot have instance variables of C++ classes that do not have a default constructor or that have one or more virtual methods, but pointers to C++ objects can be used as instance variables without restriction (allocate them with new in the -init method).

пятница, 30 апреля 2010 г.

Как выяснить причину EXC_BAD_ACCESS в Xcode

Нашёл интересную статью (на английском) на http://www.codza.com/how-to-debug-exc_bad_access-on-iphone (там это приводится с рисунками и комментариями читателей).

НАЧАЛО ЦИТАТЫ.

EXC_BAD_ACCESS. Debugging this one is on par with figuring out why the wife says “not tonight, honey.” And they are equally unfortunate situations.

Let’s see what we can do about EXC_BAD_ACCESS.

EXC_BAD_ACCESS happens when a message is sent to an object that has already been released. By the time the error is caught, the call stack is usually gone especially if dealing with multiple threads.

How nice would it be to keep a dummy around after the object is released that could stop execution, tell us what message was sent and show us the call stack… well, there’s a way to do just that.

If you set the NSZombieEnabled environment variable, the Objective C runtime will leave a dummy object behind for every deallocated object. When the zombie object is called, execution stops and you can see the message that was sent to the object and the call stack that tells you where the message came from (it doesn’t tell you where you over released the object, but knowing where the object is called from should get you pretty close to the problem.)

To set this variable, go to the info panel of the executable in xcode, and create a new environment variable in the arguments tab by clicking the plus sign in the lower left corner of the window. Name the variable NSZombieEnabled, type YES for the value and make sure that the checkbox is selected.
set NSZombieEnabled variable

set NSZombieEnabled variable

Go ahead and run your program now (in debug mode, because you need the stack information.) When the over released object is accessed, you get an error message similar to this (xcode debug view):

2009-03-30 02:30:36.172 ninjaJumper[3997:20b] *** -[GameLayer retain]: message sent
to deallocated instance 0x59bf670

This shows the class of the object (GameLayer) and the message sent (retain).

Let’s take a look at the stack now:
call stack

call stack

The methods printed in bold are in your code, the others are in some other API. Here you can see that the object was accessed from [Director touchesBegan:withEvent], where an array was copied (most likely the over released object was in the array.)

This information should get you pretty close to the problem.

Once the problem is fixed, make sure that the NSZombieEnabled variable is disabled. You don’t need to delete it, but make sure that the checkbox is unchecked:
NSZombie disabled

NSZombie disabled

Now about the wife. Good luck there. Try a box of chocolate or load the dishwasher for a couple days.

КОНЕЦ ЦИТАТЫ.

К СОЖАЛЕНИЮ, ДАННЫЙ СОВЕТ ПОЗВОЛЯЕТ ОТСЛЕЖИВАТЬ ИМЕНА Objective C КЛАССОВ. Я ТОЛЬКО ЧТО СДЕЛАЛ ТЕСТОВОЕ ПРИЛОЖЕНИЕ, В КОТОРОМ ПОЛЕЗНЫЕ ОТЛАДОЧНЫЕ СООБЩЕНИЯ ВЫВОДЯТСЯ В КОНСОЛЬ ТОЛЬКО КАСАЕМО Objective C КЛАССОВ:


TestClass *testObj = [[TestClass alloc] init];
[testObj release]; // уничтожили объект перед попыткой использования
[testObj printMsg];

В этом случае в консоль будет выводиться информация
2010-04-28 13:02:08.030 ZombieTest[1353:207] *** -[TestClass printMsg]: message sent to deallocated instance 0x3924880)


В случае же с C++ классом:

TestCppClass *tcpp = new TestCppClass();
delete tcpp;
tcpp = 0;
tcpp- >PrintMsg(); // удивительно, но это работает (на экран выводится тестовое сообщение, так как вероятно функция-член класса способна работать и без создания объекта - ЭТО ЧТО, ОЧЕРЕДНОЙ БАГ Xcode?)
delete tcpp; // тем не менее здесь код спотыкается, так как пытаемся уничтожить объект повторно

в консоль выводится то же самое что и без применения совета из приведённой статьи):
2010-04-28 13:04:45.242 ZombieTest[1476:207] TestClass --- printMsg
ZombieTest(1476,0xa0a7b4e0) malloc: *** error for object 0x3917160: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug

вторник, 27 апреля 2010 г.

Как совмещать C++ и Objective C код в одном файле (классе)

Есть 2 способа, как совмещать C++ и Objective C код в одном исходнике:

1) При добавлении в Xcode-проект нового Objective C класса (который будет включать в себя C++ код) дайте файлу реализации расширение.mm (а не.m как предлагается по умолчанию). Кроме того, можно смело переименовать в дереве Xcode-проекта файл, изменив расширение с m на mm.

2) Если вам нужно использовать Objective C код в C++ классе (файл реализации которого будет иметь расширение.cpp) то в дереве проекта нажмите правую кнопку мыши или сделайте иное действие (в зависимости от настроек Mac OS) чтобы вызвать контекстное меню. В контекстном меню выберите Get Info. Появится окошечко, в котором нужно поменять тип файла с cpp.cpp на objcpp.cpp. Кстати, это позволит коду узнавать #ifdef __IPHONE_3_1 (или какая у вас там версия iPhone OS), что необходимо, если вы работаете в коллективном проекте, где нужно писать переносимый код для разных платформ.

Постоянные читатели