Архив за Июль 2008

Mémoires — лучший способ вести дневник на Маке
Пока вы тут скучаете, а спаммеры замусоривают комментарии, я создал Google-группу под названием sellme-dev. Чтобы не отвлекать людей, далеких от программирования, я буду туда писать свои заметки про программирование (=продолжать показывать свою некомпетентность в этой области). Это группа не только для моих дурацких заметок. Я надеюсь, там мы будем обсуждать всякие программистсткие штучки, в основном касающиеся Mac OS X. Но и не только — все велкам. Дебилов буду отписывать, поэтому там не будет такого. Сейчас там вот что есть: Группа тут — http://groups.google.com/group/sellme-dev Понеслась! Обновление: накидаю ключевых слов, чтобы было понятно, о чем эта группа: cocoa, objective-c, c, mac os x, *nix, python, ruby, wxwidgets, mach, programming, xcode, operating systems etc. Обновление 2: не забываем, что это по сути mail list, если охота; не обязательно все смотреть через веб-интерфейс.
Самые популярные спамерские комментарии в блогах — слова благодарности за заметку. На анонс Mémoires еще круче пришел спам (неавтоматизировнный) — слова благодарности за программу и ссылки на какую-то SEO-шную лабуду. Мне надоело проверять встроенным в мою голову антиспамом реальное это спасибо или спамерское, поэтому далее я буду удалять все слова благодарности, не несущие полезной информации. Например, "спасибо за xxx! отличный xxx" — удаляется. А "Спасибо за xxx. Только я не согласен с тем, что yyy. Надо вот так: zzz." — нет. Спасибо всем за спасибы, теперь я по умолчанию буду считать, что вы говорите мне слова благодарности, прочитав заметку; писать об этом не надо :-)
Добавлена возможность выбора шрифта по умолчанию, улучшен вид в Leopard (нижняя панелька выглядит как в iCal, левая становится серой, если окно неактивно, улучшенные иконки для дневников), запуск программы стал быстрее (я лоханулся в предыдущих версиях), импорт из последних версий MacJournal и Journler (задолбали формат файлов менять!), всякие исправления ошибок (в том числе утечки памяти). Красота: Mémoires screenshot Скачать можно с сайта Mémoires (который немножко обновился на временный дизайн, пока не сделаю нормальный). Обновление: Из-за моего тупизма 1.2.42 не работал в Mac OS X 10.4. Только что выпустил 1.2.43. Сорри!
Языыык!!! Один пример, и я заткнусь (на сегодня). (Осторожно, заметка представляет из себя недефрагментированные мысли из головы.) Пишем метод, который берет такие параметры: имя файла (строка), надо ли сжимать файл (bool), и callback для индикации прогресса (указатель на метод). Как вызывать этот метод для объекта doc, с файлом test.txt, с компрессией и отсутствием коллбэка? C++: bool success = doc->WriteFile("test.txt", true, NULL); C#: bool success = doc.WriteFile("test.txt", true, null); Python: success = doc.write_file("test.txt", True, None); Ruby: success = doc.write_file("test.txt", true, nil); А теперь запустим симуляцию программера, который писал это ночью, проснулся к обеду и забыл про определение метода; посмотрим на код вызова. Что за хрени эти "test.txt", true и nil? Чего мы передаем в функцию? С именем файла вроде понятно, а остальное — это что? Вот что официальные правила кодирования на C++ в Google говорят:
When you pass in NULL, boolean, or literal integer values to functions, you should consider adding a comment about what they are, or make your code self-documenting by using constants. For example, compare:

bool success = CalculateSomething(interesting_value,
                      10,
                      false,
                      NULL);  // What are these arguments??
versus:

bool success = CalculateSomething(interesting_value,
                      10,     // Default base value.
                      false,  // Not the first time we're calling this.
                      NULL);  // No callback.
Бедные вечнокомментирующие гугловские программисты, так жаль их. Встречайте Objective-C: BOOL success = [doc writeFileToPath:@"test.txt" withCompression:YES callback:nil]; Знаю, что глаза у вас затуманенны с непривычки. Вам подсветить? BOOL success = [doc writeFileToPath:@"test.txt" withCompression:YES callback:nil]; Опа, нам не надо заглядывать в определение функции (или писать комментарий), потому что название метода уже содержит всю нужную информацию. Это именно название метода — writeFileToPath:withCompression:callback:, а не передача параметров в виде хэша/словаря, как это можно сделать в Ruby/Python. Вот, если хотите, большой метод:

NSAlert *alert = [NSAlert alertWithMessageText:@"Passwords do not match"
			  defaultButton:@"OK"
			  alternateButton:nil
		 	  otherButton:nil 
			  informativeTextWithFormat:@"Crap! Can you type?"];
