GDB. Легко и даже изящно.

Хотя в начале GDB вызывал у меня ощущение ужаса и сильно отторжение, сейчас я к нему привык и он мне даже начал нравится. Что интересно, пришлось выучить не так уж и много команд.

Итак, по группам.

Запуск отладки

attach pid – присоединение к процессу с идентификатором pid для отладки.
set args args – установка args в качестве аргументов приложения.
run – запустить открытое приложение на отладку.
continue – продолжить выполнение приложения (например после того как сработала точка останова или в случае в присоединением к процессе).

Установка точек останова.

break file:line – установить точку останова на заданную линию заданного файла.
break func_name – установить точку останова на заданную функцию.
break – установить точку останова на текущую линию в файле.
watch – установка точки наблюдения за изменением состояния переменной (write).
rwatch addr – установка точки наблюдения за изменением состояния переменной (read). Например команда rwatch *0xfeedface, означает что необходимо наблюдать за изменением данных находящихся по адресу 0xfeedface.

Сигналы

handle SIGNAL_NAME nostop – не останавливаться на сигнале SIGNAL_NAME. Бывает очень полезно для SIGPIPE.

Управление отладкой

n – шаг, не заходя в функцию.
s – шаг в функцию.
fin – закончить функцию.

Просмотр состояния объектов

whatis – информация о типе объекта по указателю на него.
ptype – детальное описание типа.
print – распечатать значение объекта.
Кроме того, необходимо скачать файл stl-views.gdb, который легко гуглится по фразе “STL GDB evaluators/views/utilities”. Благодаря нему появляется возможность выводить содержимое контейнеров STL. Например, появляются команды pvector, pmap и т.д.

Сбор дополнительной информации

bt – показать вызовов.
i sharedlibrary – показать список загруженный динамических библиотек.
i threads – показать все активные потоки.
i line numb – отобразить информацию о линии с номером numb.
i thread – детальная информация по текущему потоку.
thread apply all command – применить команду command ко всем потокам. Очень полезно использовать thread apply all bt для вывода стека всех потоков.
frame – текущая позиция в отлаживаемом коде.
list – отобразить линию либо функцию. Если вызывается без аргументов, отображается текущая линия.
disassemble start end – диассемблировать код начинающийся по адресу start и заканчивающийся на адресе end. Команда бывает полезна в купе с i line.

Далее. Крайне желательно установить скрип который можно найти у IBM вот тут.
Благодаря ему открываются такие прекрасные команды как:

reg – вывести содержимое регистров.
dd addr – вывести содержание памяти по адресу addr в формате HEX | ASCII.

Самописные ништяки

Так же будут полезны команды загрузки/сохранения точек останова:

define bsave
shell rm -f brestore.txt
set logging file brestore.txt
set logging on
info break
set logging off
# reformat on-the-fly to a valid gdb command file
shell perl -n -e 'print "break $1n" if /^d+.+?( S+)$/g' brestore.txt > brestore.gdb
shell rm -f brestore.txt
end
document bsave
store actual breakpoints
end
define brestore
source brestore.gdb
end
document brestore
restore saved breakpoints
end

И вывод значений wchar_t строки:

define pwchar
echo "
set $c = ( wchar_t*)$arg0
while ( *$c )
if ( *$c > 0x7f )
printf "
[ %x]", *$c
else
printf "
%c", *$c
end
set $c++
end
echo "
n
end

Вобщем как-то так. Еще планирую написать о том, как искать падения по дампам при помощи GDB, что тоже довольно занимательно.

CodeReview. Теория и практика.

CodeReview – классная штука, не правда-ли? Если отвлеченно подумать об открывающихся возможностях и количестве потенциальных ошибок, найденных при проведении CodeReview, то начинает казаться что практика просто великолепна и мега полезна.
Обычно, путь к внедрению этой практики извилист и тернист, но ништяки, которые по легенде ожидают прошедшего, заставляют забыть о сложной дороге.
Как ни странно, первым делом, обычно, создается стандарт кодирования. Думаю, это вызвано тем что любая уважающая себя компания должна иметь собственный стандарт. В стандарте обязательно нужно прописать где ставить скобочки, а где пробелы, какие индентификаторы писать с прописной буквы, а какие со строчной. Стандарт просто необходимо согласовать, проревьювить раз надцать и утвердить. После этого весь код будет однородным и очень красивым, не зря же стандарт составляли лучшие из лучших компании! Continue reading

