Каковы лучшие практики, которые вы используете при написании Objective-C и какао?

348

Я знаю о HIG (что весьма удобно!), но какие методы программирования вы используете при написании Objective-C, и более конкретно при использовании Cocoa (или CocoaTouch).

Теги:
iphone
cocoa
cocoa-touch

33 ответа

380
Лучший ответ

Есть несколько вещей, которые я начал делать, я не считаю их стандартными:

1) С появлением свойств я больше не использую переменные класса "_" для префикса "private". В конце концов, если переменная может быть доступна другим классам, не должно существовать для нее свойство? Я всегда не любил префикс "_", чтобы сделать код более уродливым, и теперь я могу его оставить.

2) Говоря о частных вещах, я предпочитаю размещать частные определения методов в файле .m в расширении класса, например:

#import "MyClass.h"

@interface MyClass ()
- (void) someMethod;
- (void) someOtherMethod;
@end

@implementation MyClass

Зачем загромождать файл .h с вещами, которые посторонние не должны волновать? Пустой() работает для частных категорий в файле .m и выдает предупреждения компиляции, если вы не реализуете объявленные методы.

3) Я взял, чтобы положить dealloc в начало файла .m, чуть ниже директив @synthesize. Разве вы не должны быть в верхней части списка вещей, о которых вы хотите думать в классе? Это особенно верно в среде, такой как iPhone.

3.5) В ячейках таблицы сделать каждый элемент (включая сам ящик) непрозрачным для производительности. Это означает настройку соответствующего фона фона во всем.

3.6) При использовании NSURLConnection, как правило, вы можете захотеть реализовать метод делегата:

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
                  willCacheResponse:(NSCachedURLResponse *)cachedResponse
{
      return nil;
}

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

Также интересны некоторые полезные советы iPhone от Джозефа Маттиелло (полученные в списке рассылки iPhone). Их больше, но они были наиболее полезными, я думал (обратите внимание, что несколько бит теперь слегка отредактированы из оригинала, чтобы включить в него предложения):

4) Используйте только двойную точность, если это необходимо, например, при работе с CoreLocation. Убедитесь, что вы закончили свои константы в 'f', чтобы gcc сохранил их как float.

float val = someFloat * 2.2f;

Это в основном важно, когда someFloat может фактически быть двойным, вам не нужна математика в смешанном режиме, поскольку вы теряете точность в 'val' на хранилище. Хотя числа с плавающей запятой поддерживаются на оборудовании на iPhone, для выполнения арифметики с двойной точностью, в отличие от одиночной точности, может потребоваться больше времени. Ссылки:

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

5) Задайте свои свойства как nonatomic. По умолчанию они atomic, а при синтезе создается код семафора для предотвращения многопоточных проблем. 99% из вас, вероятно, не должны беспокоиться об этом, и код намного раздувается и более эффективен с точки зрения памяти при установке на неатомический.

6) SQLite может быть очень и очень быстрым способом кэширования больших наборов данных. Например, приложение карты может кэшировать свои фрагменты в файлы SQLite. Самая дорогая часть - дисковый ввод-вывод. Избегайте многих мелких записей, отправляя BEGIN; и COMMIT; между большими блоками. Мы используем 2-секундный таймер, например, который сбрасывается на каждый новый submit. По истечении этого срока мы отправляем COMMIT;, что заставляет все ваши записи перемещаться в один большой кусок. SQLite хранит данные транзакций на диск и делает эту операцию "Начало/Окончание" позволяет избежать создания многих файлов транзакций, группируя все транзакции в один файл.

Кроме того, SQL будет блокировать ваш графический интерфейс, если он находится в основном потоке. Если у вас очень длинный запрос, рекомендуется хранить ваши запросы как статические объекты и запускать SQL в отдельном потоке. Обязательно оберните все, что изменяет базу данных для строк запроса в блоках @synchronize() {}. Для коротких запросов просто оставьте вещи в основном потоке для удобства.

Дополнительные советы по оптимизации SQLite приведены здесь, хотя документ выглядит устаревшим. Многие из них, вероятно, все еще хороши;

http://web.utk.edu/~jplyon/sqlite/SQLite_optimization_FAQ.html

  • 1
    Ух какой классный список. Спасибо за пост!
  • 3
    Хороший отзыв о двойной арифметике.
Показать ещё 19 комментариев
113