* * * Objective-C — компилируемый язык, всего лишь небольшая объектно-ориентированная надстройка над C (в отличие от C++, который совсем другой язык). Он динамический, и в нем не обязательно делать типизацию (NSString *myString — с типом, id myString — пофиг на тип).
Одной из отличительных черт Objective-C является его динамизм — целый ряд решений, обычно принимаемых на этапе компиляции, здесь откладывается непосредственно до этапа выполнения. Ещё одной из особенностей языка является то, что он message-oriented в то время как С++ — function-oriented. Это значит, что в нём вызовы метода интерпретируются не как вызов функции (хотя к этому обычно все сводится), а именно как посылка сообщения (с именем и аргументами) объекту, подобно тому, как это происходит в Smalltalk-е. Такой подход дает целый ряд плюсов — так любому объекту можно послать любое сообщение. Объект может вместо обработки сообщения просто переслать его другому объекту для обработки (так называемое делегирование), в частности именно так можно легко реализовать распределенные объекты (то есть объекты находящиеся в различных адресных пространствах и даже на разных компьютерах).
(википедия) С Objective-C можно делать многое интересное, что можно сделать с языками вроде Python и Ruby и нельзя сделать с языками типа C++. При этом, так как это Си с надстройкой, код можно смешивать: в Си-функциях использовать посылку сообщений ObjC объектам, а из ObjC-методов вызывать функции Си. В Objective-C 2.0 есть properties и (опциональная) сборка мусора. Товарищи, на этом языке писать — одно удовольствие! * * * Напоследок, еще немножко примеров с Google'овскими рекомендациями по C++:
Doing Work in Constructors Do only trivial initialization in a constructor. If at all possible, use an Init() method for non-trivial initialization. * There is no easy way for constructors to signal errors, short of using exceptions (which are forbidden). * If the work fails, we now have an object whose initialization code failed, so it may be an indeterminate state. * If the work calls virtual functions, these calls will not get dispatched to the subclass implementations. Future modification to your class can quietly introduce this problem even if your class is not currently subclassed, causing much confusion. * If someone creates a global variable of this type (which is against the rules, but still), the constructor code will be called before main(), possibly breaking some implicit assumptions in the constructor code. For instance, gflags will not yet have been initialized. Decision: If your object requires non-trivial initialization, consider having an explicit Init() method and/or adding a member flag that indicates whether the object was successfully initialized.
В Objective-C нет конструкторов по умолчанию, и принято писать init-методы: например, init, initWithFileName, initWithSomethingCool:doMore: и т.п. Создание объекта происходит таким образом: [[SomeClass alloc] initWithFileName:fileName]. Другой способ — метод класса, который сам возвращает инициализированный объект (см. пример с NSAlert выше).
The #define Guard All header files should have #define guards to prevent multiple inclusion. The format of the symbol name should be project_path_file_H_. link To guarantee uniqueness, they should be based on the full path in a project's source tree. For example, the file foo/src/bar/baz.h in project foo should have the following guard:

#ifndef FOO_BAR_BAZ_H_
#define FOO_BAR_BAZ_H_

...

#endif  // FOO_BAR_BAZ_H_
Бедняжки. В Objective-C можно использовать #include, но для защиты от того, чтобы файл не включился повторно, есть #import: #import "foo/src/bar/baz.h" * * * Кстати, есть еще Objective-C++ — это если лень переписывать существующий C++ код. * * * Ну а если вам неохота писать на Objective-C, тогда для обращения к Cocoa можно использовать Ruby или Python (они поставляются с Mac OS X) — благодаря тому, что рантайм ObjC (на котором написан Cocoa) весь такой из себя динамический. И это не теория — например, Checkout написан на PyObjC. * * * Обновление: Про категории забыл! В Objective-C, как в Ruby, можно добавить метод в любой класс — например, добавить метод для перевода строки в "LOLspeak" сразу в NSString:

@interface NSString (LOLSpeak_Additions)
  - (NSString *)lolSpeakStringFromString:(NSString *)normalString;
@end
Это очень полезная вещь, я вам говорю.
Статья про шаровару на BizTimes. Интересное интервью с Сергеем Корниловым:
Каковы основные рынки сбыта shareware? В вашем случае? Мы распространяем наши программы только через онлайн. Основная масса покупателей идет из Северной Америки и Европы. До оффлайна и региональных представителей мы еще не доросли. Основной трафик на наш вебсайт доставляет Google Adwords. Специализированные вебсайты и журналы вносят свою долю, но она ощутимо меньше.