Objective-C биндинги

Наверное, наиболее привлекательной возможностью в Objective-C я бы назвал биндинги. И если про то, как связать данные с графическое представлением при помощи InterfaceBuilder написано очень и очень много (даже на русском ), то вот о том, как и почему вся эта магия работает можно найти разве что на сайте Apple.
Итак, вся технология биндингов базируется на двух паттернах: Key-Value Coding и Key-Value Observing. Думаю, для начала стоит написать именно про эти паттерны, а потом перейти к самим биндингам.

Итак, Key-Value Coding позволяет обратиться к данным класса по имени, включая вложенную обращения с учетом вложенной иерархии. Для начала небольшой пример иллюстрирующий, о чем же идет речь.

Пусть будет класс MyClass, следующего вида:

@interface MyClass 
@property NSString *stringProperty;
@property NSInteger integerProperty;
@property MyClass *linkedInstance;
@end

Обычно, доступ к данным этого класса реализуется как-то так:

MyClass *myInstance = [[MyClass alloc] init]; 
NSString *string = myInstance.stringProperty;
myInstance.integerProperty = 2;

MyClass *anotherInstance = [[MyClass alloc] init];
anotherInstance.integerProperty = 5;
myInstance. linkedInstance = anotherInstance;
myInstance. linkedInstance. integerProperty = 10;

В то же время, при использовании Key-Value Coding можно написать так:

MyClass *anotherInstance = [[MyClass alloc] init]; 
[anotherInstance setValue:5 forKey:@"integerProperty"];
[myInstance setValue:anotherInstance forKey:@"linkedInstance"];
[myInstance setValue:[NSNumber numberWithInt:10]
forKeyPath:@"linkedInstance.integerProperty"];

Для работы KVO паттерна необходимо чтобы класс соответствовал следующим условиям:
– Должны присутствовать методы –, -is, либо должна существовать переменная класса <key>;
– Для изменяемых значений так же необходимо наличи метода -set:;
– Метод -set: не должен проверять устанавливаемое значение.
– Если проверка устанавливаемого значения все-же необходима, в классе должен присутствовать метод -validate:error:.
На мой взгляд, самый простой способ поддержать эти требования – это создать это использовать свойства.

Для получения значений атрибутов используется метод valueForKey:. В случае если необходимо запросить значение с использованием пути, необходимо воспользоваться методом valueForKeyPath:. В случае, если запрашиваемый ключ отсутствует возбуждается исключение NSUndefinedKeyException.

Задание значения происходит полностью по аналогии с получением значения. Есть методо setValue:forKey:, устанавливающий значение указанного атрибута и setValue:forKeyPath: если необходимо задать путь. При отсутствии заданного ключа, так же будет возбуждено исключение NSUndefinedKeyException.

А вот функционал операций с коллекциями меня приятно удивил. В Objective-C есть операторы коллекций (Collection Operators), благодаря которым можно манипулировать с коллекциями по подобию SQL-запросов. Само собой, возможности очень ограниченные, тем не менее полезные.
Операторы коллекций записываются следующим образом:
путьВКоллекции.@операторКоллекции.путьКСвойству

Операторы коллекций делятся на 3 группы: простые операторы, операторы объектов и операторы массивов.
К простым операторам относятся @avg, @count, @max, @min, @sum. Их использование выгляди следующим образом. Допустим есть массив объектов Transaction с полями payee, amount и date. Тогда для получения среднего значения по полю amount можно написать такой запрос:

NSNumber *transactionAverage=[transactions valueForKeyPath”@avg.amount”];

А вот с операторы объектов радуют еще больше, например для получения выборки уникальных значений поля payee можно воспользоваться вот таким запросом:

NSArray *payees=[transactions valueForKeyPath"@distinctUnionOfObjects.payee"];

Кроме того, есть еще один оператор объектов – @unionOfObjects.

Операторы массивов, вобщем-то тоже самое что и операторы объектов, только применяются к нескольким массивам, содержащим однотипные объекты, одновременно. Какой-то практической пользы я в них не нашел.

Разве что отсутствие возможности добавить собственный оператор для работы с коллекциями меня огорчило. Может я и не прав, но никакой информации о том, как добавить еще один оператор я не нашл.

