Как я могу делать такие вещи, как Clojure, при использовании неизменяемых коллекций в Objective-C?

Я немного пристрастился к использованию неизменяемых коллекций (в основном в Clojure, который называет их «постоянными структурами данных»), и мне бы хотелось иметь возможность программировать таким образом в некоторых контекстах на iOS и OS X.

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

Да, вы можете сделать это сейчас, используя неизменяемые экземпляры NSArray и NSDictionary, но становится все более неэффективным копировать их для создания «измененных» версий, поскольку у вас все больше и больше коллекций и/или вы часто вносите изменения: небольшое изменение в большие данные тогда структура требует непропорционально большого объема работы.

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


person Matthew Phillips    schedule 13.08.2013    source источник
comment
Дайте определение неэффективным; пока вы придерживаетесь списков свойств и обращаете внимание на предупреждения компилятора, вы можете эмулировать большую часть любого из доступных постоянных шаблонов. Очевидно, что потребуется настоящая поддержка во время выполнения, чтобы выполнить полный танец ошибок/копирования-при-мутации. т.е. голосование закрыть связано с отсутствием конкретных примеров в вашем вопросе.   -  person bbum    schedule 13.08.2013
comment
Я расширил вопрос и добавил некоторые особенности. Но обратите внимание, что я не имею в виду постоянные, такие как устойчивые, сохраняемые на диск и т. д., поэтому поддержка во время выполнения не требуется.   -  person Matthew Phillips    schedule 13.08.2013
comment
Я еще раз переформулировал вопрос. Я считаю, что это тема для SO, и любые ответы будут полезны его пользователям. Я не смог найти никаких советов по этому поводу после довольно небольшого поиска.   -  person Matthew Phillips    schedule 14.08.2013
comment
Похоже, вы можете использовать объектные контроллеры. Посмотрите на NSArrayController   -  person uchuugaka    schedule 14.08.2013
comment
Однако, согласно комментарию bbum, определите неэффективность. Например. копирование словаря не приводит к копированию каких-либо значений, копирование массива не приводит к копированию каких-либо содержащихся в нем объектов и т. д. Копии, как правило, неглубокие.   -  person Tommy    schedule 14.08.2013
comment
Будьте осторожны, попытка втиснуть один метод кодирования в другой метод часто может привести к разочарованию, паническим атакам, депрессии, увеличению веса и особенно выпадению волос.   -  person Ralph Caraveo    schedule 14.08.2013
comment
uchuugaka: спасибо, но пример уведомления о событии, который я привел, был не столько об аспекте уведомления, сколько о возможности видеть старые и новые значения. NSArrayController (и ему подобные) предназначены для генерации событий об изменяемом объекте, который уже был изменен.   -  person Matthew Phillips    schedule 14.08.2013
comment
Томми: Я говорю о вложенных деревьях объектов, то есть вложенных NSArray и NSDictionary. Листья могут быть общими, а ветви — нет, поэтому изменение значения одного листа означает выполнение непропорционально большого количества копий. Это можно сделать (я делаю это), но это влияет на накладные расходы на выделение памяти, которые не обязательно должны быть.   -  person Matthew Phillips    schedule 14.08.2013
comment
Ральф: Я понимаю и полностью согласен. Вы не найдете никого более ленивого и более готового идти за зерном, чем я :) Я пришел к этому только после того, как попробовал обычные подходы. И это действительно больше похоже на другой алгоритм, чем на новый способ кодирования, если это имеет смысл.   -  person Matthew Phillips    schedule 14.08.2013
comment
Я не знаю ничего, что эмулировало бы типичные структуры данных функционального языка в ObjC. Если скорость мутации низкая, вы иногда можете получить достойные свойства совместного использования/потокобезопасности с копированием изменяемого, а затем заморозить подход или прокси-объекты с копированием при записи.   -  person Catfish_Man    schedule 14.08.2013
comment
Catfish_Man: не могли бы вы уточнить, что включает в себя изменяемое копирование, а затем замораживание? Оба они звучат так, как будто они обеспечивают параллелизм, но все же требуют много накладных расходов на копирование.   -  person Matthew Phillips    schedule 14.08.2013
comment
Я думаю, вам нужно написать свою собственную библиотеку с нуля, написав примитивные функциональные контейнеры, такие как Finger Tree. или Zipper. Известно, что хэш-таблица сложнее, чем последовательный массив.   -  person eonil    schedule 08.09.2013
comment
Я считаю, что NSArray частично использует эти концепции под капотом, но ничто не гарантируется.   -  person eonil    schedule 08.09.2013
comment
Кроме того, если вам нужна производительность параллельного выполнения, рассмотрите возможность написания низкоуровневых примитивов на C/C++ с более высокоуровневыми оболочками Objective-C. Насколько мне известно, среда выполнения Objective-C выполняет атомарные операции для управления подсчетом ссылок, что может несколько снизить производительность параллельного выполнения.   -  person eonil    schedule 08.09.2013
comment
Вы смотрели на mutableArrayValueForKey:, mutableSetValueForKey: и подобные методы? Я не совсем уверен, что это то, что вы ищете, но они предоставляют необходимую вам функциональность (по крайней мере, для массивов и наборов).   -  person Ian    schedule 07.10.2013
comment
@Ian: спасибо, но эти методы изменяют объект на месте. Мне нужны (гипотетические) методы, которые возвращают новый объект с изменениями и делают это эффективно.   -  person Matthew Phillips    schedule 09.10.2013
comment
Понимаю. Не уверен насчет влияния copy, но уверен, что это не так уж неэффективно.   -  person Ian    schedule 09.10.2013
comment
@Ian: copy, вероятно, довольно эффективен, но мне нужно использовать mutableCopy, который, вероятно, действительно копирует все данные.   -  person Matthew Phillips    schedule 15.10.2013
comment
@MatthewPhillips На самом деле это не так. Взгляните на этот фрагмент. Вы заметите, что адрес для номера не меняется. Словари не копируют данные, они просто сохраняют их.   -  person Ian    schedule 15.10.2013
comment
@ian: спасибо, что указали на это. Я имел в виду, что вложенные массивы и словари должны быть скопированы. Оказывается, mutableCopy не создает изменяемые копии вложенных массивов и словарей, поэтому мне нужен CFPropertyListCreateDeepCopy. Смотрите также мои комментарии к ответу tjklemz ниже.   -  person Matthew Phillips    schedule 22.10.2013


