Блог О пользователеiphonesdk

Регистрация

 

Теги

... _isnan _t adobe apple automation badge bash bug c++ cellforrowatindexpat h chflags class cocoa condition cpp cpp.cpp crash debug debugger email events exist file flash gcc header headers herb sutter icloud instruments interface builder ios ios 5.0 ios 6 ipad iphone iphone os iphone sdk ipod ipod touch isnan jpeg junior line link linker linux mac mac os man member memory microsoft visual studio mm multithread multithreading navigation bar nslog nsstring nsthread null objcpp.cpp object objective c php phread png posix programming project pthread reference regex release rotation script sdk senior setevent shell sleep stack stack size status bar std::toupper tab template temporary files terminal thread transition uiscrollview uitableview uitableviewcell unix variables vi visual c++ win32 windows xcode xcode 4.2 xcodebuild xcodeproj xml автоматизация автоматически активити алёна c++ алёна сагалаева андрей александреску архив банда четырёх библиотеки блокировка бумажные книги герб саттер герберт шилдт гигантская гиперссылки градация дата деструктор джоэл спольски директория должность дополнительный поток карьера каталог квалификация компиляция консоль константная контрол копировать кэш линкование линковка макроопределение макрос маркетинг мембер многопоточность настройка несколько либ нехватка новосибирск онлайн ооп основной поток отладчик ошибка падение память паттерны паттерны проектирования переименовать переполнение переполнение стека повернуть полоска получить путь к папке портирование поток проблемы программирование программист проект размер текста разработчик роберт мартин сборка си++ скрипт создать список сравнение ссылки статические библиотеки статические либы стек странности отладчика страуструп строки существует текст тест тестирование удалить утечки памяти файл файлов фредерик брукс функция человеко-лет человеко-месяц член электронные книги язык программирования

Программирование для iPhone

 
Приёмы программирования приложений для iPhone с использованием iPhoneSDK
1 |2 |3 |4 |5
 

Плоха ли привычка выходить с помощью return из метода/функции? И про ==


Вспомнил недавно, что читал у одного известного автора (простите, не помню как его зовут), что выходить из функции/метода с помощью return - не самая полезная привычка.

Он объяснял это тем, что однажды код метода/функции будет модифицироваться, и тот кто будет спустя месяцы или годы добавлять что-то новое может не придать должного внимания преждевременному возврату из функции/метода при определённом условии.

Автор советовал вместо того чтобы просто выходить при определённых условиях с помощью return не делать этого, а везде явно написать либо if-else, либо switch-case конструкции, потому что это будет в будущем явно показывать: при таких-то условиях выполняется вот этот блок, а при таких-то условиях будет выполняться другой блок. Нигде не будет ни одного return, а только в конце метода/функции мы уже явно выдаём результат или просто выходим восвояси.

Интересно, что думают другие на этот счёт?

И ещё вспомнил один позабытый совет, который сейчас стал использовать. Автор одной книги делать не так, как принято у многих:

if (variable == CONSTANTA)
{
    // bla bla bla
}

а делать так:

if (CONSTANTA == variable)
{
    // bla bla bla
}

Потому что если по запарке напишешь не ==, а = (9 999 раз напишешь правильно, а в 10 000 раз всё-таки ошибёшься, потому что ты обычный человек, а ошибёшься в важном месте, и люди понесут серьёзные убытки из-за банальной человеческой невнимательности), то константа тебе не позволит это сделать физически.

 

Чем мне понравился язык программирования Swift


Попробовал писать на Swift. В целом язык больше нравится, чем не нравится.


Немного ломает то, что строчки не нужно заканчивать точкой с запятой. Немного ломает то, что после if необязательно писать скобки. Впрочем я их пишу, чтобы не отвыкать от своих привычек к C++ и Objective-C.


Очень радует, что язык Swift заставляет более внимательно относиться к типам данных. То есть программист вынужден обращать внимание на совместимость типов данных что по моему представлению хотя бы немного увеличивает надёжность написанного кода.


Ещё одна интересная особенность: nil не указывает на несуществующую область памяти (как в C++ или Objective-C), он говорит о том, что переменная равна ничему. Удивило, что даже переменной типа Int? можно присвоить nil, и что самое приятное лично для меня - эта переменная не будет равна целочисленному нулю! Это очень классно, я считаю, что это уменьшает вероятность логических ошибок в коде.


 

