Языыык!!!

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

Пишем метод, который берет такие параметры: имя файла (строка), надо ли сжимать файл (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

Это очень полезная вещь, я вам говорю.

alberka 2008-07-07 19:08

А в C# (Visual Studio) он наведет курсором на открывающую скобку, нажмет ctrl+shift+space и получит подробное описание каждого передаваемого параметра (разумеется, если не поленился написать это описание)...

alberka: Так это и так понятно, что типичный C# программист не умеет писать код вне IDE :) Впрочем, аргумент неважный — зачем писать код вне IDE я не знаю :)

Дело в том, что код чаще читается, чем пишется. А мышка не заменит глаз — лучше, когда все сразу видно.

Nikolay 2008-07-07 19:08

А что мешает использовать в питоне именованные аргументы?

То есть писать не success = doc.write_file("test.txt", True, None);

но success = doc.write_file(tofilewithname="test.txt", withCompression=True, callback=None);

jimbo 2008-07-07 19:08

Во-первых, пример с компрессией и коллбэком очень неудачный. В ОО-языке, где document -- это объект, неплохо бы использовать events. Параметр колбэк отпадает. Флаг true/false можно заменить на enum и всё будет читабельно. Вывод: неплохо бы иметь нормальный стиль разработки.

Во-вторых, в MS C++ есть #pragma once, которая работает #define guard.

В-третьих, в чём прелесть написания софта вне IDE? Можно, конечно, обходиться Notepad и командной строкой, но зачем? IDE не мешает автоматизировать билд, если нужно, но предоставляет уйму полезных инструментов. Почему-то никого не удивляет, что хорошему столяру нужна мастерская...

jimbo 2008-07-07 19:08

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

Nikolay:

keyword arguments в питоне рулят. Без них под wxPython писать было бы в 1000 раз геморройнее.

jimbo:

Во-первых, пример с компрессией и коллбэком очень неудачный.

Нормальный пример. Какая разница, коллбэк-не коллбэк — смысл в том, что не читабельны параметры. enum — выход. Ага, придется enum'ы для всех true/false делать.

Во-вторых, в MS C++ есть #pragma once, которая работает #define guard.

Ключевое слово — MS C++.

В-третьих, в чём прелесть написания софта вне IDE? Можно, конечно, обходиться Notepad и командной строкой, но зачем?

Я про это и говорю.

Виталий 2008-07-07 19:08

дима, а чего комменты не публикуются? цензура? ;)

Виталий 2008-07-07 19:08

а этот опубликовался. а два больших до этого - нет. абыдно. еще раз написать?

Виталий 2008-07-07 19:08

или у тебя фильтр на слова "obective-c" и "дебильный" в одном сообщении? ;)

Виталий: в админке нету, антиспама никакого кроме капчи не стоит. Модерация только для комментирующих в первый раз. Глюк какой-то что ли? Напищи еще если не лень.

Виталий 2008-07-07 19:08

начну с конца. с категорий. дима, ты представляешь что категории сделают с мало-мальски серьезным проектом? десять программистов добавили по методу в NSString. убиться. вообще их не рекомендуют использовать всерьез. так, изредка и в рамках файла - не вопрос. еще ими удобно описывать private-методы.

имена переменных. в С++ предпочтительны ошибки времени компиляции, Obj-C предпочитает ошибки времени выполнения. оставим это уродство за кадром, хочется провести аналогию, но наоборот. программист с++ будет вспоминать завтра названия параметров. а программист obj-c должен будет вспомнить их уже сегодня, чтобы хоть что-то написать ;) да, есть кнопка Esc, но так и в Visual Studio есть Ctrl+Space и, кстати, получше работающая ;)

Виталий 2008-07-07 19:08

едем дальше, скобки. ненавижу скобки. у меня в проекте ядро на с++, надо строчками меняться. знаешь как весело городить эти скобки (не дает форум кусок кода вставить).

вряд ли знаешь, иначе бы не писал про скобочки хорошие слова ;)

скобки - уродство. эппл косвенно с этим соглашается, вводя проперти и пряча от нас половину скобок за автогенерацией.

сборка мусора. на ADС видел статейку про создание классов, которые одинаково успешно могут работать с retain/release и с GC. статью написали. большую... не все так просто, да? ;)

резюмирую: cocoa - очень неплохой фреймворк, но язык - дебильный.

Виталий 2008-07-07 20:08

че-то не то с форумом. пытаюсь кусок кода написать - сообщения не публикуются. клево ;)

Виталий:

а программист obj-c должен будет вспомнить их уже сегодня, чтобы хоть что-то написать ;)

Зато ему же потом читать удобней.

начну с конца. с категорий. дима, ты представляешь что категории сделают с мало-мальски серьезным проектом?

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

Перечисляю категории для Mémoires:

@interface NSString (CRAdditions)

- (NSString *)stringByWrappingWithLineLength:(unsigned)lineLength;

- (NSString *)stringByRemovingWhiteSpace;

@end

@interface NSData (NSDataAdditions)

+ (NSData *) dataWithBase64EncodedString:(NSString *) string;

- (id) initWithBase64EncodedString:(NSString *) string;

- (NSString *) base64Encoding;

- (NSString *) base64EncodingWithLineLength:(unsigned int) lineLength;

- (BOOL) hasPrefix:(NSData *) prefix;

- (BOOL) hasPrefixBytes:(void *) prefix length:(unsigned int) length;

@end

(копи-пейст, кто-то странно категорию назвал. Надо переделать).

@interface NSData (AQDataExtensions)

- (NSData*)dataEncryptedWithPassword:(NSString*)password;