Я тут подумал и решил, что это не шароварщики пока не доросли до оффлайна, а оффлайн пока не дорос до шароварщиков. Да, оффлайновый рынок пока большой, но он постепенно уменьшается, уступая места онлайну. Во-первых, оффлайн — геморрой для покупателя, и еще больший геморрой для вендоров (мало того, что производство дисков обходится дороже трафика с сайта, так еще и вся цепочка дистрибьюторов забирает огромный процент). А во-вторых, скоро эти диски вообще некуда будет воткнуть — например, у MacBook Air и Asus eeePC нет CD/DVD приводов.

Холивор! Комментарий к предыдущей заметке позабавил:
:)) XAML’ий binding и мощнее и более ортогонален всему WPF. А тут любимая Apple’ая манера поведения: присобачить нарост сбоку и получившейся «фичей» внушать неофитам ощущение собственной значимости.
Позабавил тем, что WPF, в отличие от фич Cocoa, которые основаны на отличной архитектуре, как раз и есть нарост сбоку. Сначала Microsoft выпустили WinForms. Через лет пять после выхода WinForms наконец-то стали появляться коммерческие приложения для конечных пользователей, написанные на нем (пришлось смириться с тем фактом, что в Windows XP не включен .NET runtime, заставили юзеров его качать). Википедия прекрасно объясняет проблемы с ним прямо в описании WinForms:
While it is seen as a replacement for the earlier and more complex C++ based Microsoft Foundation Class Library, it does not offer a paradigm comparable to model-view-controller. Some after-market and third party libraries have been created to address this issue.
Microsoft поняли это и спустя несколько лет предложили совершенно другой фреймворк с другими виджетами (в XP они ведут себя и выглядят совсем не как нативные) — Windows Presentation Foundation. MVC дошло! Ура! Удачи в переписывании ваших прог. Дайте-ка вспомнить, какие end-user проги самой Microsoft написаны с использованием WinForms... Щас... Ах, помню — Windows Live Writer — прога, которую в MS написал купленный ею Onfolio. Дайте подумать что еще... Не думается, но кажется, уже многие программисты переключились на WinForms. Ага? Все-таки этой технологии уже больше 5 лет. Ой, правда, теперь надо о ней забыть и писать на новом WPF. Посмотрим, какие проги Microsoft написаны с использованием Windows Presentation Foundation... Программа синхронизации Zune, наверное на ней написана, ведь там модный интерфейс, да и программа достаточно свежая — Zune не так давно вышел.
As others have pointed out, much of the GUI is written with an assembly/API called "Iris" (UIX.dll). It's managed code, and appears to be centered around web-oriented multimedia presentation. Why they're using this instead of WPF is anyone's guess.
(источник) Упс. Нет, что-то другое.
Whilst all of this is all well and interesting, the thing that really captures my attention is why they would invest so much time into developing this framework and implementing it just for [t]his software when they have plenty of alternatives like Windows Presentation Foundation (WPF) to work with. I can’t stop wondering if this is going to become a new framework offering by Microsoft for all developers to use.
(источник) Ну ладно. Посмотрим в википедии:
WPF is intended to be the next-generation graphics API for Windows applications on the desktop. The following are some of Microsoft's own products or other third-party mainstream applications written in WPF:
  • Microsoft Expression Design
  • Microsoft Expression Blend
  • Yahoo! Messenger
  • Nokia Music PC Beta
  • Clear Office Spreadsheet [2]
  • MuvUnder Cover: The Album Art Sleuth [3]