UICollectionView на iOS 6


Часа 3 с половиной сегодня ушло на то, чтобы понять почему UICollectionView совершенно не показывает своих ячеек с картинками (теплилась лишь слабая надежда, потому что снизу всё-таки была жива прокрутка). Оказалось, что фрейм был задан чуть с меньшей высотой чем нужно. И это проявлялось в моём случае только в устаревшей iOS 6 и только на iPhone. Помог только лишь тупой перебор вариантов (спасло то, что соседние UICollectionView показывали свои картинки-ячейки). Догадаться, что уменьшение высоты контрола делает его ячейки невидимыми я бы просто не смог.


 
 
 

Как быстро выяснить, кто виноват в том, что обычный контрол (UIView, UIImageView и прочего станда...


Просто быстро на время создайте подкласс (например для UIImageView создайте его дочерний класс UIImageView2). Для этого даже не создавайте отдельные исходники, добавьте interface часть в заговолочный файл в класс того контроллера, где возникает непонятный баг. В исполняемый файл (m) добавьте implementation вашего UIImageView2 класса всего лишь с одним методом setFrame (этим методом вы переопределите стандартный setFrame-метод). Вот как это сделал я

@implementation UIImageView2

- (void)setFrame:(CGRect)frame
{
    NSLog(@"++++++ frame.origin.x = %f", frame.origin.x);
    [super setFrame:frame]; // сюда поставьте брекпоинт, чтобы выяснить какая такая "сволочь" портит фрейм вашего объекта на окне :)
}

@end

 

Инструмент Automation (автоматическое тестирование iOS-приложений) - некоторые способы обойти траблы


Инструмент Automation позволяет автоматически тестировать iOS приложения, используя для написания тестовых скриптов JavaScript.

Вероятно, в Apple решили, что заботиться об удобстве работы программистов не настолько остро актуально в отличие от заботы о массовом пользователе... В общем Automation имеет ряд неприятных багов.

1 баг:
Кнопка нажимается с точки зрения Automation, но вы своими глазами видите на экране, что ничего не произошло. В таком случае я выхожу из положения так: я вставляю в скрипте цикл (обычно хватает 2-3 итераций), с помощью которого выясняю встроенными в Automation JavaScript-функциями произошло ли то, что должно произойти после нажатия (например, появилось ли окно, что можно проверить через myView.isVisible() && myView.isValid() условие)

2 баг:
Кнопка, контрол или что-нибудь ещё вообще никак не реагирует на нажатие, если это нажатие является первым нажатием по только что открытому окну. Спасает меня вот что: я с помощью скрипта "провожу пальцем" по тому месту (обычно это середина верхнего бара), где можно водить и это ни к чему не приведёт. После этого окно "оживает" и дальше уже можно нажимать на кнопку.

 

Как прицеплять в Xcode-проект статические либы отдельно для Debug и Release


Это касается только Xcode 4 и более поздних версий:

 

ПЕРВЫЙ ТРУДОЁМКИЙ СПОСОБ: прицепить либы к дереву проекта намертво (вероятно можно и мышкой добавить нужные либы из Finder-папки перетаскивая их прямо в дерево проекта), но я делал это выходя в свойства проекта, потом переключался на вкладку Target, выбирал там Build Phases - > Link Binary With Libraries.

 

Этот первый способ плох тем, что Xcode порой глючит, и не в состоянии отличить дебаг-либу от релизной и может прицеплять дебаг-либу при релизной сборке приложения. Поэтому мне пришлось написать shell-скрипты, которые прятали в Temp-папки те либы, которые мне мешали и shell-скрипты для возвращения этих либ на прежние места. То есть перед Release-сборкой приложения все дебаг-либы можно аккуратно убрать одним запуском скрипта - при этом эти либы окрасятся в тревожный красный цвет в дереве проекта, но пусть это вас не пугает - приложение будет исправно собираться с оставшимися релизными либами.

 

НОВЫЙ ХОРОШИЙ СПОСОБ (пока плохо мной оттестирован): не добавлять либы первым способом в дерево проекта. Добавьте пути к нужным либам (включая имена файлов этих собранных либ) в настройках проекта и настройках таргета  - > Build Settings - > Linking - > Other Linker Flags (в подраздел Debug добавьте пути к дебаг-либам, в подраздел Release - к релиз-либам). Кроме того, вы можете в Debug/Release подразделы добавить вложенные подразделы для разных таргетов (для симуляторов и девайсов) - для этого найдите + кнопку справа внизу "Add Build Settings", нажмите на неё и выберите "Add Conditional Settings" (укажите пути для симуляторных и девайсовских дебаг/релиз либ).

 

Как отключать все вызовы NSLog для release-сборки


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

#ifdef DEBUG
#define DLog(f, ...) NSLog(f, ## __VA_ARGS__)
#else
#define DLog(f, ...)
#endif

Теперь с помощью Find and Replace замените все NSLog на DLog. Если в некоторых исходниках вылезут ошибки компиляции, то добавьте в эти исходники #include "AppMacros.h"

 
 
 

Отладка приложений работающих с серверами


Очень важно тестировать приложения на настоящих iPhone-девайсах, чтобы была возможность вместо конторского Wi-Fi быстрого соединения включить медленный интернет (отключив 3G). Тем самым удаётся приблизить поведение приложения к реальным условиям обычного пользователя.

При этом часто всплывают наружу неудачные решения в области GUI. Например, когда приложение просто замерзает вместо показа хотя бы активити-индикатора на фоне полупрозрачного окна, перегораживающего все контролы. На этом полупрозрачном окне кроме активити-индикатора можно показать надпись "Загрузка данных...". Считаю, что это более юзабильно.

В идеале длительную загрузку или отправку данных лучше делать в отдельном потоке, но при этом разработка приложения усложняется - нужно как-то предусматривать use cases, при которых пользователь захочет совершать действия с данными, которые в этот момент изменяются в другом потоке.

 

О важности IBOutlet прикрепления объектов к объекту-хозяину


Очень важно, если вы решили часть рутинных операций возложить на такой удобный инструмент как Interface Builder не забывать об одном правиле. Каждый объект, который создаётся не динамически в коде (например smthObject = [[SmthClass alloc] initWith... ]), а создан с помощью Interface Builder, каждый такой объект должен иметь своего хозяина (разве что главный делегат приложения гарантированно работает по умолчанию).

То есть любой ваш объект (созданный с помощью Interface Builder) должен принадлежать кому-нибудь как IBOutlet-переменная. Я убеждался в этом уже много раз на практике. Иначе  происходят падения программы при попытке заставить этот объект что-либо сделать, из-за того, что у этого объекта (если он не принадлежит никому как IBOutlet-переменная) например вызывается метод, а объект вроде не существует (раз ни к кому не принадлежит). Может быть гуру-программисты дадут более точное объяснение этому явлению...

 

О важности округления размеров GUI-контролов


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

Оказалось, что я задавал высоту ячейки не как целое или округлённое до целого число (то есть возвращал из метода heightForRowAtIndexPath значение с заметной на экране дробной частью).


 

Как собирать проекты с гигантскими функциями


Случается так, что гигантские функции (в несколько тысяч строк кода) не позволяют собрать проект. Если нет ошибок компиляции, но есть непонятная ошибка линковки, то попробуйте в свойствах Xcode-проекта убрать галочку с пункта GCC 4.2 Code Generation - > Compile for Thumb. Мне это помогло (нашёл совет где-то в англоязычных сайтах).

 

Мои скрипты для работы в Xcode


Вот какие скрипты я использую для работы в Xcode (на них можно назначать сочетания клавиш). Для добавления скриптов и назначения им сочетаний клавиш найдите в полосе меню (у меня Xcode 3) пункт, напоминающий свиток, а в нём субменю Edit user scripts...

В ОСНОВНОМ ЭТО СКРИПТЫ ДЛЯ ДОБАВЛЕНИЯ ЧАСТО ИСПОЛЬЗУЕМЫХ СТРОК (без горячих сочетаний клавиш теряется их неоценимая помощь в работе):

1) Добавление NSLocalizedString(@"", @"")
#!/bin/sh
echo "NSLocalizedString(@\"\", @\"\")"