Используйте стандартные Cocoa соглашения об именах и форматировании и терминологию, а не то, что вы использовали в другой среде. Там есть множество разработчиков Cocoa, и когда еще один из них начинает работать с вашим кодом, он будет гораздо более доступным, если он будет выглядеть и похож на другой код Cocoa.

Примеры того, что делать и что не делать:

  • Не объявляйте id m_something; в интерфейсе объекта и называйте его переменной-членом или полем; используйте something или _something для своего имени и назовите его переменной экземпляра.
  • Не называйте getter -getSomething; правильное имя Cocoa - это просто -something.
  • Не называйте setter -something:; это должно быть -setSomething:
  • Имя метода перемежается с аргументами и включает двоеточия; это -[NSObject performSelector:withObject:], а не NSObject::performSelector.
  • Используйте inter-caps (CamelCase) в именах методов, параметрах, переменных, именах классов и т.д., а не в нижних строках (подчеркивания).
  • Названия классов начинаются с букв в верхнем регистре, переменных и имен методов с нижним регистром.

Что бы вы ни делали, не использовать венгерскую нотацию Win16/Win32. Даже Microsoft отказалась от этого с переходом на платформу .NET.

  • 5
    Я бы сказал, что не используйте setSomething: / что-то вообще - вместо этого используйте свойства. На данный момент есть немного людей, которым действительно нужно нацелиться на Тигра (единственная причина не использовать свойства)
  • 18
    Свойства по-прежнему генерируют методы доступа для вас, а атрибуты getter = / setter = для свойства позволяют указывать имена методов. Кроме того, вы можете использовать синтаксис [foo что-то] вместо синтаксиса foo.something со свойствами. Таким образом, присвоение имен по-прежнему актуально.
Показать ещё 8 комментариев
105

Не используйте неизвестные строки в качестве строк формата

Когда методы или функции принимают аргумент строки формата, вы должны убедиться, что у вас есть контроль над содержимым строки формата.

Например, при записи строк, возникает соблазн передать строковую переменную в качестве единственного аргумента в NSLog:

    NSString *aString = // get a string from somewhere;
    NSLog(aString);

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

    NSLog(@"%@", aString);
  • 4
    Я был укушен этим раньше.
  • 0
    Это хороший совет для любого языка программирования
Показать ещё 1 комментарий
102

IBOutlets

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

@interface MyClass :NSObject {
    NSTextField *textField;
}
@property (nonatomic, retain) IBOutlet NSTextField *textField;
@end

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

  • 1
    не будет ли загрузка пера удерживать его дважды? (один раз в кончике, второй по присваиванию собственности). Должен ли я выпустить их в dealloc?
  • 6
    Вы должны обнулить розетки в viewDidUnload (iPhone OS 3.0+) или в пользовательском методе setView: чтобы избежать утечек. Очевидно, что вы должны также выпустить в dealloc.
Показать ещё 6 комментариев
98

Использовать статический анализатор LLVM/Clang

ПРИМЕЧАНИЕ. В Xcode 4 это теперь встроено в среду IDE.

Вы используете Clang Static Analyzer - неудивительно - проанализируйте свой код C и Objective-C (еще не С++) на Mac OS X 10.5. Это тривиально для установки и использования:

  • Загрузите последнюю версию эту страницу.
  • Из командной строки cd в каталог проекта.
  • Выполнить scan-build -k -V xcodebuild.