- (NSData*)dataDecryptedWithPassword:(NSString*)password;

@end

@interface NSFileManager (CRAdditions)

- (void)securelyRemoveFileAtPath:(NSString *)path;

@end

@interface NSMutableAttributedString (CRAdditions)

- (unsigned int)replaceOccurrencesOfString:(NSString *)target withString:(NSString*)replacement options:(unsigned)opts range:(NSRange)searchRange;

@end

Что в них плохого? Удобство заключается в том, что не нужно переписывать существующий код для использования других классов, достаточно поменять вызов метода (например, в предыдущих версиях вызывался removeFileAtPath:handler:, в нынешней — securelyRemoveFileAtPath:.

Виталий 2008-07-07 20:08

а еще (ну а где мне еще жаловаться? ;) они в IB3.0 отрубили возможность Ctrl+Drag'а action'ов от кастомных контролов. кидаешь какой-нибудь NSButton - и от него отлично тащишь action. а кинешь NSView, поставишь ему свой класс наследник NSControl - а там раз и нету группы Sent Action. охренеть. написал им багрепорт - в 2.5 все работало. одно смущает: никто не замечал этого почти год?

jimbo 2008-07-07 20:08

Дмитрий, ключевое слово "Objective C". Который вообще не похож C. Код с #pragma once хотя бы скомпилируется другими, поскольку #pramga -- это стандартная фича C++.

Можно, конечно, думать об enum'ах в пределах true/false, а можно подумать о Compression_Deflate/Compression_Deflate64/Compression_LZMA. Возвращаясь к дизайну класса document, можно бы вообще иметь проперть Compression в самом документе, что убирает ещё один из параметров метода.

jimbo 2008-07-07 20:08

Добавлю: если очень хочется посмеяться над "ущербным" C++, то достаточно попробовать описать тип колбэка-члена класса, не заглядывая ни в какие howto. Полчаса веселья обеспечено! :)

едем дальше, скобки. ненавижу скобки.

Осторожно, сейчас придут злобные лисписты и закидают тебя своими скобками!

Скобки — окей. Не то, чтобы хорошо, но и не плохо. Просто по-другому. Хорошо, что не "->".

сборка мусора. на ADС видел статейку про создание классов, которые одинаково успешно могут работать с retain/release и с GC. статью написали. большую… не все так просто, да? ;)

Неа, все просто. Статья — про создание фреймворков, которые люди будут хотеть присоединить как к GC-проектам, так и к reference counted.

пытаюсь кусок кода написать - сообщения не публикуются.

Плохой код не принимается. Смотри — мои категории принялись :-)))

(На самом деле, хз почему — пришли мне на мыло, я вставлю в твой комментарий — dmitry@sellme.biz).

а еще (ну а где мне еще жаловаться? ;) они в IB3.0 отрубили возможность Ctrl+Drag’а action’ов от кастомных контролов.

Наверное, ты забыл, что у NSControl action и target возвращают action/target Cell.

Я попробовал и все получилось:

@implementation MyControl

+ (Class)cellClass;

{

return [NSActionCell class];

}

- (void)mouseDown:(NSEvent *)theEvent;

{

if ([self target] != nil && [[self target] respondsToSelector:[self action]])

[[self target] performSelector:[self action] withObject:self];

}

@end

картинко. и еще.

(хотя я юзаю Xcode 3.1 — в Xcode 3.0 не проверял)

mekal 2008-07-07 22:08

я прочитал первую строчку... и последнюю! и я поверил!!1)

Виталий 2008-07-07 22:08

дима, а покажи @interface... на самом деле это задница. потому что у меня, в принципе, все то же самое. включая +cellClass. впрочем, в .h-файле его нет, по идее IB о нем и не знает.

я пишу минимальный вариант

@interface MyControl : NSControl

{

}

@end

вполне легальный "контрол", который по мнению IB2.5 мог посылать экшены. в 3.0 отказывается.

вопрос номер два: где взять IB3.1?

Vladimir 2008-07-07 23:08

Начал писать про именованные аргументы в Python, но потом заметил что уже написано выше про них. Еще добавлю что в Python 3 есть новая фича для документирования.

def foo (x: "First parameter", y: "Second parameter") -> "Summ":

return x + y

После имени каждого параметра функции через двоеточие можно задать любое выражение, которое может быть вычислено на момент описания функции. Это выражение будет связано с именем параметра в специальном словаре. Также после описания всех параметров, но до символа : можно задать выражение (с помощью оператора -> ), которое будет связано с возвращаемым значением. Самым очевидным применением этой возможности является задание текстовых строк, описывающие входные параметры и возвращаемое значение.

Виталий 2008-07-08 00:08

а, все. сам нашел, скачал, поставил. да, в 3.1 все отлично пашет, группа Sent Actions есть и все таскается. а в 3.0, который раздают мак-девелоперам - не пашет. скотство какое...

Интерфейс такой же. IB 3.1 из iPhone SDK beta 8.

Виталий 2008-07-08 18:08

еще чуток подумал: а ты не совсем прав в -mouseDown

по идее ведь надо так:

[self sendAction:[self action] to:[self target]]

нет?

Ага, верно.

Сергей Шепелев 2008-07-09 23:08

Бедные, бедные гугловские плюсники...

ВАААУ, круто, прям как в Visual Basic'e Ж))

Владимир Захаров: не гони, в VB функций с названиями, перемежающимися аргументами.

^^ имелось в виду "нет".

D@rkNeo 2008-07-20 15:08

брр. мне кажется, objective-c - извращение какое-то, а вот вариант..

>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.

.. мне намного симпатичней.

времени убьешь больше - зато читать удобней. намного. имхо