2) Добавление // property
#!/bin/sh
echo "// property"

3) и так далее до пункта 11)
#!/bin/sh
echo "@property (nonatomic, retain) IBOutlet "

4)
#!/bin/sh
echo "//----------------------------------------------------------"

5)
#!/bin/sh
echo "////////////////////////////////////////////////////////////"

6)
#!/bin/sh
echo "NSLog(@\"\");"

7)
#!/bin/sh
echo "NSLog(@\"%s function %s \", __FILE__, __FUNCTION__);"

8)
#!/bin/sh
echo "NSLog(@\"%s line %d \", __FILE__, __LINE__);"

9)
#!/bin/sh
echo "using namespace "

10)
#!/bin/sh
echo "@synthesize "

11) Вставляю свои инициалы, дату и время (чтобы не забыть удалить после экспериментов)
#!/bin/sh
echo "//MD:$(date +%Y%m%d-%H:%M)"

12)
#!/bin/sh
echo "////////////////////////////////////////////////////////////"
echo "#pragma mark -"
echo "#pragma mark  methods"
echo "////////////////////////////////////////////////////////////"

13) Сначала выделяете нужный текст (например имя функции). После выполнения этого скрипта у вас получится в коде вывод в отладочную консоль выделенного текста.
#!/bin/sh
echo "NSLog(@\"%%%{PBXSelectedText}%%%\");";

 