(Есть некоторые дополнительные ограничения и т.д., в частности, вам следует проанализировать проект в его конфигурации "Отладка" - см. http://clang.llvm.org/StaticAnalysisUsage.html для деталей - но это более или менее то, к чему оно сводится.)

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

  • 1
    У меня были некоторые проблемы с тем, чтобы заставить это работать, пока я не следовал этим инструкциям: oiledmachine.com/posts/2009/01/06/…
  • 15
    В XCode 3.2.1 на Snow Leopard он уже встроен. Вы можете запустить его вручную, используя Run -> Build and Analyze , или включить его для всех сборок с помощью параметра сборки «Run Static Analyzer». Обратите внимание, что этот инструмент в настоящее время поддерживает только C и Objective-C, но не C ++ / Objective-C ++.
95

Это тонкий, но удобный. Если вы передаете себя как делегат другому объекту, reset, этот делегат объекта перед вами dealloc.

- (void)dealloc
{
self.someObject.delegate = NULL;
self.someObject = NULL;
//
[super dealloc];
}

Выполняя это, вы гарантируете, что больше методов делегата не будет отправлено. Когда вы собираетесь dealloc и исчезаете в эфире, вы хотите удостовериться, что ничто не может отправить вам больше сообщений случайно. Помните, что self.someObject может быть сохранен другим объектом (это может быть синглтон или пул авторекламы или что-то еще), и пока вы не скажете "перестаньте отправлять мне сообщения!", Он думает, что ваш объект, который должен быть освобожден от несанкционированного доступа это честная игра.

Вхождение в эту привычку спасет вас от множества странных сбоев, которые являются причиной отладки.

Тот же принцип применяется к наблюдению за ключевым значением и NSNotifications.

Edit:

Еще более защитный, измените:

self.someObject.delegate = NULL;

в

if (self.someObject.delegate == self)
    self.someObject.delegate = NULL;
  • 8
    В этом нет ничего тонкого, в документации четко сказано, что вы обязаны это сделать. Из Memory Management Programming Guide for Cocoa : Additional cases of weak references in Cocoa include, but are not restricted to, table data sources, outline view items, notification observers, and miscellaneous targets and delegates. In most cases, the weak-referenced object is aware of the other object's weak reference to it, as is the case for circular references, and is responsible for notifying the other object when it deallocates.
  • 0
    Лучше использовать nil вместо NULL, потому что NULL не освободит память.
Показать ещё 3 комментария
87

@kendell

Вместо:

@interface MyClass (private)
- (void) someMethod
- (void) someOtherMethod
@end

Использование:

@interface MyClass ()
- (void) someMethod
- (void) someOtherMethod
@end

Новое в Objective-C 2.0.

Расширения классов описаны в справочнике Apple Objective-C 2.0.

"Расширения классов позволяют объявлять дополнительный API для класса в местах, отличных от блока основного класса @interface"

Таким образом, они являются частью категории класса - и НЕ (закрытой) в дополнение к классу. Тонкая, но важная разница.

  • 0
    Вы могли бы сделать это, но мне нравится явно обозначать его как «закрытый» раздел (больше документации, чем функциональный), хотя, конечно, это уже достаточно очевидно из того, что он находится в файле .m ...
  • 2
    За исключение существует разница между частными категориями и расширениями класса: «Расширение класса позволяет объявить необходимый дополнительный API для класса в других , чем в блоке основного класса @interface мест, как показано в следующем примере:» Смотрите ссылку на редактировании.
Показать ещё 6 комментариев
75

Избегайте автообновления

Поскольку вы обычно (1) не имеете прямого контроля над своей жизнью, автореализованные объекты могут сохраняться в течение сравнительно длительного времени и излишне увеличивать объем памяти вашего приложения. Хотя на рабочем столе это может иметь мало значения, на более ограниченных платформах это может быть серьезной проблемой. Поэтому на всех платформах, и особенно на более ограниченных платформах, считается лучшей практикой избегать использования методов, которые приводят к автореализованным объектам, и вместо этого вам предлагается использовать шаблон alloc/init.

Таким образом, вместо:

aVariable = [AClass convenienceMethod];

где возможно, вы должны использовать:

aVariable = [[AClass alloc] init];
// do things with aVariable
[aVariable release];

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

Таким образом, вместо:

- (MyClass *)convenienceMethod {
    MyClass *instance = [[[self alloc] init] autorelease];
    // configure instance
    return instance;
}

вы можете написать:

- (MyClass *)newInstance {
    MyClass *instance = [[self alloc] init];
    // configure instance
    return instance;
}

Поскольку имя метода начинается с "нового", пользователи вашего API знают, что они отвечают за освобождение полученного объекта (см., например, NSObjectController newObject).

(1) Вы можете взять управление, используя собственные локальные пулы автоопределений. Подробнее об этом см. Пулы автообновления.

  • 0
    Похоже, что ссылка на «Соглашение об именах Какао» была перемещена. Попробуйте developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/…
  • 1
    Эти ошибки, связанные с автоматическим выпуском, укусили меня много-много раз за мою короткую карьеру в разработке для iPhone. Сознательное избегание ситуаций значительно смягчило бы.
Показать ещё 10 комментариев
70

Некоторые из них уже упоминались, но здесь я могу думать о моей голове:

  • Следуйте правилам именования KVO. Даже если вы не используете KVO сейчас, по моему опыту часто это время еще полезно в будущем. И если вы используете KVO или привязки, вам нужно знать, что все работает так, как они должны. Это распространяется не только на методы доступа и переменные экземпляра, но и на многие отношения, проверку достоверности, автоматическое уведомление зависимых ключей и т.д.
  • Поместить частные методы в категорию. Не только интерфейс, но и реализация. Хорошо иметь некоторую дистанцию ​​концептуально между частными и не частными методами. Я включаю все в файл .m.
  • Поместите методы потока фона в категорию. То же, что и выше. Я нашел, что хорошо держать четкий концептуальный барьер, когда вы думаете о том, что на главной теме, а что нет.
  • Использовать #pragma mark [section]. Обычно я группирую свои собственные методы, каждый подкласс переопределяет и любые информационные или формальные протоколы. Это значительно облегчает переход к тому, что я ищу. В той же теме групповые аналогичные методы (например, методы делегата представления таблиц) вместе не помещаются в них.
  • Префикс private methods и ivars с _. Мне нравится, как он выглядит, и я с меньшей вероятностью использую ivar, когда я имею в виду свойство случайно.
  • Не используйте методы/свойства mutator в init и dealloc. Я никогда не имел ничего плохого из-за этого, но я могу видеть логику, если вы измените метод, чтобы сделать что-то, что зависит от состояния вашего объекта.
  • Поместите IBOutlets в свойства. Я на самом деле просто прочитал это здесь, но я собираюсь начать делать это. Независимо от каких-либо преимуществ памяти, это выглядит стилистически (по крайней мере, для меня).
  • Избегайте написания кода, который вам не нужен.. Это действительно охватывает множество вещей, например, создание ivars, когда будет выполняться #define, или кеширование массива вместо сортировки его каждый раз, когда данные необходимы. Там я могу сказать об этом, но в нижней строке не писать код, пока он вам не понадобится, или профайлер говорит вам. Это упрощает работу в долгосрочной перспективе.
  • Завершите то, что вы начинаете. Имея много половины готового кода, глючный код является самым быстрым способом убить проект мертвым. Если вам нужен метод заглушки, который будет прекрасным, просто укажите его, вставив NSLog( @"stub" ) внутрь, или же вы хотите отслеживать все.
  • 3
    Я бы посоветовал вам поместить частные методы в продолжение класса. (т.е. @interface MyClass () ... @end в вашем .m)
  • 3
    Вместо #PRAGMA вы можете использовать комментарий // Mark: [Section], который более переносим и работает идентично.
Показать ещё 6 комментариев
57

Золотое правило: если вы alloc, то вы release!

UPDATE: если вы не используете ARC

  • 26
    Также, если вы copy , mutableCopy , new или retain .
56

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

У вас также нет видимости public vs. protected vs. private method, которая мешает написанию тестов для ваших внутренних компонентов.

  • 0
    Какие тестовые рамки вы рекомендуете?
  • 13
    Xcode включает в себя OCUnit, инфраструктуру модульного тестирования Objective-C и поддержку для запуска пакетов модульных тестов в рамках процесса сборки.
55

Не записывайте Objective-C, как если бы это были Java/С#/С++/etc.

Я когда-то видел, что команда, используемая для написания веб-приложений Java EE, пытается написать настольное приложение Cocoa. Как будто это веб-приложение Java EE. Было много абстрактных FooFactory и FooFactory и IFoo и Foo, когда они действительно нуждались в классе Foo и, возможно, в протоколе Fooable.

Часть обеспечения того, что вы этого не делаете, действительно понимает различия в языке. Например, вам не нужны абстрактные классы factory и factory, потому что методы класса Objective-C отправляются так же динамически, как методы экземпляра, и могут быть переопределены в подклассах.

  • 10
    Как Java-разработчик, который написал абстрактную фабрику в Objective-C, я нахожу это интригующим. Не могли бы вы объяснить немного больше, как это работает - возможно, на примере?
  • 2
    Вы все еще верите, что нам не нужны абстрактные фабричные классы после того, как вы оставили этот ответ?
50

Убедитесь, что вы закладок Отладка магии". Это должно быть вашей первой остановкой, когда вы стучите головой о стену, пытаясь найти источник ошибки Cocoa.

Например, он расскажет вам, как найти метод, в котором вы сначала выделили память, которая впоследствии вызывает сбои (например, во время завершения приложения).

  • 1
    Теперь есть специальная версия страницы отладки Magic для iOS.
39

Попытайтесь избежать того, что я теперь решил назвать Newbiecategaholism. Когда новички в Objective-C обнаруживают категории, они часто бывают свирепыми, добавляя полезные маленькие категории к каждому существующему классу ( "Что? Я могу добавить метод для преобразования числа в римские цифры в NSNumber rock!" ).

Не делайте этого.

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

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

Существуют и другие опасности, если вы не используете имена ваших методов категорий (и кто, кроме безумного ддрибина?), есть вероятность, что Apple, или плагин, или что-то еще, работающее в вашем адресном пространстве, также определит метод той же категории с тем же именем со слегка отличающимся побочным эффектом....

OK. Теперь, когда вас предупредили, игнорируйте "не делайте эту часть". Но проявляйте чрезвычайную сдержанность.

  • 0
    Мне нравится ваш ответ, мой совет не будет использовать категорию для хранения служебного кода, если вы не собираетесь копировать некоторый код в более чем одном месте, и этот код явно принадлежит классу, который вы собираетесь классифицировать ...
  • 0
    Я просто хотел бы передать и озвучить свою поддержку методов категории пространства имен. Это просто кажется правильным.
Показать ещё 3 комментария
38

Сортировка строк по желанию пользователя

Когда вы сортируете строки для представления пользователю, вы не должны использовать простой метод compare:. Вместо этого вы всегда должны использовать локализованные методы сравнения, такие как localizedCompare: или localizedCaseInsensitiveCompare:.

Подробнее см. Поиск, сравнение и сортировка строк.

36

Сопротивление подклассификации мира. В Cocoa многое происходит через делегирование и использование базовой среды выполнения, которая в других рамках выполняется посредством подкласса.

Например, в Java вы часто используете экземпляры анонимных подклассов *Listener, а в .NET вы часто используете подклассы EventArgs. В Cocoa вы тоже не выполняете: вместо этого используется целевое действие.

  • 6
    В противном случае известен как «Композиция над наследством».
32

Объявленные свойства

Обычно вы должны использовать функцию объявленных свойств Objective-C 2.0 для всех ваших свойств. Если они не являются общедоступными, добавьте их в расширение класса. Использование объявленных свойств упрощает семантику управления памятью и упрощает проверку вашего метода dealloc - если вы объединяете объявления свойств, вы можете быстро их сканировать и сравнить с реализацией вашего метода dealloc.

Вы должны много думать, прежде чем не указывать свойства как "неатомные". Поскольку Примечания по языку программирования Objective C, свойства по умолчанию являются атомарными и несут значительные накладные расходы. Более того, просто сделать все ваши свойства атомарными не делает ваше приложение потокобезопасным. Также обратите внимание, конечно, что если вы не укажете "неатомические" и не реализуете свои собственные методы доступа (вместо их синтезирования), вы должны реализовать их по-атомному.

25

Используйте NSAssert и друзей. Я использую nil как действительный объект все время... особенно отправка сообщений в nil отлично действует в Obj-C. Однако, если я действительно хочу убедиться в состоянии переменной, я использую NSAssert и NSParameterAssert, что помогает легко отслеживать проблемы.

25

Подумайте о значениях nil

Как этот вопрос, сообщения nil действительны в Objective-C. Хотя это часто является преимуществом, что приводит к более чистому и более естественному коду, функция может иногда приводить к сложным и сложным ошибкам, если вы получаете значение nil, когда вы этого не ожидали.

  • 0
    У меня есть это: #define SXRelease(o); o = nil и то же самое для CFRelease и free . Это все упрощает.
22

Простая, но забытая. Согласно спецификации:

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

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

@interface FooInt:NSObject{}
-(int) print;
@end

@implementation FooInt
-(int) print{
    return 5;
}
@end

@interface FooFloat:NSObject{}
-(float) print;
@end

@implementation FooFloat
-(float) print{
    return 3.3;
}
@end

int main (int argc, const char * argv[]) {

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];    
    id f1=[[FooFloat alloc]init];
    //prints 0, runtime considers [f1 print] to return int, as f1 type is "id" and FooInt precedes FooBar
    NSLog(@"%f",[f1 print]);

    FooFloat* f2=[[FooFloat alloc]init];
    //prints 3.3 expectedly as the static type is FooFloat
    NSLog(@"%f",[f2 print]);

    [f1 release];
    [f2 release]
    [pool drain];

    return 0;
}   
  • 0
    это легко забыть. Тем не менее важно
  • 3
    Это только проблема при воздержании от статической типизации. Если компилятор знает тип, аргумент и возвращаемый тип могут отличаться без проблем. Лично я считаю, что это не часто является проблемой. У Apple также есть много методов, которые имеют одинаковые имена, но различаются по типам возвращаемых данных. Наконец, есть флаг компилятора, чтобы предупредить вас в неоднозначных случаях.
