Я знаю о ВЫСОКИЙ (что очень удобно!), Но какие методы программирования вы используете при написании Objective-C, а точнее при использовании Cocoa (или CocoaTouch).





Золотое правило: если у вас alloc, то вы release!
ОБНОВЛЕНИЕ: если вы не используете ARC
Также, если у вас copy, mutableCopy, new или retain.
Я начал делать несколько вещей, которые не считаю стандартными:
1) С появлением свойств я больше не использую "_" для префикса "частных" переменных класса. В конце концов, если к переменной могут обращаться другие классы, разве не должно быть для нее свойства? Мне всегда не нравился префикс «_», делающий код более уродливым, и теперь я могу его опустить.
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 val = someFloat * 2.2f;
Это особенно важно, когда someFloat может фактически быть двойным, вам не нужна математика в смешанном режиме, поскольку вы теряете точность в 'val' при хранении. Хотя числа с плавающей запятой аппаратно поддерживаются на iPhone, для выполнения арифметических операций с двойной точностью может потребоваться больше времени, чем для арифметических операций с одинарной точностью. Использованная литература:
На старых телефонах предположительно вычисления выполняются с той же скоростью, но в регистрах может быть больше компонентов одинарной точности, чем удвоений, поэтому для многих вычислений одинарная точность будет быстрее.
5) Установите свои свойства как nonatomic. По умолчанию это atomic, и после синтеза будет создан семафорный код для предотвращения проблем с многопоточностью. 99% из вас, вероятно, не должны беспокоиться об этом, и код намного менее раздут и более эффективен с точки зрения памяти, когда установлен неатомарный.
6) SQLite может быть очень и очень быстрым способом кэширования больших наборов данных. Например, картографическое приложение может кэшировать свои листы в файлы SQLite. Самая дорогая часть - это дисковый ввод-вывод. Избегайте множества мелких записей, отправляя BEGIN; и COMMIT; между большими блоками. Например, мы используем 2-секундный таймер, который сбрасывается при каждой новой отправке. Когда он истекает, мы отправляем COMMIT; , что приводит к тому, что все ваши записи идут одним большим фрагментом. SQLite хранит данные транзакции на диске, и выполнение этой упаковки «Начало / конец» позволяет избежать создания множества файлов транзакций, группируя все транзакции в один файл.
Кроме того, SQL заблокирует ваш графический интерфейс, если он находится в вашем основном потоке. Если у вас очень длинный запрос, рекомендуется хранить запросы как статические объекты и запускать SQL в отдельном потоке. Обязательно оберните все, что изменяет базу данных для строк запроса, в блоки @synchronize() {}. Для коротких запросов просто оставьте все в основном потоке для большего удобства.
Здесь можно найти больше советов по оптимизации SQLite, хотя документ кажется устаревшим, многие пункты, вероятно, все еще хороши;
http://web.utk.edu/~jplyon/sqlite/SQLite_optimization_FAQ.html
Хороший намек на двойную арифметику.
Расширения классов теперь являются предпочтительным способом для частных методов: developer.apple.com/Mac/library/documentation/Cocoa/Conceptu al /…
Ваш совет насчет дублеров на айфоне устарел stackoverflow.com/questions/1622729/…
Вау, я только начинаю заниматься разработкой для iPhone, и это невероятно!
Casebash - спасибо за ссылку, хотя информация о сетях 3G устарела, похоже, у вас есть еще больше причин для использования FP с одинарной точностью, чем когда-либо прежде! Я обновил этот раздел ответа.
Не устарело; Совершенно неверно: исходный iPhone поддерживал аппаратное управление плавающей и двойной скоростью примерно с той же скоростью. SQLite также не хранит транзакции в памяти; они записываются на диск. Только длинные запросы блокируют ваш интерфейс; менее беспорядочно запускать все в основном потоке и использовать более быстрые запросы.
@tc: я исправил элемент SQL о транзакциях, обратите внимание, что я сам не писал эти последние четыре или около того. Я также пояснил, что часть, касающаяся перемещения запросов в фон, предназначена только для очень длинных запросов (иногда вы просто не можете сделать их короче). Но назвать все это «неправильным» из-за нескольких моментов - это довольно крайняя крайность. Кроме того, в приведенном выше ответе уже говорилось: «Предположительно, на старых телефонах вычисления выполняются с той же скоростью», но обратите внимание на то, что большее количество регистров одинарной точности делает их по-прежнему предпочтительными.
Немного о неатомных свойствах - это преждевременная оптимизация. Лучше оставить их по умолчанию и изменить их на неатомарные, если профилирование показывает, что можно получить значительное улучшение производительности.
Такая оптимизация вовсе не преждевременна. Нет никакой пользы в том, чтобы оставлять их атомарными, и фактически представляет потенциальную опасность тупиковой ситуации, если вы не знаете, что делаете, и фактически используете их многопоточными. Это распространенный метод, рекомендуемый многими опытными разработчиками ObjC и используемый по умолчанию в таких приложениях, как Accessorizer, которые генерируют настройки свойств.
+1 за пункт №5. я бы хотел, чтобы неатомные были по умолчанию. atomic довольно бесполезен (imo) - разработчик (или клиент) должен знать, когда многопоточность является проблемой или нет, и они должны решать эти проблемы соответствующим образом. «Обычно это работает, хотя занимает очень много времени» - это не «особенность».
По поводу второго пункта .... Объявление частного метода :: Нет необходимости писать какую-либо категорию. Я все еще могу писать методы без объявления и без предупреждения. Вот способ: - (void) myPrivateMethod {} - (void) myClassMethod {[self myPrivateMethod]; } Проверь это...
Боюсь, что информация о дублях здесь совершенно неверна. Даже связанный вопрос, упомянутый в качестве источника для (4), полностью противоречит утверждению. Еще раз: iDevices всех видов аппаратно поддерживает математику с двойной точностью.
@Tim Тот факт, что математика с двойной точностью поддерживается аппаратно, не делает ее быстрее, чем математика с одинарной точностью. Можете ли вы предоставить ссылку или пример, чтобы показать, что на самом деле нет никакой разницы или что это будет быстрее? Обратите внимание, что в моем тексте явно указано, что математика с двойной точностью поддерживается аппаратно ... и предоставленные ссылки не противоречат друг другу, они показывают, как одинарная точность может быть быстрее.
@mrunal - этот метод работает только тогда, когда ваш метод вызывается методами, которые следуют за ним. Часто по организационным причинам вы можете захотеть поместить вспомогательный метод после некоторых других методов, которые его вызывают - тогда появляется частная категория. Я сам не всегда помещаю определения методов в частную категорию, но важно знать, что это вариант, а не неестественное структурирование ваших методов.
@KendallHelmstetterGelner, ваша редакция полностью соответствует моему возражению; благодаря. Моя проблема заключалась не в том, была ли двойная или одинарная точность быстрее, а в том, что вы утверждали, что математика двойной точности эмулировалась в программном обеспечении и не поддерживалась аппаратным обеспечением. Я должен был быть яснее.
Понятно, спасибо за разъяснения. Я был смущен.
@KendallHelmstetterGelner - Могу я узнать, какие организационные причины?
Одним из примеров является группировка всех методов контроллера представления вместе с вспомогательными методами, расположенными после в конце файла, чтобы их было легче найти.
@KendallHelmstetterGelner Текущие версии clang больше не зависят от объявлений методов для текущей единицы компиляции. Таким образом, теперь они могут быть исключены из расширения класса без вреда.
@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 Reference.
«Расширения класса позволяют объявлять дополнительный требуемый API для класса в местах, отличных от основного блока @interface class»
Таким образом, они являются частью фактического класса, а НЕ (частной) категорией в дополнение к классу. Тонкая, но важная разница.
Вы могли бы это сделать, но мне нравится явно обозначать его как «частный» раздел (больше документации, чем функционального), хотя, конечно, это уже достаточно очевидно, поскольку он находится в файле .m ...
За исключением является, разница между частными категориями и расширениями классов: «Расширения классов позволяют вам объявлять дополнительный требуемый API для класса в местах, отличных от основного блока @interface класса, как показано в следующем примере:« См. Ссылку в редактировании.
Я согласен, что есть разница, когда компилятор предупредит вас, если вы не реализовали методы CE, но я не считаю этот аспект очень важным, когда все методы находятся в одном файле и все частные. Я все еще предпочитаю аспект ремонтопригодности, отмечая блок прямых ссылок как частный
Я могу утверждать, что удобнее поддерживать компилятор в соответствии с вашим кодом, но мне кажется, что это происходит естественным образом благодаря предупреждениям компилятора, когда методы меняются, а объявления методов - нет. Я вижу, что другие предпочитают CE, он явно предназначен для этого.
Я действительно не считаю (Private) более удобным в обслуживании, чем (). Если вы так обеспокоены, то может помочь хорошая доза комментариев. Но, очевидно, живи и дай жить другим. YMMV и др.
Использование () вместо (Private) (или другого названия категории) дает довольно важное преимущество: вы можете повторно объявить свойства как readwrite, в то время как для публики они доступны только для чтения. :)
Если вы находитесь в основном файле .m вашего объекта, то с помощью () вы можете разместить определения методов в любом месте раздела интерфейса myObject. Если вы объявите как (Private), вы получите предупреждение, если не поместите определение в интерфейс myObject (Private).
Если вы действительно хотите написать Private, вы можете сделать (/*Private*/), и, поскольку это комментарий, на самом деле это (), и вы все равно можете объявлять свойства
Очистите в dealloc.
Об этом легче всего забыть - особенно. при кодировании на скорости 150 миль в час. Всегда, всегда, всегда очищайте свои атрибуты / переменные-члены в dealloc.
Мне нравится использовать атрибуты Objc 2 - с - новую точечную нотацию, так что это делает очистку безболезненной. Часто бывает так просто, как:
- (void)dealloc
{
self.someAttribute = NULL;
[super dealloc];
}
Это позаботится о выпуске за вас и установит для атрибута значение NULL (что я считаю защитным программированием - в случае, если другой метод, расположенный ниже в dealloc, снова обращается к переменной-члену - редко, но происходит мог).
Когда сборщик мусора включен в 10.5, в этом больше нет необходимости, но вам все равно может потребоваться очистка других ресурсов, которые вы создаете, вы можете сделать это в методе finalize.
В общем, вам следует нет использовать методы доступа в dealloc (или init).
При использовании сборки мусора настоятельно не рекомендуется реализовывать метод finalize - вы должны найти другие возможности для очистки ресурсов.
Помимо причин производительности (аксессоры немного медленнее, чем прямой доступ), почему бы мне не использовать аксессоры в dealloc или init?
(a) Причины производительности вполне адекватны сами по себе (особенно если ваши аксессоры атомарны). (b) Вам следует избегать любых побочных эффектов, которые могут иметь средства доступа. Последнее особенно важно, если ваш класс может быть подклассом.
Замечу, что если вы работаете в современной среде выполнения с синтезированными ivars, вы должен используете аксессоры в dealloc. Большая часть современного кода среды выполнения - это сборщик мусора, но не весь.
Более подробное представление о том, следует ли использовать методы / свойства средств доступа в методах -init и -dealloc, можно найти здесь: mikeash.com/?page=pyblog/…
Разве это не должно быть nil, а не NULL?
Это тонкий, но удобный. Если вы передаете себя в качестве делегата другому объекту, сбросьте делегата этого объекта перед dealloc.
- (void)dealloc
{
self.someObject.delegate = NULL;
self.someObject = NULL;
//
[super dealloc];
}
Делая это, вы гарантируете, что больше не будут отправлены методы делегата. Когда вы приближаетесь к dealloc и исчезаете в эфире, вы хотите убедиться, что ничто не сможет отправить вам больше сообщений случайно. Помните, что self.someObject может быть сохранен другим объектом (это может быть одноэлементный объект или в пуле автозапуска, или что-то еще), и пока вы не скажете ему «перестань отправлять мне сообщения!», Он будет думать, что ваш объект, который вот-вот должен быть освобожден это честная игра.
Привыкание к этой привычке избавит вас от множества странных сбоев, отлаживать которые сложно.
Тот же принцип применяется и к наблюдению за ключевыми значениями, и к NSNotifications.
Редактировать:
Еще более оборонительное изменение:
self.someObject.delegate = NULL;
в:
if (self.someObject.delegate == self)
self.someObject.delegate = NULL;
В этом нет ничего тонкого, в документации четко сказано, что вы обязаны это сделать. С 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.
Лучше использовать nil вместо NULL, потому что NULL не освободит память.
@NaveenShan nil == NULL. Они точно такие же, за исключением того, что nil - это id, а NULL - это void *. Ваше утверждение не соответствует действительности.
@WTP да, nil == NULL, но использование nil явно является предпочтительным способом, если вы просматриваете фрагменты кода примеров яблок, они везде используют nil, и, как вы сказали, nil - это идентификатор, что делает его предпочтительнее, чем void * , в тех случаях, когда вы отправляете идентификаторы, то есть.
@Ahti точно, а Nil (в верхнем регистре) относится к типу Class*. Несмотря на то, что все они равны, использование неправильного может привести к небольшим неприятным ошибкам, особенно в Objective-C++.
Старайтесь избегать того, что я сейчас назвал категорией для новичков. Когда новички в Objective-C открывают для себя категории, они часто сходят с ума, добавляя полезные маленькие категории к каждому существующему классу («Что? Я могу добавить метод преобразования числа в римские цифры в NSNumber rock on!»).
Не делай этого.
Ваш код будет более переносимым и понятным без десятков небольших методов категорий, добавленных поверх двух десятков базовых классов.
В большинстве случаев, когда вы действительно думаете, что вам нужен метод категории, чтобы упростить некоторый код, вы обнаружите, что никогда не будете использовать этот метод повторно.
Есть и другие опасности, если вы не назначаете пространство имен для своих методов категорий (и кто, кроме совершенно безумного ddribin?), Есть вероятность, что Apple, или плагин, или что-то еще, работающее в вашем адресном пространстве, также определит ту же категорию метод с тем же именем с немного другим побочным эффектом ....
В ПОРЯДКЕ. Теперь, когда вас предупредили, игнорируйте «не делайте этого». Но проявляйте крайнюю сдержанность.
Мне нравится ваш ответ, я бы посоветовал не использовать категорию для хранения служебного кода, если вы не собираетесь реплицировать какой-то код более чем в одном месте, и код явно принадлежит классу, который вы собираетесь категоризировать ...
Я просто хотел бы выразить свою поддержку методов категорий с пространством имен. Это кажется правильным поступком.
+1 хотя бы для римских цифр. Я бы полностью это сделал!
Контрпункт: последние полтора года я придерживался прямо противоположной политики: «Если это можно реализовать в категории, сделайте это». В результате мой код стал намного более кратким, выразительным и легким для чтения, чем подробный пример кода, который предоставляет Apple. Я потерял в общей сложности около 10 минут на один конфликт пространств имен и, вероятно, выиграл человеко-месяцы благодаря эффективности, которую я создал для себя. Каждому - свое, но я принял эту политику, зная о рисках, и очень рад, что сделал это.
Я не согласен. Если это будет функция и она применима к объекту Foundation, и вы можете придумать хорошее имя, поместите его в категорию. Ваш код станет более читабельным. Я думаю, что главное здесь: делайте все в меру.
Кроме того, наполовину связанная тема (с возможностью для большего количества ответов!):
Что это за маленькие советы и хитрости Xcode, о которых вы хотели бы знать 2 года назад?.
Напишите модульные тесты. Вы можете протестировать много вещей в Какао, что может быть сложнее в других фреймворках. Например, с помощью кода пользовательского интерфейса вы обычно можете проверить, что все подключено так, как должно быть, и поверить, что они будут работать при использовании. И вы можете легко настроить состояние и вызвать методы делегата для их тестирования.
У вас также нет видимости публичных, защищенных и частных методов, которые мешают написанию тестов для ваших внутренних компонентов.
Какие тестовые среды вы рекомендуете?
Xcode включает OCUnit, платформу модульного тестирования Objective-C и поддержку запуска пакетов модульных тестов как части процесса сборки.
Сопротивляйтесь разделению мира на подклассы. В Какао многое делается посредством делегирования и использования базовой среды выполнения, что в других фреймворках выполняется через создание подклассов.
Например, в Java вы часто используете экземпляры анонимных подклассов *Listener, а в .NET вы часто используете свои подклассы EventArgs. В Какао вы этого не делаете - вместо этого используется target-action.
Иначе известный как «Композиция по наследованию».
Не пишите Objective-C, как если бы это были Java / C# / C++ / и т. д.
Однажды я видел, как команда разработчиков веб-приложений Java EE пыталась написать настольное приложение Cocoa. Как будто это веб-приложение Java EE. Было много AbstractFooFactory, FooFactory, IFoo и Foo, когда все, что им действительно нужно, это класс Foo и, возможно, протокол Fooable.
Отчасти гарантировать, что вы этого не сделаете, - это истинное понимание различий в языке. Например, вам не нужны абстрактные фабричные и фабричные классы, указанные выше, потому что методы класса Objective-C отправляются так же динамически, как и методы экземпляра, и могут быть переопределены в подклассах.
Как Java-разработчик, который написал абстрактную фабрику на Objective-C, я нахожу это интригующим. Не могли бы вы объяснить еще немного, как это работает - возможно, на примере?
Вы все еще верите, что нам не нужны абстрактные фабричные классы после того, как вы опубликовали этот ответ?
Используйте стандартные соглашения об именах и форматировании какао, а также терминологию, а не то, к чему вы привыкли из другой среды. Есть являются многих разработчиков Cocoa, и когда другой из них начнет работать с вашим кодом, он будет намного более доступным, если он будет выглядеть и ощущаться похожим на другой код Cocoa.
Примеры того, что делать и чего не делать:
id m_something; в интерфейсе объекта и называйте его переменная-член или поле; используйте something или _something в качестве имени и назовите его переменная экземпляра.-getSomething; правильное имя Какао - просто -something.-something:; это должно быть -setSomething:-[NSObject performSelector:withObject:], а не NSObject::performSelector.Что бы вы ни делали, не использует венгерскую нотацию в стиле Win16 / Win32. Даже Microsoft отказалась от этого с переходом на платформу .NET.
Я бы сказал, вообще не используйте setSomething: / something - вместо этого используйте свойства. На данный момент мало людей, которым действительно нужно нацеливаться на Tiger (единственная причина не использовать свойства)
Свойства по-прежнему генерируют для вас методы доступа, а атрибуты getter = / setter = в свойстве позволяют указывать имена методов. Кроме того, вы можете использовать синтаксис [foo something] вместо синтаксиса foo.something со свойствами. Так что именование аксессуаров по-прежнему актуально.
Это отличный справочник для тех, кто работает с C++, где я сделал большую часть того, что вы не советуете.
Иногда невозможно использовать стандартные сеттеры / геттеры. У меня был случай, когда сеттеру приходилось также сохранять что-то в файл / базу данных и т. д. Так что совет по-прежнему относится к созданным вручную сеттерам / геттерам.
Установщик не должен приводить к сохранению чего-либо в базе данных. Есть причина, по которой в Core Data есть метод -save: в NSManagedObjectContext, а не для того, чтобы сеттеры генерировали немедленные обновления.
в моем случае это был не вариант. Метод основных данных не мог работать. Иногда советы из учебников приходится отвергать.
Я сомневаюсь, что это был не вариант, однако, возможно, потребовалось пересмотреть архитектуру вашего приложения. (Для ясности: я не говорю: «Вам следовало использовать Core Data». Я говорю: «Установщики не должны сохранять в базу данных»). Наличие контекста для управления графом объектов, а не сохранение в нем отдельных объектов , практически всегда возможно и является лучшим решением.
Я просто вмешаюсь и упомяну, что все, что Крис говорит о Core Data, следует воспринимать как авторитетное. Он был одним из инженеров, написавших это.
По общему признанию, я новичок в iOS, но в документации Apple есть места, где они, кажется, используют «переменную-член» и «переменную экземпляра» взаимозаменяемо (например, bit.ly/hR8RGI), поэтому я не считаю это жестким правилом. .
Кроме того, если вы хотите настроить таргетинг на старые системы Mac OS X (до 10.5), у вас нет другого выбора, кроме как использовать методы доступа.
Я знаю, что упустил это из виду, когда впервые начал программировать на Какао.
Убедитесь, что вы понимаете обязанности по управлению памятью в отношении файлов NIB. Вы несете ответственность за освобождение объектов верхнего уровня в любом загружаемом вами файле NIB. Прочтите Документация Apple по этой теме.
Это неправда. Несете ли вы ответственность за выпуск объектов верхнего уровня, зависит от того, от какого класса вы наследуете и какую платформу вы используете. См., Среди прочего, developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgm t /….
Убедитесь, что вы добавили в закладки страницу Отладка Magic. Это должно быть вашей первой остановкой, когда вы бьетесь головой о стену, пытаясь найти источник ошибки какао.
Например, он расскажет вам, как найти метод, в котором вы сначала выделили память, которая позже вызывает сбои (например, при завершении работы приложения).
Теперь есть версия страницы Отладка Magic для iOS.
Если вы используете Leopard (Mac OS X 10.5) или новее, вы можете использовать приложение «Инструменты» для поиска и отслеживания утечек памяти. После создания программы в Xcode выберите «Выполнить»> «Начать с инструментом производительности»> «Утечки».
Даже если ваше приложение не обнаруживает утечек, возможно, вы слишком долго храните объекты. В инструментах для этого можно использовать инструмент ObjectAlloc. Выберите инструмент ObjectAlloc в документе «Инструменты» и откройте подробные сведения об инструменте (если он еще не отображается), выбрав «Просмотр»> «Сведения» (рядом с ним должна быть галочка). В разделе «Срок службы размещения» на детали ObjectAlloc убедитесь, что вы выбрали переключатель рядом с «Создано и еще жив».
Теперь, когда вы прекращаете запись своего приложения, выбор инструмента ObjectAlloc покажет вам, сколько ссылок есть на каждый все еще живущий объект в вашем приложении, в столбце «# Net». Убедитесь, что вы смотрите не только на свои собственные классы, но и на классы объектов верхнего уровня ваших файлов NIB. Например, если у вас нет окон на экране, и вы видите ссылки на все еще живое NSWindow, возможно, вы не выпустили его в своем коде.
Не забывайте, что NSWindowController и NSViewController будут освобождать объекты верхнего уровня файлов NIB, которыми они управляют.
Если вы вручную загружаете файл NIB, вы несете ответственность за освобождение объектов верхнего уровня этого NIB, когда вы закончите с ними.
Исторически сложилось так, что управление памятью розеток было плохим. Текущая лучшая практика - объявлять выходы как свойства:
@interface MyClass :NSObject {
NSTextField *textField;
}
@property (nonatomic, retain) IBOutlet NSTextField *textField;
@end
Использование свойств проясняет семантику управления памятью; он также обеспечивает согласованный шаблон, если вы используете синтез переменных экземпляра.
неужели загрузка пера не сохранит его дважды? (один раз в пере, второй по назначению свойству). Должен ли я выпустить их в Dealloc?
Вы должны обнулить выходы в viewDidUnload (iPhone OS 3.0+) или в пользовательском методе setView:, чтобы избежать утечек. Очевидно, вы также должны выпустить в dealloc.
Имейте в виду, что не все согласны с этим стилем: weblog.bignerdranch.com/?p=95
Так поступает и Apple. «Начало разработки iPhone 3» также упоминает об этом изменении по сравнению с предыдущими версиями.
Я упомянул об этом в другом комментарии, но должен был разместить его здесь: Как только начнется динамический синтез ivar для приложений iOS (если / когда?), Вы будете рады, что поместили IBOutlet в свойство вместо ivar!
@mmalc: у вас есть доказательства этому? голоса, кажется, тебе верят, но я скептик.
@Michael: сообщение, на которое вы ссылаетесь, не согласна с созданием IBOutlets retain, а не с прикреплением IBOutlet к собственности вместо ivar.
Обычно вы должны использовать функцию объявленных свойств Objective-C 2.0 для всех своих свойств. Если они не являются общедоступными, добавьте их в расширение класса. Использование объявленных свойств сразу делает семантику управления памятью и упрощает проверку вашего метода dealloc - если вы группируете объявления свойств вместе, вы можете быстро сканировать их и сравнивать с реализацией вашего метода dealloc.
Вам следует хорошенько подумать, прежде чем не отмечать свойства как «неатомные». Как отмечает Руководство по языку программирования Objective C, свойства по умолчанию являются атомарными и влекут за собой значительные накладные расходы. Более того, простая атомарность всех ваших свойств не делает ваше приложение поточно-ориентированным. Также обратите внимание, конечно, что если вы не укажете «неатомарный» и реализуете свои собственные методы доступа (а не синтезируете их), вы должны реализовать их атомарным способом.
ПРИМЕЧАНИЕ. В Xcode 4 это теперь встроено в IDE.
Вы используете Статический анализатор Clang, что неудивительно, чтобы проанализировать свой код C и Objective-C (еще не C++) в Mac OS X 10.5. Установить и использовать несложно:
cd в каталог вашего проекта.scan-build -k -V xcodebuild.(Есть некоторые дополнительные ограничения и т. д., В частности, вам следует проанализировать проект в его конфигурации «Отладка» - см. http://clang.llvm.org/StaticAnalysisUsage.html для подробностей - но это более или менее то, к чему он сводится.)
Затем анализатор создает для вас набор веб-страниц, которые показывают вероятное управление памятью и другие основные проблемы, которые компилятор не может обнаружить.
У меня были проблемы с тем, чтобы это работало, пока я не выполнил следующие инструкции: oiledmachine.com/posts/2009/01/06/…
В XCode 3.2.1 для Snow Leopard он уже встроен. Вы можете либо запустить его вручную, используя Выполнить -> Сборка и анализ, либо включить его для всех сборок с помощью параметра сборки «Запуск статического анализатора». Обратите внимание, что этот инструмент в настоящее время поддерживает только C и Objective-C, но не C++ / Objective-C++.
Когда методы или функции принимают аргумент строки формата, вы должны убедиться, что у вас есть контроль над содержимым строки формата.
Например, при регистрации строк возникает соблазн передать строковую переменную в качестве единственного аргумента NSLog:
NSString *aString = // get a string from somewhere;
NSLog(aString);
Проблема в том, что строка может содержать символы, которые интерпретируются как строки формата. Это может привести к ошибочному выводу, сбоям и проблемам с безопасностью. Вместо этого следует подставить строковую переменную в строку формата:
NSLog(@"%@", aString);
Меня это уже кусало.
Это хороший совет для любого языка программирования.
При сортировке строк для представления пользователю не следует использовать простой метод compare:. Вместо этого вы всегда должны использовать локализованные методы сравнения, такие как localizedCompare: или localizedCaseInsensitiveCompare:.
Подробнее см. Поиск, сравнение и сортировка строк.
Поскольку вы обычно (1) не имеете прямого контроля над их жизненным циклом, автоматически выпущенные объекты могут сохраняться в течение сравнительно долгого времени и излишне увеличивать объем памяти, занимаемый вашим приложением. Хотя на настольных компьютерах это может иметь незначительные последствия, на более ограниченных платформах это может стать серьезной проблемой. Поэтому на всех платформах, и особенно на платформах с более ограниченными возможностями, рекомендуется избегать использования методов, которые могут привести к автоматическому выпуску объектов, и вместо этого рекомендуется использовать шаблон alloc / init.
Таким образом, а не:
aVariable = [AClass convenienceMethod];
где возможно, вместо этого следует использовать:
aVariable = [[AClass alloc] init];
// do things with aVariable
[aVariable release];
Когда вы пишете свои собственные методы, которые возвращают вновь созданный объект, вы можете воспользоваться Соглашение об именах какао, чтобы указать получателю, что он должен быть освобожден, добавив к имени метода слово «новый».
Таким образом, вместо:
- (MyClass *)convenienceMethod {
MyClass *instance = [[[self alloc] init] autorelease];
// configure instance
return instance;
}
можно было написать:
- (MyClass *)newInstance {
MyClass *instance = [[self alloc] init];
// configure instance
return instance;
}
Поскольку имя метода начинается с «new», потребители вашего API знают, что они несут ответственность за освобождение полученного объекта (см., Например, Метод newObject NSObjectController).
(1) Вы можете взять под контроль свои собственные локальные пулы с автоматическим выпуском. Подробнее об этом см. Пулы с автоматическим выпуском.
Похоже, что ссылка "Соглашение об именах Какао" перемещена. Попробуйте developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgm t /…
Эти ошибки с автоматическим выпуском укусили меня много-много раз за мою короткую карьеру разработчика iPhone. Сознательное избегание ситуаций могло бы значительно смягчить.
Я считаю, что преимущества нет с использованием autorelease перевешивают его затраты (то есть больше ошибок утечки памяти). Код в основном потоке в любом случае должен быть довольно коротким (иначе пользовательский интерфейс будет зависать), а для более длительного фонового кода, интенсивно использующего память, вы всегда можете обернуть интенсивно потребляющие память части в локальные пулы автозапуска.
Я не согласен. По возможности следует использовать автоматически выпущенные объекты. Если они слишком увеличивают объем памяти, вам следует использовать другой NSAutoreleasePool. Но только после того, как вы подтвердите, что это действительно проблема. Преждевременная оптимизация и все такое ...
Я трачу меньше 40 сек. день, набрав [someObject release] и прочитав «лишнюю строку» при создании экземпляра нового объекта, но однажды я потратил 17 часов на то, чтобы найти ошибку автозапуска, которая проявлялась только в особых случаях и не давала внятной ошибки в консоли. Так что я согласен с Адибом, когда он говорит: «Я считаю, что преимущества неиспользования автоспуска перевешивают его затраты».
Нет ничего плохого в использовании AutoreleasePool, если вы получаете право собственности на объект; в вашем примере я бы использовал свойства: self.aVariable = [AClass comfortMethod]; фактически, объекты с автоматическим выпуском являются самой причиной использования при возврате из методов; это совет, данный Apple и Стэнфордским курсом iPhone.
Я согласен со Свеном. Основная цель должна заключаться в ясности кода и сокращении ошибок кодирования с оптимизацией памяти только там, где это необходимо. Ввод [[[Foo alloc] init] autorelease] выполняется быстро, и вы сразу же решаете проблему выпуска этого нового объекта. При чтении кода вам не нужно искать соответствующий выпуск, чтобы убедиться, что он не просочился.
Жизненный цикл автоматически выпущенных объектов четко определен и определяется на достаточном уровне.
Жизненный цикл обычно представляет собой один цикл цикла событий. Это означает, что временные объекты могут накапливаться в процессе касания, смахивания или чего-то еще. Согласно исходному ответу, допускать это - не лучшая практика, особенно когда вы можете осуществлять более прямой контроль.
Синтаксис autoreleased обеспечивает гораздо лучшую читаемость кода, чем дополнительные вызовы функций alloc init и дополнительная строка для выпуска. Если ваш объект не освобождается после цикла выполнения, это означает, что объект удерживается чем-то еще и не будет освобожден, даже если вы освобождали его «вручную», проблема не будет в «какой-то ошибке автоматического освобождения». .
@mmalc в случае, когда вы имеете дело с большим количеством распределений в пределах одной итерации цикла выполнения, вы можете создать вложенный пул автозапуска и немедленно его слить. Нет необходимости освобождать объекты вручную.
Принципиальный отказ от автоматического выпуска - отличный способ сделать ваш код менее удобным в обслуживании, более подверженным ошибкам и сложным для понимания без уважительной причины (в большинстве случаев).
Как отмечает этот вопрос, сообщения для nil действительны в Objective-C. Хотя это часто является преимуществом, приводящим к более чистому и естественному коду, эта функция может иногда приводить к специфическим и трудным для отслеживания ошибкам, если вы получаете значение nil, которого не ожидали.
У меня такой: #define SXRelease(o); o = nil и такой же на CFRelease и free. Это все упрощает.
Некоторые из них уже упоминались, но вот то, что я могу придумать в своей голове:
#pragma mark [section]. Обычно я группирую по своим собственным методам, переопределениям каждого подкласса и любой информации или формальным протоколам. Это значительно упрощает переход к тому, что я ищу. По той же теме группируйте похожие методы (например, методы делегата табличного представления) вместе, а не просто прикрепляйте их куда-нибудь.#define, или кеширование массива вместо его сортировки каждый раз, когда требуются данные. Я могу многое сказать по этому поводу, но главное - не пишите код, пока он вам не понадобится или пока профилировщик не скажет вам об этом. Это значительно упрощает обслуживание в долгосрочной перспективе.NSLog( @"stub" ) внутрь, или как вы хотите отслеживать вещи.Я бы посоветовал вам поместить частные методы в продолжение класса. (например, @interface MyClass () ... @end в вашем .m)
Вместо #PRAGMA вы можете использовать комментарий // Mark: [Section], который более переносим и работает идентично.
Если нет специального синтаксиса, который мне не хватает, // Mark: не добавляет метку в раскрывающееся меню функций Xcode, что на самом деле является половиной причины его использования.
Вам нужно использовать прописные буквы "// MARK: ...", чтобы он отображался в раскрывающемся списке.
Что касается Finish what you start, вы также можете использовать // TODO: для отметки кода для завершения, который будет отображаться в раскрывающемся списке.
Как только ivars будут динамически синтезированы в будущем (для приложений iOS), вы будете рады, что разместили IBOutlets в свойствах!
@iWasRobbed или #warning, который будет отображаться в выводе компилятора.
Все эти комментарии великолепны, но я очень удивлен, что никто не упомянул Руководство по стилю Google Objective-C, который был опубликован некоторое время назад. Я считаю, что они проделали очень основательную работу.
Хм, первый пример уже полон чуши. Никогда не документируйте языковые идиомы. Если бы я нашел такие комментарии в заголовочном файле, я бы не стал читать дальше.
Ой глаза !!!!! Я не могу поверить в то, что увидел.
Используйте NSAssert и друзей. Я все время использую nil как допустимый объект ... особенно отправка сообщений на nil совершенно допустима в Obj-C. Однако, если я действительно хочу убедиться в состоянии переменной, я использую NSAssert и NSParameterAssert, которые помогают легко отслеживать проблемы.
Подробнее здесь: developer.apple.com/mac/library/documentation/Cocoa/Referenc e /… А здесь: stackoverflow.com/questions/2521275/what-is-nsparameterasser т
Предоставленные Apple образцы, которые я видел, рассматривали делегат приложения как глобальное хранилище данных, своего рода диспетчер данных. Это заблуждение. Создайте синглтон и, возможно, создайте его экземпляр в делегате приложения, но не используйте делегат приложения как нечто большее, чем обработка событий на уровне приложения. Я искренне поддерживаю рекомендации в эта запись в блоге. Эта ветка предупредил меня.
Вы когда-нибудь слышали, чтобы кто-нибудь говорил вам, что синглтон - это антипаттерн? Для этого есть причина .... Я разочарован тем, что синглтоны все еще рассматриваются программистами какао как ответ на все беды.
-1. Синглтон определенно является анти-шаблоном, когда вы решаете, что вашему приложению требуется более одной базы данных (синхронизация, несколько пользователей, ...).
Включите все предупреждения GCC, затем отключите те, которые регулярно вызываются заголовками Apple, чтобы уменьшить шум.
Также часто запускайте статический анализ Clang; вы можете включить его для всех сборок с помощью параметра сборки «Запуск статического анализатора».
Напишите модульные тесты и запускайте их с каждой сборкой.
И, если можете, включите «Обрабатывать предупреждения как ошибки». Не позволяйте предупреждению существовать.
Удобный скрипт для настройки вашего проекта с рекомендуемыми предупреждениями доступен здесь: rentzsch.tumblr.com/post/237349423/hoseyifyxcodewarnings-scp t
Один довольно очевидный для новичка: используйте функцию автоматического отступа Xcode для своего кода. Даже если вы копируете / вставляете из другого источника, после того, как вы вставили код, вы можете выбрать весь блок кода, щелкнуть его правой кнопкой мыши и затем выбрать вариант повторного отступа всего в этом блоке.
Xcode фактически проанализирует этот раздел и сделает отступ на основе скобок, циклов и т. д. Это намного эффективнее, чем нажатие клавиши пробела или табуляции для каждой строки.
Вы даже можете настроить Tab на отступ, а затем использовать Cmd-A и Tab.
Простой, но о котором часто забывают. Согласно спецификации:
In general, methods in different classes that have the same selector (the same name) must also share the same return and argument types. This constraint is imposed by the compiler to allow dynamic binding.
в этом случае все селекторы с одинаковыми именами, даже если в разных классах, будут считаться имеющими идентичные типы возврата / аргумента. Вот простой пример.
@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's 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;
}
это легко забыть. Тем не менее важно
Это вызывает беспокойство только при отказе от статической типизации. Если компилятор знает тип, типы аргументов и возвращаемых значений могут отличаться без проблем. Лично я не часто считаю, что это проблема. У Apple также есть много методов с одинаковыми именами, но различающихся типами возврата. Наконец, есть флаг компилятора, который предупреждает вас в неоднозначных случаях.
Если мы будем следовать рекомендациям Apple по соглашению об именах, такой ситуации не будет :)
Будьте более функциональный.
Objective-C - объектно-ориентированный язык, но он поддерживает функциональный стиль фреймворка Какао и во многих случаях разрабатывается в функциональном стиле.
Есть разделение изменчивости. Используйте классы неизменный в качестве первичных, а изменяемый объект в качестве вторичных. Например, используйте в первую очередь NSArray и используйте NSMutableArray только тогда, когда вам нужно.
Есть чистые функции. Не так много, покупайте многие API-интерфейсы фреймворка, разработанные как чистые функции. Посмотрите на такие функции, как CGRectMake() или CGAffineTransformMake(). Очевидно, форма указателя выглядит более эффективной. Однако косвенный аргумент с указателями не может обеспечить отсутствие побочных эффектов. Проектируйте конструкции максимально чисто.
Разделяйте даже государственные объекты. Используйте -copy вместо -retain при передаче значения другому объекту. Потому что общее состояние может незаметно влиять на изменение значения в другом объекте. Так что не может быть без побочных эффектов. Если у вас есть значение из внешнего объекта, скопируйте его. Так что также важно разработать как можно более минимальное общее состояние.
Однако не бойтесь использовать и нечистые функции.
Есть ленивая оценка. Посмотрите что-то вроде свойства -[UIViewController view]. Представление не будет создано при создании объекта. Он будет создан при первом чтении свойства view вызывающим абонентом. UIImage не будет загружен, пока он не будет отрисован. Есть много реализаций, подобных этому дизайну. Такие конструкции очень полезны для управления ресурсами, но если вы не знакомы с концепцией ленивой оценки, понять их поведение непросто.
Есть закрытие. По возможности используйте C-блоки. Это значительно упростит вам жизнь. Но прежде чем использовать его, прочтите еще раз об управлении блочной памятью.
Есть полуавтоматический GC. NSAutoreleasePool. Используйте первичный -autorelease. Используйте ручной -retain/-release вторичный, когда вам действительно нужно. (например: оптимизация памяти, явное удаление ресурсов)
Что касается 3), я предлагаю противоположный подход: используйте ручное сохранение / освобождение везде, где это возможно! Кто знает, как этот код будет использоваться - и если он будет использоваться в жестком цикле, это может без надобности взорвать ваше использование памяти.
@Eiko Это просто Преждевременная оптимизация, не может быть общим руководством.
Я думаю, что это больше связано с дизайном, особенно при работе с классами моделей. Я считаю рост памяти побочным эффектом, и это не то, о чем я хочу часто появляться. Хуже того, у другого разработчика, использующего мой код, нет шансов, кроме как обернуть дорогостоящие вызовы в пулы автозапуска (если это вообще возможно - мои объекты могут быть отправлены в какой-то другой код библиотеки). И эти проблемы трудно диагностировать позже, но их дешево избежать в первую очередь. Если вы копируете / автоматически выпускаете переданные объекты, вы можете потеряться, если они намного больше, чем вы ожидали. Однако я более расслаблен с кодом графического интерфейса.
@Eiko Я согласен, что autorelease будет держать память дольше в целом, а ручной retain/release может уменьшить потребление памяти в этом случае. Однако это должно быть руководством для оптимизации в особых случаях (даже если вы чувствуете себя всегда!), Не может быть причиной обобщения преждевременной оптимизации как упражняться. И на самом деле ваше предложение не противоречит моему. Я упомянул это как случай очень нужно :)
Освободите только имущество в методе освободить. Если вы хотите освободить память, которую удерживает имущество, просто установите ее как nil:
self.<property> = nil;
Обратите внимание, что по этому поводу ведутся большие споры. Использование установщика в dealloc может привести к ошибке, если вы не освобождаете свойства в правильном порядке. Однако то же самое верно и при прямом использовании release - могут появиться другие ошибки. Apple рекомендует использовать release в dealloc, но они используют сеттер в своей реализации (например, выпуск delegate в [UITableView dealloc])
Переменные и свойства
1 / Сохранение чистоты заголовков, скрытие реализации
Не включайте переменные экземпляра в заголовок. Частные переменные помещаются в продолжение класса как свойства. Публичные переменные объявляются в вашем заголовке как общедоступные свойства.
Если он должен быть только для чтения, объявите его как доступный только для чтения и перезапишите его как readwrite в продолжении класса.
В основном я вообще не использую переменные, а только свойства.
2 / Дайте вашим свойствам имя переменной, отличное от имени переменной по умолчанию, например:
@synthesize property = property_;
Причина 1: вы обнаружите ошибки, вызванные забыванием «я». при передаче собственности. Причина 2: Судя по моим экспериментам, у Leak Analyzer in Instruments есть проблемы с обнаружением утечки с именем по умолчанию.
3 / Никогда не используйте удержание или выпуск непосредственно на свойствах (или только в очень исключительных ситуациях). В вашем dealloc просто присвойте им ноль. Свойства удержания предназначены для самостоятельной обработки удержания / освобождения. Вы никогда не узнаете, не добавляет ли сеттер, например, или удаляет наблюдателей. Вы должны использовать переменную напрямую только внутри ее установщика и получателя.
Взгляды
1 / Поместите каждое определение представления в xib, если можете (исключение обычно составляет динамический контент и настройки уровня). Это экономит время (это проще, чем писать код), его легко изменить, и он сохраняет ваш код чистым.
2 / Не пытайтесь оптимизировать просмотры, уменьшая количество просмотров. Не создавайте UIImageView в своем коде вместо xib только потому, что вы хотите добавить в него подпредставления. Вместо этого используйте UIImageView в качестве фона. Фреймворк представления может без проблем обрабатывать сотни представлений.
3 / IBOutlets не обязательно всегда сохранять (или быть сильными). Обратите внимание, что большинство ваших IBOutlet являются частью вашей иерархии представлений и поэтому неявно сохраняются.
4 / Освободить все IBOutlets в viewDidUnload
5 / Вызовите viewDidUnload из вашего метода dealloc. Это не вызывается неявно.
объем памяти
1 / Автозапуск объектов при их создании. Многие ошибки вызваны перемещением вашего релизного вызова в одну ветку if-else или после оператора return. Release вместо autorelease следует использовать только в исключительных ситуациях - например, когда вы ждете выполнения цикла выполнения и не хотите, чтобы ваш объект был автоматически выпущен слишком рано.
2 / Даже если вы используете автоматический подсчет ссылок, вы должны хорошо понимать, как работают методы сохранения-освобождения. Использование удержания-выпуска вручную не сложнее, чем ARC, в обоих случаях вам нужно подумать об утечках и циклах удержания. Рассмотрите возможность использования сохранения-выпуска вручную для больших проектов или сложных иерархий объектов.
Комментарии
1 / Сделайте свой код автодокументированным. Каждое имя переменной и имя метода должны указывать на то, что они делают. Если код написан правильно (в этом вам потребуется много практики), вам не понадобятся комментарии к коду (не то же самое, что комментарии к документации). Алгоритмы могут быть сложными, но код всегда должен быть простым.
2 / Иногда вам понадобится комментарий. Обычно для описания неявного поведения кода или взлома. Если вы чувствуете, что должны написать комментарий, сначала попробуйте переписать код, чтобы он был проще и без комментариев.
Отступ
1 / Не увеличивайте слишком много отступов. Большая часть кода вашего метода должна иметь отступ на уровне метода. Вложенные блоки (if, for и т. д.) Ухудшают читаемость. Если у вас есть три вложенных блока, вы должны попытаться поместить внутренние блоки в отдельный метод. Ни в коем случае нельзя использовать четыре или более вложенных блока. Если большая часть кода вашего метода находится внутри if, отмените условие if, например:
if (self) {
//... long initialization code ...
}
return self;
if (!self) {
return nil;
}
//... long initialization code ...
return self;
Понять C-код, в основном C-структуры
Обратите внимание, что Obj-C - это только легкий слой ООП над языком 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 / C++ и наших собственных дополнений. Задокументируйте свой код, используйте стандартные стандартные отступы, пробелы и пустые строки в нужных местах и т. Д.
#import "MyClass.h"
@interface MyClass ()
- (void) someMethod;
- (void) someOtherMethod;
@end
@implementation MyClass
см. это сообщение в блоге, очень приятно. ironwolf.dangerousgames.com/blog/archives/913