среда, 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).

UIActivityIndicator не всегда работает!

В некоторых случаях UIActivityIndicator не всегда работает. С чем это связано, я до сих пор не знаю.

Выяснил вот что - если после запуска в основном потоке активити-индикатора программа в основном потоке ничего не делает (что само по себе редкость и вряд ли имеет смысл, ведь пользователю нужно дать знать, что дескать программа что-то будет делать, и что она не зависла, а занята выполнением работы), то в таком случае активити-индикатор работает исправно.

Если же программа занялась плотно какими-то задачами в основном потоке (кстати, согласно рекомендациям от Apple в iPhone ГУИ-контролы вообще лучше юзать в основном потоке, если я не ошибаюсь), то активити-индикатор может "не успеть" сделать какую-то подготовительную работу и тогда пользователь его не увидит. Но это лишь мои догадки.

Как я вышел из положения? Приходится программно запускать специально созданный для этого таймер на 1/10 долю секунды (этого времени индикатору хватает), после этого запускать активити-индикатор и просто ничего не делать до срабатывания таймера (то есть до срабатывания функции-обработчика таймера). Этой паузы хватит, чтобы активити-индикатор глотнул воздуха и начал работать. Как только сработает таймер через 1/10 секунды, то останавливайте таймер и запускайте в основном потоке нужные вам функции, это уже не помешает активити-индикатору продолжать показывать, что дескать дела в основном потоке идут.

Конечно, есть риск, что пользователь что-то успеет нажать за 1/10 секунды। Для этого можно перестраховаться с помощью булевой переменной (т.е. заблокировать обработчики нажатий на контролы, вернее выходить из функций обработчиков если эта переменная показывает, что сейчас как раз идёт та самая 1/10 доля секунды).

Добавлено через много месяцев: сейчас я вместо таймера использую более подходящий и простой вызов функции
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay
Смотрите подробнее в документации (эта функция способна вызвать нужный вам код в текущем потоке через нужное время, этого глотка времени хватает, чтобы активити-индикатор закрутился).

Кстати, вся эта статья относится не только к активити-индикаторам, но и многим другим контролам (которым необходим глоток воздуха для того, чтобы успеть отобразиться на экране).

четверг, 17 марта 2011 г.

Стандартные размеры контролов

Материал взят из http://jdg.net/post/106465937/standard-iphone-element-sizes

Core Elements:

Carrier Status bar - 320x20
UIView - 320x460
UINavigationBar - 320x44
UITabBar - 320x49
UISearchBar - 320x44
UIToolBar - 320x44

Data Input:

UIPickerView - 320x216
UIDatePicker - 320x216
UIKeyboard - 320x216

Buttons:

UISegmentedControl - 320x44
UIButton xx37

Fields:

UITextField - xx37
UISwitch 94x27
UISlider - xx23

Indicators:

UIProgressView -xx9
UIActivityIndicatorView - 37x37
UIPageControl - 38x36

четверг, 10 февраля 2011 г.

Как сделать один общий проект для нескольких статических библиотек (либ)

Недавно я сумел таки обойти некоторые ограничения Xcode и сделать один общий проект для нескольких статических библиотек (либ).

Ограничения касаются прежде всего такого макроопределения как __IPHONE_3_1 (последние цифры не столь важны, это уже технические моменты - всё зависит от того, начиная с какой версии IOS ваше приложение и библиотеки должны работать).

Если для каждой либы (т.е. библиотеки) вы создаёте свой отдельный Xcode-проект, то в m и mm исходниках (а также в c/cpp - исходниках, если они в своих свойствах выставлены как c.objcpp и cpp.objcpp файлы) макроопределения __IPHONE_... срабатывают безотказно.

То есть вы можете в вашем коде сделать такую проверку:

#ifdef __IPHONE_3_1
sad;flkasjdf;laksjdfa;sldfkja;sdlkfjasd;lkfj; // всякая дрянь, которая не точно компилируется
#endif

Если компилятор будет ругаться на эту дрянь, то значит всё в порядке.

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

Можно искусственным способом заставить ваш проект узнавать макроопределение __IPHONE_3_1

Для этого зайдите в свойства проекта и найдите там раздел GCC 4.2 Preprocessing, а в этом разделе такую строку (слева) Preprocessor Macros. Так вот в поле справа можно добавить такую строку

__IPHONE_3_1=1

Туда же можно добавить и другие важные строки, например __GNUC__=1 или TARGET_OS_IPHONE=1

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

среда, 26 января 2011 г.

Xcode vs Visual Studio

Я уже работаю полтора года разработчиком приложений для iPhone (используя единственную официальную среду разработки Xcode). За несколько лет до этого я несколько месяцев работал с Visual Studio.

Да, действительно чувствуется, что в Apple самые главные люди - дизайнеры, а не программисты. Microsoft проявляет больше заботы об удобстве работы программистов с их основным инструментом.

То есть я больше люблю Visual Studio, чем Xcode.

вторник, 14 декабря 2010 г.

Как узнать в iPhone-приложении, существует ли файл

Материал взят из http://stackoverflow.com/questions/1356701/whats-a-quick-way-to-test-to-see-a-file-exists

BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:somePath];

среда, 24 ноября 2010 г.

Скрипт для автоматического создания архива проекта с датой и временем в имени файла

Этот скрипт расчитан на консоль для Unix-подобных систем (впрочем, возможно его можно запустить и в Cygwin-консоли в Windows, но думаю, что проще найти похожие функции для bat-файлов, принятых в Windows).

Разумеется, нужно вместо MyProject и MyProjectFolderFullPath подставить полные или корректные относительные пути к вашей папке с проектом и к архиву (zip-файлу)

Предположим, что архив будет создаваться в текущей папке:

#!/bin/sh
echo "Enter comment: "
read comment
zipFileName="./MyProject$(date +%Y%m%d_%H-%M)"
echo "compress MyProject to zip archive..."
txtFileName="$zipFileName.txt"
echo $comment > $txtFileName
zip -r $zipFileName MyProjectFolderFullPath

Не забудьте сделать скрипт исполняемым файлом. Теперь в консоли достаточно будет ввести имя файла этого скрипта (проще разместить его в домашней папке) и нажать Enter - и архив папки проекта вместе с одноимённым текстовым файлом комментариев будут выглядеть примерно так:

MyProject20101124_14-20.txt
MyProject20101124_14-20.zip

Очень удобно.

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