Новый Xcode 4.2 меня разочаровал


Новый Xcode 4.2 меня разочаровал примерно в том же отношении, как меня лет 10 тому назад основательно разочаровал Windows 98 (тогда приходилось как минимум каждый час нажимать кнопку Reset). Тогда я всерьёз решил перейти на Linux (хотя Линукс имхо хорошо подходит для людей, готовых в любой момент серьёзно заниматься именно Линуксом, то есть Линукс - это не инструмент для решения таких задач, где можно забыть про инструмент и решать задачи, а Линукс - это инструмент для овладения инструмента под названием Линукс. Но это моё мнение).

Так вот, Xcode 4.2 постоянно преподносит мне какие-то сюрпризы... Совершенно запутанные и нелогичные. Я уже втайне начал мечтать заняться по работе другой темой в программировании.

Сейчас истратил полчаса, из-за того, что Xcode утверждал мне о том, что невозможно использовать StoryBoard в IOS 4.3 и более ранних, хотя я многократно переключал DeploymentTarget в IOS 5.0. Оказывается, кроме моих действий (Clean не помогает) нужно ещё и поменять хотя бы что-то в исходниках, и снова сбилдить приложение. Жопа, жопа, жопа...

Много ещё и других глюков (столько глюков сразу в Xcode не было ни разу, хотя в этой новой версии много хороших задумок). Неужели после того, как Apple осталась без Стива Джобса, эта компания обречена потерять лидерские позиции? Я думаю, что очень даже может быть. Любое могущество основано на могуществе личности (это я прочитал у Наполеона Хилла).

 
Теги: xcode 4.2
 
 

Функция поворота CGImage


Материал взят из http://connordenman.wordpress.com/2010/09/18/rotation-of-cgimage-iphoneipad-improved/

- (CGImageRef)CGImageRotatedByAngle:(CGImageRef)imgRef angle:(CGFloat)angle
{
CGFloat angleInRadians = angle * (M_PI / 180);
CGFloat width = CGImageGetWidth(imgRef);
CGFloat height = CGImageGetHeight(imgRef);

CGRect imgRect = CGRectMake(0, 0, width, height);
CGAffineTransform transform = CGAffineTransformMakeRotation(angleInRadians);
CGRect rotatedRect = CGRectApplyAffineTransform(imgRect, transform);

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef bmContext = CGBitmapContextCreate(NULL,
rotatedRect.size.width,
rotatedRect.size.height,
8,
0,
colorSpace,
kCGImageAlphaPremultipliedFirst);
CGContextSetAllowsAntialiasing(bmContext, YES);
CGContextSetInterpolationQuality(bmContext, kCGInterpolationHigh);
CGColorSpaceRelease(colorSpace);
CGContextTranslateCTM(bmContext,
+(rotatedRect.size.width/2),
+(rotatedRect.size.height/2));
CGContextRotateCTM(bmContext, angleInRadians);
CGContextDrawImage(bmContext, CGRectMake(-width/2, -height/2, width, height),
imgRef);

CGImageRef rotatedImage = CGBitmapContextCreateImage(bmContext);
CFRelease(bmContext);
[(id)rotatedImage autorelease];

return rotatedImage;
}

 

Сохранение UIImage в png или jpeg файл