Показать ещё 1 комментарий
22

Если вы используете Leopard (Mac OS X 10.5) или более позднюю версию, вы можете использовать приложение "Инструменты" для поиска и отслеживания утечек памяти. После создания вашей программы в Xcode выберите "Выполнить" > "Начать работу с инструментом производительности" > "Утечки".

Даже если ваше приложение не показывает никаких утечек, вы можете слишком долго хранить объекты. В Инструментах для этого вы можете использовать инструмент ObjectAlloc. Выберите инструмент ObjectAlloc в документе "Инструменты" и вызовите деталь инструмента (если он еще не отображается), выбрав "Просмотр" > "Подробно" (рядом с ним должна быть отметка). В разделе "Распределение продолжительности жизни" в деталях ObjectAlloc убедитесь, что вы выбрали переключатель рядом с "Созданный и неподвижный".

Теперь, когда вы прекращаете запись своего приложения, выбирая инструмент ObjectAlloc, вы покажете, сколько ссылок есть на каждый неподвижный объект в вашем приложении в столбце "# Net". Убедитесь, что вы не только просматриваете свои собственные классы, но и классы ваших объектов верхнего уровня NIB файлов. Например, если у вас нет окон на экране, и вы видите ссылки на все еще живое NSWindow, возможно, вы не выпустили его в свой код.