Вторая составляющая механизма биндингов это Key-Value Observing (KVO). KVO позволяет получать уведомления об изменении заданных свойств других объектов. Проще говоря, это callback механизм, работающий по заданному стандарту.

Для регистрации observer используется сообщение addObserver:forKeyPath:options:context:. Например для того чтобы получать сообщения об изменениях свойства openingBalance объекта account необходимо послать следующее сообщение:

[account addObserver:self
forKeyPath:@"openingBalance" options:(NSKeyValueObservingOptionNew) context:NULL];

В результате чего, при изменении значения openingBalance объекта account будет вызван метод observeValueForKeyPath:ofObject:change:context: для self. В качестве context можно передать любой C-указатель.

Нотификации об изменении свойства приходит в метод observeValueForKeyPath:ofObject:change:context:. Нужно не забывать о том, что полученное сообщение должно быть проброшено дальше:

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
{
// to-do smth.
[super observeValueForKeyPath:keyPath
ofObject:object change:change
context:context];
}

Если получать нотификации больше не требуется, необходимо вспользоваться методом removeObserver:forKeyPath:.

Несмотря на то, что в большинстве случаев вполне достаточно автоматического извещения об изменениях свойств, уведомления можно сделать и в ручную. Лично мне эта возможность пригодилась только однажды – для оптимизации скорости работы GUI. Дело в том, что при отображении массива динамически изменяемых данных, интерфейс начинает перерисовываться при изменении каждого из элементов массива. И, как следствие, вместо одного обновления всех элементов разом, происходило N обновлений, что требовало дополнительных ресурсов.
К счастью, при помощи метода willChangeValueForKey: можно сообщить о том, что началось изменение значений для определенного ключа и закончить его вызовом метода didChangeValueForKey:.

Честно сказать, после того как становится понятно с KVO и KVC говорить про биндинги уже особо и нечего. Для создания биндинга используется сообщение bind:toObject:withKeyPath:options:, которое говорит получателю поддерживать заданный атрибут в синхронном состоянии. Например сообщение [joystick bind:@”angle” toObject:GraphicController withKeyPath:@”selection.shadowAngle” options:options]; можно интерпретировать следующим образом:
– bind: @”angle” если значение ассоциируемое с angle изменится,
– toObject: GraphicController то необходимо сообщиться заданному объекту GraphicController
– withKeyPath: @”selection.shadowAngle” что его значение selection.shadowAngle было изменено
– options: options используя трансформер options

Так же стоит заметить, что в iOS метод bind:toObject:withKeyPath:options: вообще не доступен. И при необходимости реализуется самостоятельно.

Для упрощения работы с биндигами, существует ряд стандартных контроллеров, облегчающих работу с массивами и древовидными структурами данных. Это NSArrayController, NSTreeController и NSDictionaryController. Кроме того, существуют контроллеры для управления видами NSViewController, объектами NSObjectController и пользовательскими настройками UserDefaultsController.