Материал взят из http://iphonedevelopertips.com/data-file-management/save-uiimage-object-as-a-png-or-jpeg-file.html

// Create paths to output images
NSString *pngPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/Test.png"];
NSString *jpgPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/Test.jpg"];

// Write a UIImage to JPEG with minimum compression (best quality)
// The value 'image' must be a UIImage object
// The value '1.0' represents image compression quality as value from 0.0 to 1.0
[UIImageJPEGRepresentation(image, 1.0) writeToFile:jpgPath atomically:YES];

// Write image to PNG
[UIImagePNGRepresentation(image) writeToFile:pngPath atomically:YES];

// Let's check to see if files were successfully written...

// Create file manager
NSError *error;
NSFileManager *fileMgr = [NSFileManager defaultManager];

// Point to Document directory
NSString *documentsDirectory = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];

// Write out the contents of home directory to console
NSLog(@"Documents directory: %@", [fileMgr contentsOfDirectoryAtPath:documentsDirectory error:&error]);

 
Теги: uiimage|png|jpeg
 
 

Проверка валидности email


- (BOOL) validateEmail: (NSString *) candidate
{
   
NSString *emailRegex = @"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}";
   
NSPredicate *emailTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", emailRegex];

   
return [emailTest evaluateWithObject:candidate];
}

Код взят из http://stackoverflow.com/questions/800123/best-practices-for-validating-email-address-in-objective-c-on-ios-2-0

 

Регулярные выражения для поиска/замены в исходниках Xcode-проекта


[^//][^ ]NSLog - это регулярное  выражение для поиска всех незакомментированных NSLog (лично я люблю выводить в лог всяческие значения и прочую лабуду, которая помогает мне понять, что вообще происходит в программе, не всегда это можно сделать только с одними брекпоинтами).

[^a-zA-Z_]id[^a-zA-Z_] - это регулярное выражение для поиска всех id, где справа и слева нет букв и нет знаков подчёркивания (может пригодиться, если вам нужно заменить тысячи локальных переменных или мемберов в тех коллективных исходниках, которые писали те плюсовые программисты, которые не знали или забыли (а у вас не хватает наглости их упрекать за повторяющуюся забывчивость), что в Obj-С id является именем типа (псевдоним типа NSObject*). Внимание! Квадратные скобки дают нам по одному символу слева и справа от id, которые не являются буквами и знаком подчёркивания. То есть поиск нам выдаст в числе прочих и всё 4 буквенные строки, где в центре каждой строки мы имеем id.

Поэтому, если вам нужно заменять в коллективных Си++ исходниках id на _id (ведь id является типом данных в Objective C, а ваши cpp файлы наверняка будут настроены в дереве проекта как cpp.objcpp файлы), то лучше сделайте так:

Вставьте в поле Find:
([^a-zA-Z_])id([^a-zA-Z_])
Вставьте в поле Replace:
\1_id\2

Дело в том, что в квадратных скобках мы имеем по одному символу (который не буква и не знак подчёркивания). Поэтому, при замене (если не использовать круглые скобки и в поле Replace просто написать _id) мы бы заменяли не только id, но и символ справа и символ слева от id, то есть съедались бы точки с запятой и прочие важные символы. \1 и \2 заставляет Xcode кроме _id вставить одиночные символы слева и справа (те, которые были слева и справа от оригинальной id).

 

Невозможно добавить исходник в дерево проекта?


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

Тогда просто откройте вне проекта рядом папку с исходником (или иным нужным файлом) и перетащите нужный исходник (или файл) в дерево проекта в нужную группу

Удивительно, это работает!

 

Много умных мыслей для разработчиков


Я люблю читать на этом сайте различные умные мысли (от Славы Панкратова и Александра Орлова)

http://www.it4business.ru/

А ещё я обожаю читать Джоэла Спольски (прочитал его 2 книги на русском языке, но многое оттуда есть и на этом сайте)

http://russian.joelonsoftware.com/

 

Полезные ссылки для iOS-программиста


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

http://www.rsdn.ru/forum/apple.os/
http://touchdev.ru/
http://habrahabr.ru/blogs/macosxdev/

1 |2 |3 |4 |5

     
ЯнварьФевральМартАпрельМайИюньИюльАвгустСентябрьОктябрьНоябрь (82)Декабрь
           
123456789101112131415161718192021222324252627282930