21

Очистка в dealloc.

Это одна из самых простых вещей, которые нужно забыть - особенно. при кодировании со скоростью 150 миль в час. Всегда, всегда, всегда очищайте свои атрибуты/переменные-члены в dealloc.

Мне нравится использовать атрибуты Objc 2 - с новой точечной нотацией, поэтому это делает очистку безболезненной. Часто так же просто, как:

- (void)dealloc
{
    self.someAttribute = NULL;
    [super dealloc];
}

Это позаботится о выпуске для вас и установит атрибут в NULL (который я считаю защитным программированием), если другой метод еще дальше в dealloc снова получит доступ к переменной-члену - редко, но может произойти).

Когда GC включен в 10.5, это больше не нужно, но вам все равно нужно очистить другие ресурсы, которые вы создаете, но вы можете сделать это в методе finalize.

  • 12
    В общем, вы не должны использовать методы доступа в dealloc (или init).
  • 0
    При использовании сборки мусора настоятельно не рекомендуется внедрять метод финализации - вы должны найти другие возможности для очистки ресурсов.
Показать ещё 5 комментариев
17

Все эти комментарии замечательные, но я действительно удивлен, что никто не упоминал Google Objective-C Руководство по стилю, которое было опубликовано некоторое время назад. Я думаю, что они проделали очень тщательную работу.

  • 7
    Хм, первый пример уже полон дерьма. Никогда не документируйте языковые идиомы. Если бы я нашел такие комментарии в заголовочном файле, я бы не стал читать дальше.
  • 5
    О, мои глаза !!!!! Я не могу поверить в то, что я видел.
