настройка свойств и делегатов — когда использовать self?

#iphone #objective-c #properties #protocols

#iPhone #objective-c #свойства #протоколы

Вопрос:

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

В интерфейсе AppDelegate объявлено, что он следует за NSXMLParserDelegate protocol , но, похоже, он не реализует ни один из методов протокола или не устанавливает себя в качестве делегата в любой момент. Похоже, они находятся в ParseOperation классе. Это просто опечатка?

 @interface SeismicXMLAppDelegate : NSObject <UIApplicationDelegate, NSXMLParserDelegate> {
    UIWindow *window;
    UINavigationController *navigationController;
    RootViewController *rootViewController;

@private
    // for downloading the xml data
    NSURLConnection *earthquakeFeedConnection;
    NSMutableData *earthquakeData;

    NSOperationQueue *parseQueue;
}
  

Интерфейс также объявляет некоторые частные свойства. Они снова определены в расширении интерфейса в файле .m.

 @interface SeismicXMLAppDelegate ()

@property (nonatomic, retain) NSURLConnection *earthquakeFeedConnection;
@property (nonatomic, retain) NSMutableData *earthquakeData;    // the data returned from the NSURLConnection
@property (nonatomic, retain) NSOperationQueue *parseQueue;     // the queue that manages our NSOperation for parsing earthquake data

- (void)addEarthquakesToList:(NSArray *)earthquakes;
- (void)handleError:(NSError *)error;
@end
  

Хорошо, я думаю, я понимаю, что это означает, что другие классы не могут получить доступ к этим свойствам, и это кажется действительно хорошей вещью в данном случае. Вот загадка — в реализации — applicationDidFinishLaunching: два свойства определяются с использованием разных обозначений.

 self.earthquakeFeedConnection =
[[[NSURLConnection alloc] initWithRequest:earthquakeURLRequest delegate:self] autorelease];
  

против

 parseQueue = [NSOperationQueue new];
  

Почему одному назначается using self.propertyName = , а другому with propertyName = ? Я знаю, что оба parseQueue и earthquakeFeedConnection в конечном итоге сохраняют количество, равное 1, с разницей, которая earthquakeFeedConnection является частью autorelease pool и будет выпущена автоматически, тогда как нам придется выпустить parseQueue позже из-за использования new и не вызова autorelease .

Соображения памяти не объясняют использование self . Есть ли еще одно отличие?

Ответ №1:

alloc и new возвращать объекты, которые сохраняются один раз. init не влияет на управление памятью и autorelease выпустит этот объект позже один раз.

если вы пишете self.myProperty = ... synthesized , вызывается установщик, который ведет себя так, как вы определили в соответствующем свойстве nonatomic, retain . Это nonatomic означает, что средство получения и установки не являются потокобезопасными (но быстрыми). retain в этом случае означает, что установщик освободит старый объект и сохранит новый объект. если бы вы написали assign вместо retain установщика, он просто назначил бы указатель и не вызывал бы release или retain для затронутых объектов.

Цель в вашем примере — создать два объекта, которые сохраняются один раз.
Пример1:

 self.earthquakeFeedConnection = [[[NSURLConnection alloc] initWithRequest:earthquakeURLRequest delegate:self] autorelease];
  
  • после выделения: сохранение количества: 1
  • после автоматического выпуска: сохранить количество: 1-1 (минус один означает: выпущен позже)
  • после вызова установщика: retainCount: 2-1

Пример 2:

parseQueue = [NSOperationQueue new];

  • после создания: сохранение количества: 1

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

 earthquakeFeedConnection = [[NSURLConnection alloc] initWithRequest:earthquakeURLRequest delegate:self];
  

Сеттер-решение выглядит более сложным, но есть некоторые побочные эффекты. Если позже вы заметите, что вам нужно особое поведение внутри средства получения или установки (например: запуск некоторых других методов или выполнение некоторой проверки значений: NSString, которая должна содержать только адреса электронной почты), тогда вам нужно только перезаписать средство получения или установки. Если вы не используете self.myProp = ... , вам придется искать usage и также изменять этот фрагмент кода.

Материал делегата: да, вы правы: обычно вы должны (или должны) перечислять все реализованные протоколы в определении интерфейса, но NSURLConnectionDelegate это исключение. Я не знаю почему (я не нахожу точку реализации в NSObject ссылке на класс), но каждый NSObject уже реализует этот делегат. Таким образом, в результате вам не нужно упоминать, что ваш класс реализует этот делегат.