Вау, программа редактирования файлов для WPF написана на WPF! Поразительно. А еще что? По-моему, я слышал, "Сапер" и "Косынка" в Vista написаны с использованием WPF. Упс, нет, оказалось неправдой:
Q: Apparently the games use a custom framework and pure Direct3D. Are there any plans to take advantage of Windows Presentation Foundation, or is this planned for the next major version of Microsoft Windows? A: No plans at this time.
(источник) Ладно, дадим Windows Presentation Foundation пять лет, все-таки хороший фреймворк. Главное, чтобы Microsoft, пока мы привыкаем к нему, не выпустила еще какой-нибудь другой... * * * Как же не везет программистам Mac OS X! У них нихрена нового нет. Их фреймворк, который нынче называется Cocoa, был написан в конце 80-х, сразу по правильной методике MVC и с правильным динамическим языком. Cocoa развивается с каждым выпуском операционной системы. У нее практически нет "теоретических" фич — Apple придумывает что-то для своих программ, а потом дает те же инструменты сторонним разработчикам. Надоело писать код для контроллеров, который повторяется в приложениях? Давайте добавим класс встроенных контроллеров. Язык и фреймворк позволяет. Все стали использовать SQLite для программ? А почему бы не взять и не обернуть его в удобный для использования API — Core Data? КАК ЖЕ СКУЧНО! Почему бы не придумывать новые фреймворки каждые 5 лет? Хрен с ним, что придется все переписывать. * * * Правильная база важна для любого дела, а особенно для программирования. NeXT выбрали отличную архитектуру, которая работает уже около тридцати лет и развивается дальше без "наростов сбоку" — Objective-C и NeXTStep/Cocoa. Конечно, и у Apple были свои грешки — устаревшие ныне (но работающие) фреймворки, но они позволили безболезненно перейти с одной на совершенно другую операционную систему с малыми затратами (я говорю про Carbon, который используют большинство продуктов Adobe и Microsoft для OS X, и Java/Cocoa bridge). Тем не менее, проблема выбора фрейморка, под который надо писать программы для Mac OS X теперь не стоит — Cocoa. .NET — хорошая штука. WPF, наверное, хорошая штука. Проблема в том, что сама Microsoft не торопится их использовать для своих же продуктов. Они написаны исходя из теоретических задумок. Я был на "Платформе-2007", где много показывали демо-программ на WPF: вот, смотрите, можно сделать программу с такими кружочками и с такими квадратиками. Это все замечательно, но где реальные программы для конечного пользователя? Где реальная поддержка этих всех чудес операционной системой?
Newer versions of the framework (3.5 and up) are not pre-installed on any versions of the Windows operating system. Some developers have expressed concerns about the large size (around 54 MiB for end-users with .NET 3.0 and 197 MiB with .NET 3.5) and reliability of .NET framework runtime installers for end-users.
(источник) Такая херня и возникает как раз из-за того, что программы, включенные в поставку ОС не написаны на фреймворках, которые рекламируются для сторонних программеров. И это тупо.
Ненавижу какао. Я люблю Cocoa, потому что в нем не надо писать код. Код вреден. Его потом придется читать. Он будет содержать баги. Долой код! Сейчас покажу, что я имею в виду. Так как я беру пример из живого сверхсекретного проекта, который я пишу по заказу минобороны Магратеи, я не буду раскрывать название программы и описывать точную задачу. Заодно я заблюрил намеки на картинках и в коде. Сорри. Задача: Сделать окно (sheet) для дополнительных настроек, на котором будет:
  • возможность выбрать папку;
  • два чекбокса с полями для ввода цифр; при активации/деактивации чекбоксов соответствующие поля ввода должны разрешать/запрещать ввод; в первое поле можно ввести только целое число больше 1, во второе — целое или дробное положительное число;
  • две кнопки: Cancel для закрытия окна без сохранения изменений и Done для закрытия окна и сохранения изменений;
  • настройки должны читаться из и сохраняться в файл настроек приложения (~/Library/Preferences/com.codingrobots.AppName.plist).
(Про Use Defaults я расскажу в отдельной заметке.) Как это обычно бывает, сначала я начал все делать через ж... (привычка из виндового прошлого). Я начал с написания кода. Очевидно, при нажатии на кнопку открытия окна надо загрузить все настройки из файла (NSUserDefaults нам в этом помогает, с файлами не надо работать) в контролы на окне. Потом написать код для enable/disable полей ввода при нажатии на соответствующие чекбоксы. И не забыть, что в поля можно вводить только особый вид данных (ну ладно, тут NSNumberFormatter можно кинуть на поле). При нажатии на Done надо взять из контролов данные и запихнуть их в NSUserDefaults и убрать окно. Cancel, конечно, должен просто убрать окно. Так я и сделал. Код занимал полтора-два экрана (в 1050 пикселей). Потом я подумал. И сократил код до одного экрана. Потом я подумал еще, покликал в Interface Builder'е и получил вот такой код: nonImmediateUserDefaultsController (NSUserDefaultsController) — это такая штука в IB (которую я перетащил из палитры), к которой прибайндены все контролы. Все это благодаря отличной имплементации MVC (Model-View-Controller) в Cocoa. Models — это данные, views — это окна и все остальное, что рисуется в Interface Builder, controllers — это код, который мы пишем для связывания данных и интерфейса. Задача Apple — убрать код. Код для views убирается Interface Builder'ом — мы рисуем в нем окошки и контролы. Код для models убирается использованием NSUserDefaults в данном случае и Core Data в Mémoires. Код для controllers убирается использованием встроенных контроллеров: NSUserDefaultsController, NSObjectController, NSArrayController, NSTreeController и прочих. С каждым новым релизом Cocoa нам надо писать все меньше и меньше кода. Кстати, если бы нам не нужно было закрывать окно при нажатии на кнопки (странный случай), либо настройки надо было бы обновлять сразу, без нажатия Done (нормальный случай), то код вообще не надо было бы писать. Таким же образом в Cocoa не надо писать код для управления окнами, для вызова Open и Save диалогов при нажатии на соответствующие пункты меню... ну и вообще, многое просто "дано" (и это совсем не значит, что ничего нельзя поменять). Cocoa makes easy things easy and hard things possible.