Смерть Symbian :(

Захожу я сегодня на Symbian Developer, а там! Вобщем, похоже, Нокия решила окончательно убить Symbian, т.к. проект закрывается 17 декабря сего года. Все что на нем есть сейчас можно будет заказать на CD. Тем не менее, на всякий случай, я исходники Symbian-a заначил.. Мало ли?

Да, Symbian SDK остается на сайте Nokia Developer, хотя основной акцент делается все же на QT SDK.

Но все равно грустно, единственная нормальная ОС для мобильных девайсов и тут такое. Эхх

Автосинхронизация с iSync

iSync – замечательное приложение позволяющее синхронизировать практически любой девайс с календарем и адресной книгой на локальном компьютере. По большому счету, у это приложения есть 2 недостатка причем если один из них обойти никак нельзя, то второй недостаток довольно легко обходится.
Во-первых, iSync работает только по Bluetooth. Похоже что тут ничего поделать нельзя, как это не печально. Во-вторых, iSync не умеет запускать синхронизацию по расписанию, но вот эта проблема решается довольно просто.
Заставить iSync синхронизировать календари и контакты самостоятельно можно 2-мя способами. Первый вариант это UNIX-way. Надо не так уж и много, написать небольшой AppleScript и задать расписание в cron.
Создаем iSyncAuto.scpt следующего содержания:

tell application "Finder"
set iSyncRunning to (number of items in (processes whose name is "iSync") is greater than 0)
tell application "iSync" to synchronize
tell application "iSync"
repeat while (syncing is true)
end repeat
if iSyncRunning is not true then
quit
end if
end tell

И добавляем в cton задачу:

crontab -e

* */6 * * * osascript path_to_script/iSyncAuto.scpt

Чтоб сильно не грузить батарею запускаем синхронизацию каждый 6-й час.

Но это сложный, не userfriendly и не Mac-way Теперь тоже самое, но по-Маковски.
Идем на сайт Apple и качаем приложение iSyncIt. Устанавливаем, запускаем, радуемся

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

Убедившись в том что Андроид в целом и Motorola Milestone в частности просто говно несусветное, я задумался о возвращении на по-сути единственную приличную платформу для мобильных телефонов – Symbian. Как раз в конце прошлой недели я продал свой говнодроид и стал искать на что бы его заменить. iPhone отпал сразу, так как платить больше 1К долларов за телефон по моему мнению глупо, да и iOS на телефоне меня мягко говоря не вдохновляла.

Поиск вышел долгим и, похоже, я наконец-то решился – Nokia N8. Но тут речь даже не о самом телефоне, а том, что меня в нем удивило. Судя по тестовым снимкам из обзора на Mobile-review, Carl Zeiss просто творит чудеса! На снимках сделанных смартфоном(!!!) есть хоть какая-то, но глубина резкости!

Да и сам обзор реально доставляет, т.к. по мнению того идиота что его написал, наличие глубины резкости (“детализация предметов вокруг”, в оригинальном исполнении) это минус. И в тоже время немного грустно, ведь такие же идиоты пишут про политику, экономику, культуру…

Как подружить NSColor и NSUserDefaultsController

Почему-то, скорее всего из-за не внимательного изучения документации Apple, я был уверен что без каких-то особых приседаний не возможно сохранить NSColor в NSUserDefaultsController. Ну да, у NSUserDefaultsController есть поддержка только ограниченного количества количества типов, и на просторах интернета можно найти кучу решений по сохранению неподдерживаемых типов, таких как NSColor. Например сохранить в виде NSInteger или, еще более интересный вариант – в виде строки. В то же время, есть такой замечательный трансформер (как эту штуку правильно назвать по-русски?), NSUnarchiveFromData, который позволяет преобразовать любой тип, поддерживающий протокол NSCoding, в тип NSData, который в свою очередь может быть сохранен по средствам NSUserDefaultsController.
Например, для того чтобы привязать объект NSColorWell к NSUserDefaultsController

Даже код писать не надо:

private методы в Objective-C

Когда я только начал работать с Objective-C, мне очень не нравился тот факт, что в этом языке нет возможности объявить какой-либо метод класса приватным. Это действительно доставляет большое количество неудобств, т.к. пользователю класса приходится разбираться с большим количеством методов используемых для внутренних целей класса и совершенно ему не нужных. Continue reading

Дружественные шаблонные операторы

Как человек, довольно редко использующий шаблоны и метаопрограммирование, периодически путаюсь них. Недавно был сильно озадачен, пытаясь написать оператор << для шаблонного класса. Хотелось получить поддержку следующего синтаксиса:

Buffer<int> buff;
int data1 = 100;
int data2 = 200;

buff << data1 << data2;


Казалось бы ничего сложного (да и без казалось ничего сложного), тем не менее пришлось подумать. Основная проблема – синтаксис который позволял использовать компилятор из вендовой студии не сработал:

template<typename T>
class Buffer
{
public:
friend Buffer<T>& operator<< ( Buffer<T> buff, const T& data );

Buffer()
{}
~Buffer()
{}
};

template<typename T>
Buffer<T>& operator<<( Buffer<T>& buff, const T& data )
{
// do smth
return buff;
}

Погуглив, я понял, что эта проблема волнует не только меня, но врет решения предлагались несколько кривоваты, например можно сделать так:

template<typename U>
friend Buffer& operator<< ( Buffer& buff, const U& data );

Но это не то что требуется. Хотя на самом деле, все просто и логично:

template<typename T>
class Buffer;

template<typename T>
Buffer<T>& operator<<( Buffer<T>& buff, const T& data );

template<typename T>
class Buffer
{
public:
friend Buffer<T>& operator<< <T> ( Buffer<T>& buff, const T& data );

Buffer()
{}
~Buffer()
{}
};

template<typename T>
Buffer<T>& operator<<( Buffer<T>& buff, const T& data )
{
// do smth
return buff;
}