15

Кроме того, тема, связанная с полусвязью (с возможностью больше ответов!):

Что представляют собой те маленькие подсказки и трюки Xcode, которые вы хотели бы узнать около 2 лет назад?

13

Не забывайте, что NSWindowController и NSViewController освободят объекты верхнего уровня файлов NIB, которые они определяют.

Если вы вручную загружаете файл NIB, вы несете ответственность за освобождение объектов верхнего уровня NIB, когда вы закончите с ними.

12

Один довольно очевидный для новичков: используйте функцию автодементации Xcode для вашего кода. Даже если вы копируете/вставляете из другого источника, после того, как вы вставили код, вы можете выбрать весь блок кода, щелкнуть правой кнопкой мыши по нему, а затем выбрать вариант для повторного отступов всего внутри этого блока.

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

  • 0
    Вы можете даже установить Tab для отступа, а затем сделать Cmd-A и Tab.
10

Переменные и свойства

1/Сохранение заголовков в чистоте, скрытие реализации
Не включайте переменные экземпляра в свой заголовок. Частные переменные помещаются в класс как свойства. Публичные переменные объявляют как публичные свойства в вашем заголовке. Если он должен быть только прочитан, объявите его как readonly и перезапишите его как readwrite в непрерывном классе. В основном я вообще не использую переменные, только свойства.

2/Дайте вашим свойствам имя переменной, отличное от значения по умолчанию, например:


@synthesize property = property_;

Причина 1: вы поймаете ошибки, вызванные забыванием "я". при назначении свойства. Причина 2: Из моих экспериментов Leak Analyzer в Инструментах имеет проблемы с обнаружением свойства утечки с именем по умолчанию.

