Readdle Coding Standard∗ А.А. Буданцов, Д.А. Процеров
1
Введение
Данный документ содержит рекомендации по оформлению исходного кода на языках Objective-C и, в некоторой степени, C/C++. Соблюдение данных рекомендаций поможет снизить дискомфорт при работе с исходным кодом разных разработчиков работающих в Компании, повысить читаемость и понятность кода, избежать проблем при реиспользовании кода в других проектах.
2
Здравый смысл
Данный документ является дополнением к здравому смыслу. Список перечисленных рекомендаций является открытым и не учитывает все богатство фантазии разработчиков.
3
Общие положения
3.1
Длина строки
При написании кода рекомендуется ограничивать длину строки 120 символами. На заметку: В среде разработки Xcode включить индикацию горизонтального положения курсора (колонки) можно в меню Xcode → Preferences... → Text Editing → Display Options → Show column position Рассмотрите следующие варианты, для того чтобы разбить длинную строку на более короткие: • Если в строке присутствует вызов функции (передача сообщения) с большим количеством аргументов — перенесите какие-то из аргументов(каждый аргумент) на новую строку. Многострочные сообщения Xcode прекрасно выравнивает по двоеточиям: ∗ Редакция
1.1
1
UIAlertView *a = [[UIAlertView alloc] initWithTitle:title message:text delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; • Если в строке присутствует вложенные вызовы функций (результат вызова одной функции, является аргументом для другой) — объявите временную переменную строкой выше и поместите туда значение одной из функций; • Логические и арифметические выражения следует разбивать на строки по знакам операций, оставляя сам знак в конце предыдущей строки.
3.2
Пустые строки
Пустыми строками следует разделять: • Реализации функций и обработчики сообщений; • Хорошим тоном является разделение пустой строкой разных логических блоков внутри функции.
3.3
Пробелы
Пробелом следует разделять • арифметические и логические операции, оператор присваивания — выделяются пробелами с обоих сторон: int buttonOffset = frameOffset +
previousButtonHeight + margin;
• составные части конструкции for — пробел ставиться после точки с запятой: for(i = 0; i < 10; i++) • аргументы функции(С) — пробел ставится после запятой: doSomething(arg1, arg2, arg3) Пробелами не следует разделять • в вызове сообщения — имя сообщения/аргумента и его значение: [object callWithValue:value] • в вызове функции — имя функции и открывающую скобку: doSomething(argument) • переменную и операцию инкримента/декримента
2
3.4
Операторные скобки
Открывающая скобка находится на той же строке, на которой происходит объявление блока: - (id)initWithFileName:(NSString *)fileName { ... if (pathToFile && [pathToFile hasSuffix:@"rtfd.zip"]) { ... for(i = 0; i < 10; i++) { ... struct mystruct { // не совсем операторные скобки ... @interface BlackMail : UIView { Если объявление блока занимает несколько строк, то операторная скобка начинается с новой строки: @interface NewGifController : UIViewController { Закрывающая скобка всегда находиться на строке одна, и не может сопровождаться какими-либо другими символами или конструкциями (кроме комментариев). Таким образом, предыдущие правила определяют способ расстановки операторных скобок при использовании else if: if (a == 1) { // code } else if (b == 1) { // more code } else { // even more code }
3.5
* в указателях и функциях
Символ * в объявлении переменных-указателей, возвращаемых значениях функций, аргументах функций — отбивается пробелами от основных типов данных: NSString *RDDocsDirectory(); ... - (void)addColor:(UIColor *)color; ... NSObject *object = nil; ... char *test = (char *)malloc(1024);
3
4
Правила именования
В отношении именования классов, функций, сообщений и пр. мы придерживаемся рекомендаций от Apple: http://developer.apple.com/documentation/ Cocoa/Conceptual/CodingGuidelines/CodingGuidelines.html Документ по ссылке выше рекомендуется для тщательного изучения. Ключевые положения уточняющие этот документ или входящие с ним в противоречие (и имеющие более высокий приоритет) будут изложены в этом разделе позднее.
4.1
Префиксы имен классов и функций
Для классов и функций предназначенных для самого широкого использования, мы используем префикс RD. Авторам кажется удачной идея использовать для отдельных проектов свои собственные префиксы (таких как RD2 для ReaddleDocs2). Вопрос использования/выбора префикса решается для каждого проекта индивидуально.
4.2
Create Rule
Имена методов возвращающих объекты должны обязательно соответствовать Create Rule для Objective-C: You take ownership of an object if you create it using a method whose name begins with “alloc” or “new” or contains “copy” (for example, alloc, newObject, or mutableCopy), or if you send it a retain message. You are responsible for relinquishing ownership of objects you own using release or autorelease. Any other time you receive an object, you must not release it.
4.3
#define
Запрещается использовать #define для • объявления констант • реализации макросов-функций (за исключением случаев, когда это оправдано требованиями производительности)
4.4
Константы
Константы объявляются с использованием обычного C кода. Никакие специальные префиксы (вроде ‘k-’) в именах констант не используются. Константа действующая внутри файла, определяется как static int const RD2MagicNumberUniverseAnswer = 42; Глобальные константы для проекта объявляются с использованием обычного extern. В m-файле: int const RD2MagicNumberUniverseAnswer = 42;
4
В h-файле: extern int const RD2MagicNumberUniverseAnswer;
5
Прочие рекомендации
5.1
init и dealloc
В исходном коде рекомендуется располагать реализацию обработчика сообщения dealloc сразу после обработчиков сообщений init-. . . .
5.2
Переменные класса
В случае, если в реализации класса есть шанс перепутать переменную класса и переменную объявленную внутри метода (или являющуюся аргументом метода), рекомендуется предварять имя переменной класса символом _. Например: - (void)setObject:(RDObject *)object { _object = [object retain]; [self doSomething]; } Хорошей альтернативой является использование артиклей для имен аргументов: - (void)setObject:(RDObject *)anObject { object = [anObject retain]; [self doSomething]; } Символ подчеркивания в имени аргумента — это плохо.
5.3
NSLocalizedString
Все строки пользовательского интерфейса должны присутствовать в исходном коде внутри макроса NSLocalizedString. NSLocalizedString(строка, описание того, где она используется) Например: email = [UITextField alloc] initWithFrame:...]; email.placeholder = NSLocalizedString(@"e-mail", @"placeholder for email field"); По-умолчанию NSLocalizedString вернет свой первый аргумент. На начальных этапах работы мы просто пользуемся этим фактом, позже NSLocalizedString используется при переводе продукта на другой язык.
5
5.4
NSAssert
Настоятельно рекомендуется использовать NSAssert для проверки условий при которых работа программы окажется бессмысленной или невозможной. NSAssert(условие, ошибка для случая если условие не выполняется) Например: FILE *f = fopen(file, "r"); NSAssert(f != NULL, @"unable to open the File"); ... Существуют так же NSAssert1, NSAssert2, NSAssert3, NSAssert4, NSAssert5 у которые есть дополнительные аргументы, на которые можно ссылатся (форматной строкой, как в NSLog) из описания ошибки. NSAssert прерывает работу программы и должен использоваться только для фатальных ошибок.
5.5
ReaddleLib
К использованию во всех проектах Readdle предлагается ReaddleLib (ReaddleLib.h, ReaddleLib.m) содержащие некоторые функции и расширения системных классов.
5.6
#pragma mark
Хорошим тоном является разбиение длинных файлов на логические секции при помощи #pragma mark. Для наглядного выделения, мы добавляем по две директивы #pragma mark - выделяя в Xcode заголовок тонкими линиями. #pragma mark #pragma mark Заголовок #pragma mark -
5.7
Выравнивание в объявлении переменных класса
Как свидетельствует опыт, использование выравнивания в объявлении переменных класса ведет к улучшению читаемости кода. @interface TestClass : NSObject { NSInteger member1; NSInteger member2; RDSomeLongClassName *classMemeber1; } @property (nonatomic) NSInteger member1; @property (nonatomic, readonly) RDSomeLongClassName *classMemeber1;
6