Комментарии:

1. Спасибо за разъяснение методов протокола NSURLConnection, но как насчет методов делегирования NSXMLParser? Он не реализует ничего . Я отредактирую свой пост, потому что, думаю, я понимаю последствия сохранения количества. Но это простой пример приложения. Пользовательской реализации установщиков и получателей нет. Итак, почему parseQueue назначается без использования self.parseQueue ?

2. Я думаю, это просто стиль кодирования автора (или просто фрагмент копирования-вставки). Вы также можете писать self.parseQueue = [[NSOperationQueue new] autorelease]; . Если он не реализует NSXMLParserDelegate , то это не нужно. Вы говорите, что это NSXMLParser-Tutorial , возможно, фактический алгоритм синтаксического анализа передан в другой файл, и автор просто забыл удалить эту реализацию протокола. Это проблема, если протокол содержит только необязательные реализации… Можете ли вы опубликовать ссылку на этот учебник?

3. Это пример кода от Apple. Вы можете найти его по адресу developer.apple.com <a href=developer.apple.com/library/ios/#samplecode/SeismicXML /… title=»SeismicXML»</a>. Я добрался до этого из документации класса по NSOperations.

4. да, я взглянул на этот документ. Кажется, что NSXMLParserDelegate в AppDelegate это просто остаток (его там не должно быть). Во время компиляции не происходит никаких предупреждений или ошибок, потому NSXMLParser что все методы являются необязательными. Alloc-init против новая вещь: я думаю, что это просто стиль кодирования автора: он любит использовать new без вызова установщика, за исключением того, что он хочет использовать initWith... -method: тогда он также использует установщик. На самом деле это должно отражать настроение автора или просто стиль кодирования автора…

Ответ №2:

Резюме: ответ Томаса подтверждает загадку протокола делегирования. Объявление, как я и предполагал изначально, просто опечатка. Итак, я не скучаю по лодке, что приятно, поэтому я принимаю его ответ.

Ответ об использовании -alloc, -init vs new менее удовлетворительный. Фраза «просто стиль кодирования», похоже, игнорирует что-то важное, поэтому я провел собственный анализ. По сути, я думаю, что это защитный элемент стиля авторов: этот подход не делает никаких предположений об инициализированном объекте.

verbose=N Объединение new и настраиваемого установщика может привести к проблемам. Если пользовательская реализация установщика предполагает, что инициализированный объект имеет определенное состояние (например, что-то, что могло быть сделано в пользовательском методе инициализации), могут быть плохие последствия. Избегайте любых конфликтов, не делая предположений об объекте. Убедитесь, что пользовательский установщик не вызывается, если настройка явно не была выполнена. Таким образом, безопасно вызывайте [self setPropCustom] = [[PropClass alloc] initWithCustomization] и избегайте вызова [self setPropCustom] для [[PropClass alloc] init] . Поскольку -init часто переопределяется, получите минимальный объект с помощью new вместо -alloc ,-init .

vervose=Y Я в некотором роде новичок. У меня есть опыт в выполнении задач, но очень мало в том, чтобы делать это элегантно или в стиле CS. Итак, меня интересуют стили кодирования. Почему автор может «любить» делать это определенным образом? Я действительно сомневаюсь, что это просто «настроение».

В блоге Уилла Шипли много обсуждается стиль кодирования, особенно серия «Pimp my code», которая является своего рода жемчужиной. Он включает в себя довольно подробное обсуждение new vs -alloc,-init . Я процитирую один из комментариев:

сказал Корвин…

[отредактировано] Код — это ремесло. Это неточно. Писать код, который следует стилю всех остальных, все равно что писать стихи в том же стиле и стиле, что и все остальные. Подумайте немного, осознайте, что каждая маленькая особенность, которую привносит ваш код, — это маленькая часть вас, и постарайтесь сделать его «идеальным». Читайте чужой код, как стихи — черпайте в нем вдохновение, учитесь на нем, иногда подражайте ему, но никогда не воспринимайте это как личное оскорбление.

В конце концов, вы должны спросить себя: создал ли код, который я написал, искусство? Облегчит ли это жизнь пользователя? Это заставило меня чувствовать себя хорошо? Если вы можете ответить на эти вопросы да, все в порядке. Все остальное недействительно.
25 августа 2005 г. 12:08

Итак, в этом ключе, что я могу узнать из стиля кодирования в NSXMLParser? Какое вдохновение я могу извлечь из этого? Ну…