3/Никогда не используйте удержание или выпуск непосредственно по свойствам (или только в исключительных ситуациях). В вашем dealloc просто назначьте им нуль. Сохраняемые свойства предназначены для самостоятельной обработки удержания/выпуска. Вы никогда не знаете, нет ли сеттера, например, добавление или удаление наблюдателей. Вы должны использовать переменную непосредственно только внутри своего сеттера и получателя.

Просмотры

1/Поместите каждое представление в xib, если вы можете (исключение - это обычно динамический контент и настройки слоя). Это экономит время (это проще, чем написание кода), его легко изменить, и он сохраняет ваш код в чистоте.

2/Не пытайтесь оптимизировать представления, уменьшая количество просмотров. Не создавайте UIImageView в коде вместо xib только потому, что вы хотите добавить в него subviews. Вместо этого используйте UIImageView. Структура представления может обрабатывать сотни представлений без проблем.

3/IBOutlets не обязательно всегда сохраняться (или сильным). Обратите внимание, что большинство ваших IBOutlets являются частью вашей иерархии представлений и поэтому неявно сохраняются.

4/Отпустите все IBOutlets в viewDidUnload

5/Вызов viewDidUnload из вашего метода dealloc. Это неявно называется.

Память

1/Объекты Autorelease при их создании. Многие ошибки вызваны перемещением вашего запроса на выпуск в одну ветвь if-else или после оператора return. Выпуск вместо автореферата должен использоваться только в исключительных ситуациях - например, когда вы ожидаете runloop, и вы не хотите, чтобы ваш объект был автореализован слишком рано.

2/Даже если вы используете Authomatic Reference Counting, вы должны прекрасно понимать, как работают методы удержания. Использование сохранения-освобождения вручную не сложнее, чем ARC, в обоих случаях вы должны разбираться в утечках и циклах сохранения. Рассмотрите возможность использования сохранения-выпуска вручную в больших проектах или сложных иерархиях объектов.

Комментарии

1/Сделайте свой код автодокументированным. Каждое имя переменной и имя метода должны указывать, что он делает. Если код написан правильно (вам нужно много практики в этом), вам не понадобятся комментарии коментариев (не то же самое, что и комментарии к документации). Алгоритмы могут быть сложными, но код всегда должен быть простым.

2/Иногда вам нужен комментарий. Обычно описывать поведение, не являющееся очевидным кодом, или взломать. Если вы чувствуете, что вам нужно написать комментарий, сначала попробуйте переписать код, чтобы он был проще и без комментариев.

Отступ

1/Не увеличивайте отступ слишком сильно. Большая часть вашего кода метода должна быть отступом на уровне метода. Вложенные блоки (если, для и т.д.) Уменьшают читаемость. Если у вас есть три вложенных блока, вы должны попытаться поместить внутренние блоки в отдельный метод. Четыре или более вложенных блоков никогда не должны использоваться. Если большая часть вашего кода метода находится внутри if, отрицайте условие if, например:


if (self) {
   //... long initialization code ...
}

return self;


if (!self) {
   return nil;
}

//... long initialization code ...

return self;

Понять код C, в основном C structs

Обратите внимание, что Obj-C является только легким слоем OOP над языком C. Вы должны понимать, как работают базовые структуры кода в работе C (перечисления, структуры, массивы, указатели и т.д.). Пример:


view.frame = CGRectMake(view.frame.origin.x, view.frame.origin.y, view.frame.size.width, view.frame.size.height + 20);

совпадает с:


CGRect frame = view.frame;
frame.size.height += 20;
view.frame = frame;

И многое другое

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

Наши стандарты кодирования имеют в настоящее время около 20 страниц, сочетание стандартов кодирования Java, стандартов Google Obj-C/С++ и наших собственных дополнений. Документируйте свой код, используйте стандартные стандартные отступы, пробелы и пустые строки в нужных местах и ​​т.д.

10

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

Также часто выполняйте статический анализ Clang; вы можете включить его для всех сборок через настройку сборки "Запустить статический анализатор".

Напишите модульные тесты и запустите их с каждой сборкой.

  • 0
    И, если можете, включите «Обрабатывать предупреждения как ошибки». Не позволяйте предупреждению существовать.
  • 2
    Удобный скрипт для настройки вашего проекта с рекомендуемыми предупреждениями доступен здесь: rentzsch.tumblr.com/post/237349423/hoseyifyxcodewarnings-scpt
