#objective-c
#objective-c
Вопрос:
Я начинаю проект objective-c. У меня есть вопрос относительно неизменяемости. Стоит ли пытаться делать объекты неизменяемыми всякий раз, когда я могу? Если я обновляю поле, мне приходится возвращать указатель на новый объект и освобождать старый. Если я буду делать это часто, могут возникнуть проблемы с производительностью. Кроме того, код, вероятно, будет более подробным. Несомненно, есть и другие соображения. Что вы думаете?
Редактировать: Позвольте мне пояснить, что я имею в виду, когда пишу «обновить поле». Обычно, когда вы обновляете поле, вы вызываете установщик и просто изменяете значение поля. Если объект неизменяем, установщик фактически не обновляет поле, вместо этого он создает новый экземпляр, со всеми полями, имеющими одинаковое значение, за исключением поля, которое вы пытаетесь обновить. В java:
class User{
private String firstName;
private String lastName;
public User(String fn, String ln){ firstName = fn; lastName = ln; }
public User setFirstName(String fn){ return new User(fn, lastName); }
}
Комментарии:
1. По определению, вы не смогли обновить «поле» неизменяемого объекта, поэтому не совсем понятно, о чем вы спрашиваете. Кроме того, вопрос изменяемости не является специфичным для языка Objective-C, а скорее является общим соображением при разработке программного обеспечения.
2. Значит, метод setter на самом деле будет не мутатором, а конструктором? Это кажется … странным.
3. Я с Bavarious. То, что установщик возвращает объект, очень странно даже в Java, а в ObjC, безусловно, противоречит соглашению и KVC. Правильное имя для этого метода должно быть
userByChangingFirstName
. Но вопрос о том, что значит «мутировать» неизменяемый класс, справедлив. РассмотримstringByAppendingString:
. Это очень распространенный функциональный шаблон.4. Да, я согласен. Я только что привел пример. Возможно, пользователь заменяет FirstName(строка fn).
Ответ №1:
Используйте неизменяемые объекты, когда это возможно, из-за накладных расходов на производительность изменяемых объектов.
Редактировать: Ну, обычно вышесказанное должно быть правдой, но, похоже, бывают ситуации, когда производительность NSMutableArray на самом деле лучше, чем NSArray. Подробнее об этом читайте на сайте Cocos2d:
Прочитайте еще немного об изменяемости на CocoaWithLove (отличный блог для разработчиков Mac / iOS, поэтому добавьте его в избранное!).
Я также хотел бы добавить, что многие объекты имеют метод экземпляра -mutableCopy, это простой в использовании метод для извлечения изменяемой копии из неизменяемых объектов, таких как NSArray или NSString, например:
NSArray *array = [NSArray arrayWithObjects:@"apple", @"pear", @"lemon"];
NSMutableArray *mutableArray = [array mutableCopy];
// remember to release the mutableArray at some point
// because we've created a copy ...
Просто помните, что в некоторых ситуациях изменяемый объект проще в использовании, например, для UITableView, который использует источник данных, подверженный множеству изменений с течением времени.
Комментарии:
1. Вы имели в виду написать «Использовать изменяемый объект всякий раз, когда это возможно, из-за накладных расходов на производительность неизменяемых объектов»?
2. Обычно неизменяемые (т. Е. не изменяемые) объекты должны иметь меньше накладных расходов.
3. Мне любопытно. Как вы думаете, почему у неизменяемых объектов меньше накладных расходов? Одна из проблем в языках, которые подчеркивают неизменяемость (таких как clojure, scala, haskell и языки функционального программирования в целом), заключается в том, что обновление структуры данных может быть медленным, если выполнено неправильно. Например, добавление элемента в список означает возврат нового списка, и наивный (и неэффективный) способ сделать это — вернуть копию списка.
4. @yalis: Я должен признать, что я не уверен на 100%, почему неизменяемые объекты в целом, по-видимому, работают лучше в Cocoa framework (хотя я мог бы сделать несколько предположений), но я видел сравнения производительности в прошлом в Интернете, поэтому я счел хорошей практикой использовать неизменяемые объекты, когда это возможно, и изменяемые только при необходимости.
Ответ №2:
Выбор между изменяемыми или неизменяемыми объектами сильно зависит от ситуации, поэтому лучше всего, если вы приведете более конкретный пример для обсуждения. Но вот о чем стоит подумать.
-
Часто свойства объекта каким-то образом взаимосвязаны. Например,
Person
может иметьgivenName
иsurname
, но также может иметьfullName
, объединяющее эти два, и может иметьnameOrder
, указывающее, какое из них стоит первым. Если вы сделаете Person изменяемым, то могут быть моменты времени, которыеfullName
могут быть неверными, потому что вы изменилиsurname
, но неgivenName
(возможно, один из них все еще остаетсяnil
). Теперь вам нужен более сложный интерфейс, чтобы защитить вас от этого. -
Если другие объекты используют этот изменяемый параметр
Person
, они должны использовать KVO или уведомления, чтобы узнать, когда он изменился. Тот факт, что взаимосвязанные поля могут изменяться независимо, может усложнить задачу, и вы обнаружите, что пишете код для объединения изменений. -
Если некоторые комбинации свойств недопустимы, изменяемые объекты может быть очень сложно проверить на ошибку. Неизменяемый объект может выполнять всю свою проверку при его создании.
-
Между изменяемым и неизменяемым есть несколько промежуточных оснований. В приведенном выше примере
Person
и различных свойств name один из способов упростить многое из этого — позволитьPerson
быть изменяемым, но создать отдельный неизменяемыйName
объект, который содержит различные части. Таким образом, вы можете убедиться, что все имя изменено атомарным образом. -
Неизменяемые объекты значительно упрощают многопоточный код. Изменяемые объекты требуют гораздо большей блокировки и синхронизации, и это может значительно снизить производительность и стабильность. Этот код очень легко испортить. Неизменяемые объекты по сравнению с ними тривиальны.
-
Что касается вашего замечания о создании и удалении объектов, неизменяемые объекты также предоставляют возможность совместного использования, что может сделать их очень эффективными, если существует вероятность того, что будет много объектов, указывающих на одно и то же содержимое данных. Например, в нашем
Person
примере, если я создам неизменяемыйAddress
объект, то каждый человек, который живет по тому же адресу, может совместно использовать один и тот же объект. Если кто-то меняет свой адрес, это не влияет на все остальные. -
В качестве примера вышеизложенного, в моем коде содержится много адресов электронной почты. Чрезвычайно часто одна и та же строка отображается снова и снова. Создание
EmailAddress
неизменяемого и разрешение создавать его только с помощьюemailAddressForString:
позволяет классу поддерживать кэш, и это может сэкономить значительную память и время на создание и уничтожение строковых объектов. Но это работает только потому, чтоEmailAddress
является неизменяемым.
В любом случае, мой опыт показывает, что часто для простоты лучше ошибиться в сторону неизменяемых объектов данных и изменять их только тогда, когда неизменяемость создает проблемы с производительностью. (Конечно, это относится только к объектам данных. Объекты с сохранением состояния — это совсем другое дело, и, конечно, они должны быть изменяемыми по своей природе, но это не означает, что каждая их часть должна быть изменяемой.)
Комментарии:
1. Спасибо, что упомянули многопоточный код; я как раз собирался это сделать.
2. Я согласен со всеми вашими замечаниями в целом. И такие языки, как clojure, идут на многое, чтобы сделать эффективную неизменяемость нормой. Например, карта в clojure — это объект с отслеживанием состояния со всей семантикой неизменяемости (вы не обновляете на месте, а возвращаете копию). Но objective-c не ориентирован на неизменяемость, и вам приходится создавать свои собственные неизменяемые классы. Но если я вас правильно читаю, вы согласны с тем, что создание объектов неизменяемыми в objective-c стоит затраченных усилий, если только это явно не имеет смысла.
3. Я говорю, что неизменяемость часто упрощает код, и эта простота имеет много преимуществ, одним из которых может быть производительность. Бывают случаи, когда неизменяемость может значительно снизить производительность, и вам следует оптимизировать работу с различными структурами данных, когда это происходит, и, конечно, когда природа объекта зависит от состояния. Так что да, создание объектов, особенно объектов значений, неизменяемыми, как правило, стоит затраченных усилий.
Ответ №3:
Как и в любом другом императивном языке: это зависит. Я видел приличный прирост производительности кода, когда мы используем неизменяемые объекты, но они также обычно являются редко изменяемыми объектами, которые считываются из архива или устанавливаются пользователем, а затем передаются всем различным битам кода. Мне кажется, не стоит делать это для всего вашего кода, по крайней мере, не для меня, если только вы не планируете активно использовать многопроцессорность и не понимаете компромиссы, которые вы делаете.
Ответ №4:
Я думаю, что большая проблема неизменяемости заключается в том, что если вы сделали хороший дизайн, чтобы ваши данные были помечены как неизменяемые, когда это так, и изменяемые, когда это так, то будет намного проще воспользоваться преимуществами таких вещей, как Grand Central Dispatch и другое распараллеливание, где вы могли бы реализовать гораздо больший потенциальный выигрыш.
В качестве дополнительного замечания при переходе с Java на Objective C первый совет, который я могу вам дать, — отказаться от понятия public и private.