Ответы (2)


Я не думаю, что здесь есть короткий путь.

Как вы понимаете, постоянные структуры данных Clojure сильно отличаются от неизменяемых классов коллекций, которые предоставляет Cocoa.

Если вы хотите использовать постоянные структуры данных Clojure из Obj-C, единственный способ сделать это — повторно реализовать их в Objective-C. Насколько я понимаю, многие из них описаны в книге Окасаки, Чисто Функциональные структуры данных и в статьях Фила Бэгвелл.

В этом другом ответе есть несколько ссылок: Какая структура данных лежит в основе наборов Clojure? .

person algal    schedule 31.12.2013
comment
Я выбираю это как ответ, так как кажется, что мне действительно придется портировать постоянные структуры данных в ObjC. (Будет интересно посмотреть, как это относится к Swift, который, похоже, имеет какой-то механизм копирования при записи для своих словарей и массивов). - person Matthew Phillips; 12.06.2014

Пожалуйста, ознакомьтесь с этой статьей на сайте Ridiculous Fish (написанной, по-моему, Кори Дорасом, инженером команды AppKit, а также создателем оболочки Fish):

Массив: наши массивы нет. http://ridiculousfish.com/blog/posts/array.html

Вы уже ответили на свой вопрос:

Да, вы можете сделать это сейчас, используя неизменяемые экземпляры NSArray и NSDictionary...

Прелесть инфраструктуры Cocoa заключается в ее простоте, особенно в отношении структур данных. Идея состоит в том, что закулисный код должен определять, как реализовать структуру, а не вы. На практике вам понадобятся только два «типа» структур данных: массивы и словари (или карты, если вы пришли из других языков). Конечно, вам нужно много «типов» реализаций, но на самом деле вам нужны только два способа доступа к вашим данным; если вам нужно больше способов, тогда в игру вступают пользовательские классы и композиция.

Что касается вашего беспокойства об эффективности: не беспокойтесь об этом. Статья Кори (Ridiculous Fish) показывает, что внутри Apple уже выполнила ваши требования по эффективности. В конце концов, это просто указатели, как указал Ян Мюррей в комментариях: все ссылки подсчитываются и копируются только при необходимости. Наиболее вероятно, что когда вы «копируете» или «mutableCopy» NSArray или NSDictionary, базовые данные фактически не копируются. Чтобы узнать, как это можно реализовать, см. статью Роба Пайка о языке Go здесь: http://blog.golang.org/slices. Я почти уверен, что Cocoa следует тому же принципу, возможно, даже в большей степени.

Кроме того, с появлением «блоков» Objective-C становится все более и более возможным программировать в функциональном стиле а-ля варианты LISP (такие как Clojure). На самом деле, я очень рекомендую это и призываю вас продолжать этот путь. Это может привести к гораздо более стабильному и чистому коду, если все сделано правильно.

person tjklemz    schedule 15.10.2013
comment
Тем не менее, я не думаю, что сообщаю то, что мне нужно: в блоге, на который вы ссылаетесь, делается вывод о том, что изменение массивов на месте можно реализовать умными способами, а не предполагать что-либо. Я имею в виду создание модифицированных копий словарей и массивов, оставляя оригиналы нетронутыми. Насколько мне известно, единственный способ сделать это сейчас — использовать CFPropertyListCreateDeepCopy () для создания изменяемой копии, а затем изменить ее. Возможно, фрагмент на странице pastebin.com/qgsBETSu поможет вам лучше понять, что я имею в виду. - person Matthew Phillips; 22.10.2013