10

Я знаю, что я пропустил это при первом входе в программирование Cocoa.

Убедитесь, что вы понимаете ответственность за управление памятью в отношении файлов NIB. Вы несете ответственность за освобождение объектов верхнего уровня в любом загружаемом файле NIB. Прочитайте Документация Apple по этому вопросу.

  • 6
    Это неправда. Ответственность за освобождение объектов верхнего уровня зависит от того, от какого класса вы наследуетесь и какую платформу используете. См. Developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/… среди других.
9

Быть более функциональным.

Objective-C - это объектно-ориентированный язык, но Cocoa функциональный стиль фреймворка и во многих случаях разработан функциональный стиль.

  • Существует разделение изменчивости. Используйте неизменяемые классы как первичные, а изменчивый объект - как вторичный. Например, сначала используйте NSArray и используйте NSMutableArray только тогда, когда вам нужно.

  • Существуют чистые функции. Не так много, купите многие из API-интерфейсов инфраструктуры, разработанных как чистая функция. Посмотрите на функции, такие как CGRectMake() или CGAffineTransformMake(). Очевидно, что форма указателя выглядит более эффективной. Однако косвенный аргумент с указателями не может обеспечить побочный эффект. Конструктивные конструкции максимально в максимально возможной степени. Разделяйте даже объекты состояния. Используйте -copy вместо -retain при передаче значения другому объекту. Поскольку совместное состояние может влиять на мутацию, чтобы значение в другом объекте было тихо. Таким образом, не может быть побочным эффектом. Если у вас есть значение от внешнего объекта, скопируйте его. Таким образом, это также важно для разработки общего состояния как можно более минимального.

Однако не бойтесь использовать нечистые функции.

  • Существует ленивая оценка. Посмотрите что-то вроде свойства -[UIViewController view]. Представление не будет создано при создании объекта. Он будет создан, когда вызывающий абонент читает свойство view в первый раз. UIImage не будет загружаться до фактического рисования. Существует много реализаций, подобных этой конструкции. Подобные проекты очень полезны для управления ресурсами, но если вы не знаете понятия ленивой оценки, нелегко понять их поведение.

  • Существует закрытие. Используйте C-блоки как можно больше. Это значительно упростит вашу жизнь. Но прочитайте еще раз об управлении блочной памятью, прежде чем использовать его.

  • Существует полуавтоматический GC. NSAutoreleasePool. Используйте -autorelease primary. Используйте ручную -retain/-release вторичную, когда вам действительно нужно. (например: оптимизация памяти, явное удаление ресурсов)

  • 2
    Что касается 3) Я предложу противоположный подход: используйте ручное удержание / разблокирование везде, где это возможно! Кто знает, как будет использоваться этот код - и если он будет использоваться в узком цикле, это может излишне взорвать использование вашей памяти.
  • 0
    @Eiko Это просто преждевременная оптимизация , не может быть общим руководством.
Показать ещё 2 комментария
8

Представленные Apple образцы, которые я видел, рассматривали делегат Приложения как глобальное хранилище данных, сортировщик данных. Это неправильно. Создайте синглтон и, возможно, создайте экземпляр в делегате приложения, но избегайте использования делегата приложения как нечто большее, чем обработка событий на уровне приложений. Я от всей души повторю рекомендации в этой записи в блоге. Этот поток отбросил меня.

  • 5
    Вы когда-нибудь слышали, чтобы кто-нибудь говорил вам, что синглтон - это анти-паттерн? Для этого есть причина ... Я разочарован тем, что программисты по-прежнему считают синглтоны ответом на все беды.
  • 0
    -1. Синглтон определенно является анти-паттерном, когда вы решаете, что вашему приложению требуется более одной базы данных (синхронизация, несколько пользователей, ...).
4

Выделите свойство только в методе dealloc. Если вы хотите освободить память, которую удерживает свойство, просто установите ее как nil:

self.<property> = nil;
  • 0
    Обратите внимание, что есть большой спор об этом. Использование setter в dealloc может привести к ошибке, если вы не используете свойства в правильном порядке. Однако то же самое справедливо при использовании release напрямую - могут появиться другие ошибки. Apple рекомендует использовать release в dealloc но в своей реализации они используют setter (например, освобождают delegate в [UITableView dealloc] )
0
#import "MyClass.h"

@interface MyClass ()
- (void) someMethod;
- (void) someOtherMethod;
@end

@implementation MyClass

Ещё вопросы

Сообщество Overcoder
Наверх
Меню