Смысл наличия стиля кодирования заключается в том, чтобы предоставить себе дополнительную информацию о том, как вы решаете что-то делать, и использовать согласованные шаблоны, чтобы избежать известных ошибок. Обычно существует несколько способов достижения желаемого результата. Выделение памяти — это то, что программисты делают постоянно. Он вызывается так часто, что помогает быть последовательным в том, как вы это делаете. Боже, если бы я был последователен в том, куда я кладу обувь ребенка, мне не пришлось бы рыскать по 10 минут каждое утро. Согласованность — это короткий путь, а стиль кодирования обеспечивает согласованность кода.

Поэзия — это многозначность. Речь идет не просто о вызове слова, но и о одновременном вызове эмоции или ощущения. … такой сочный и такой сладкий.

Итак, разработка стиля кодирования — это разработка краткого и последовательного средства общения с другим программистом или с вашим будущим «я». Вам не нужно спрашивать «что я здесь делал». Это должно быть частью вашего стиля кодирования.

Итак, вернемся к NSXMLParser. По словам Томаса, этот автор «любит использовать new без вызова установщика, за исключением того, что он хочет использовать initWith... -метод: тогда он также использует установщик». (thomas, 4 октября). Что я хочу знать, так это почему автор выбрал это соглашение как часть своего стиля кодирования? В чем смысл этого?

Конечно, я бы не стал писать это, если бы не думал, что понимаю.

Когда вы пишете класс, вы можете переопределить -init , но вы не переопределяете new . Когда вы пишете класс, вы можете переопределить синтезированные установщики и получатели -setProperty и -property .

Последовательно присваивая свойство непосредственно при использовании new , автор использует синтаксис, чтобы подчеркнуть, что это «обнаженный» объект. Пусто. Самый минимум. Затем он обязательно назначает его, не вызывая установщика. Это кажется хорошей идеей. В конце концов, объединение new и пользовательского установщика может привести к проблемам, если пользовательская реализация установщика предполагает что-либо о состоянии инициализированного объекта (например, что-то, что могло быть сделано в пользовательском методе инициализации).Лучше избегать проблемы.Ах, вот оно. Ответ, который я искал.

Итак, что я могу в моем состоянии ragged noobie извлечь из этого «стиля кодирования». Хммм … этому придется повариться некоторое время. Возьмите номер 1:

  1. Вызов new и избегание любых пользовательских установщиков — это защитное кодирование.
  2. Настройка -init и настройки по умолчанию должны выполняться с осторожностью.
  3. Если требуется пользовательский -init метод, возможно, было бы лучше переименовать его -initWithDefaults или что-то в этом роде. С другой стороны, если вы постоянно используете new with assignment без вызова self , то вам, вероятно, сойдет с рук больше настроек, но вы должны быть последовательными.

Я достаточно взрослый, чтобы точно знать, что я не последователен. Поиск обуви происходит не реже одного раза в неделю, несмотря на некоторые усилия с моей стороны. Итак, мне нужен защитный стиль кодирования. С этой целью я должен сделать оба: 1) развить любовь к new с прямым назначением, когда требуется голый объект, и 2) стараться избегать переопределения реализаций init по умолчанию и синтезированных установщиков и добытчиков.

Как говорит Томас:

«есть некоторые побочные эффекты. Если позже вы заметите, что вам нужно особое поведение внутри средства получения или установки (например: запуск некоторых других методов или выполнение некоторой проверки значений: NSString, которая должна содержать только адреса электронной почты), тогда вам нужно только перезаписать средство получения или установки. Если вы не используете self.myProp = … затем вам нужно выполнить поиск по использованию и также изменить этот фрагмент кода. ‘

Достаточно справедливо. Если я не буду настраивать установщики и получатели, мне в конечном итоге придется убедиться, что мое дополнительное поведение происходит где-то в другом месте. Но, на самом деле, я уже склонен делать это, реализуя такие вещи, как -changeLocationTo:(CLLocation *)aPlace inTimeZone:(NSTimeZone *)Часовой пояс’. Проверка идет туда, и довольно очевидно, что она делает из именования, и ее существование напоминает мне, что когда я меняю aPlace , я также хочу убедиться, что я проверяю его timeZone . Я вызываю значение по умолчанию self.setTimeZone и self.setAPlace оттуда и выполняю любую очистку. Начало стиля. Но, вероятно, у него есть некоторые недостатки…