В практике начинающих разработчиков 1С часто встречается ситуация: при заполнении массива структурами в цикле обнаруживается, что все элементы массива в итоге содержат данные только из последней итерации. Кажется, что записи «затираются», хотя код написан по логике добавления. Разберем подробно, почему так происходит, как работает память в 1С и как правильно организовать сбор данных.
Для начала проанализируем, как 1С работает с объектами типа Структура, Массив, ТаблицаЗначений или Соответствие. Все эти объекты относятся к ссылочным типам. Это означает, что в переменной хранится не само содержимое объекта, а адрес (ссылка) на участок оперативной памяти, где этот объект располагается.
Рассмотрим ошибочный алгоритм, который часто используют новички:
Запись = Новый Структура до начала цикла.Запись.Вставить(...).История.Добавить(Запись).В этот момент происходит критическая ошибка понимания: метод Добавить() не копирует данные структуры в массив. Он добавляет в массив ссылку на ту же самую область памяти, в которой лежит объект Запись. Если цикл совершит 10 итераций, в массиве будет лежать 10 одинаковых ссылок, указывающих на один и тот же объект. Естественно, при каждом изменении свойств структуры в следующей итерации, данные «меняются» во всех элементах массива сразу, так как все они смотрят в одну и ту же «коробку».
Выясним причину неэффективности метода Очистить(). В обсуждении предлагалось использовать Запись.Очистить() перед заполнением. Это не поможет, так как метод просто удаляет ключи из существующего объекта, но сам адрес объекта в памяти остается неизменным. Все элементы массива по-прежнему будут ссылаться на этот обновляемый адрес.
Единственный верный способ — это создание нового экземпляра объекта на каждой итерации цикла. Рассмотрим исправленный программный код:
История = Новый Массив;
Для каждого СтрРасх Из Объект.Расходы Цикл
Если Строка(СтрРасх.СтатьяРасходаИмущество) = "Бензин АИ-92" Тогда
// КЛЮЧЕВОЙ МОМЕНТ: Создаем новый объект в памяти
Запись = Новый Структура;
Запись.Вставить("Дата", Объект.Дата);
Запись.Вставить("Имущество", СтрРасх.РасходыРеквизитАналитики_36);
Запись.Вставить("Статья", СтрРасх.СтатьяРасходаИмущество);
Запись.Вставить("Пробег", СтрРасх.РасходыРеквизитАналитики_71);
Запись.Вставить("Цена", СтрРасх.Цена);
Запись.Вставить("Количество", СтрРасх.Количество);
// Теперь в массив добавляется ссылка на НОВЫЙ участок памяти
История.Добавить(Запись);
КонецЕсли;
КонецЦикла;
Таким образом, на каждом шаге цикла операционная система выделяет новую ячейку памяти для структуры. Массив в итоге будет состоять из уникальных ссылок на разные объекты.
Проанализируем ситуацию с производительностью, затронутую в обсуждении. Использование конструкции Строка(СтрРасх.СтатьяРасходаИмущество) внутри цикла на стороне клиента — это серьезный «антипаттерн» в разработке 1С.
Если СтатьяРасходаИмущество является ссылкой на справочник, то при попытке преобразовать её в строку на клиенте, платформа вынуждена неявно обратиться к серверу (и к базе данных), чтобы получить строковое представление этого объекта. Если в таблице 100 строк, ваша программа сделает 100 микро-запросов к серверу. На слабом канале связи это вызовет значительные задержки («фризы») интерфейса.
Рекомендация: Все манипуляции с поиском и обработкой строк данных следует переносить на сервер. В идеале — передать таблицу на сервер один раз, обработать её и вернуть массив структур обратно на клиент.
Сравнение Строка(...) = "Бензин АИ-92" крайне ненадежно. Посмотрим на причины:
Правильнее будет сравнивать ссылку со ссылкой. Для этого можно использовать предопределенные элементы или заранее найти нужную ссылку на сервере и передать её в качестве параметра.
Когда массив структур успешно сформирован, следующим шагом будет его запись в базу данных. Поскольку автор планирует записывать данные в Регистр Сведений, разберем два подхода:
РегистрыСведений.ВашРегистр.СоздатьНаборЗаписей().Записать(), который одним пакетом обновляет данные в БД.Важно помнить, что любая запись в базу данных возможна только на сервере. Массив структур является универсальным типом, поэтому он прекрасно передается с клиента на сервер в качестве параметра процедуры.
Для исправления «затирания» данных в массиве всегда инициализируйте новую структуру внутри цикла с помощью оператора Новый. Помните о разнице между клиентом и сервером: старайтесь минимизировать обращения к серверу из циклов и избегайте преобразования ссылок в строки для логических сравнений. Соблюдение этих правил сделает ваш код не только рабочим, но и производительным.