Экземпляр объекта в 1С: от создания в памяти до записи в базу
Содержание
Экземпляр объекта в 1С — это конкретное значение какого-либо типа, которое существует в оперативной памяти во время работы программы. Для встроенных типов его создают оператором Новый (например, Новый Структура), для прикладных объектов — методом менеджера, таким как СоздатьЭлемент(), либо получают из ссылки через ПолучитьОбъект().
Проще говоря, тип или объект метаданных — это описание, чертёж. А экземпляр — отдельная «вещь», сделанная по этому чертежу и занимающая место в памяти. Справочник Номенклатура один, а экземпляров-элементов у него тысячи: болт, гайка, шайба.
Ниже разберём, чем экземпляр отличается от типа и от объекта метаданных, какими способами он создаётся, почему одна и та же номенклатура бывает то ссылкой, то объектом, и как всё это влияет на память и ошибки в коде.
Что скрывается за словом «экземпляр»
В обычном коде вы постоянно создаёте экземпляры, даже не называя их так. Каждая строка с оператором Новый порождает новый экземпляр:
Параметры = Новый Структура;
Параметры.Вставить("Организация", ВыбраннаяОрганизация);
ТоварыНаСкладе = Новый ТаблицаЗначений;
Запрос = Новый Запрос;
Здесь Параметры, ТоварыНаСкладе и Запрос — три разных экземпляра трёх разных типов. У каждого своё содержимое и свой участок памяти. Если следом написать ВторыеПараметры = Новый Структура, появится ещё один, независимый экземпляр Структуры — заполнять и менять его можно отдельно от первого.
Важная деталь: присваивание само по себе новый экземпляр не создаёт.
А = Новый Массив; // создан экземпляр массива
Б = А; // Б указывает на ТОТ ЖЕ экземпляр
Б.Добавить("строка");
Сообщить(А.Количество()); // выведет 1 — массив один на двоих
Переменные А и Б ссылаются на один объект в памяти. Это поведение называют ссылочной семантикой, и для большинства объектных типов 1С оно работает именно так. Примитивные значения (число, строка, булево, дата) ведут себя иначе — они копируются при присваивании.
Чем экземпляр отличается от типа и объекта метаданных
Три понятия легко путаются, поэтому разложим их по полочкам.
- 🧩 Тип — это вид значения:
Строка,Число,Массив,СправочникСсылка.Контрагенты. Тип отвечает на вопрос «что это вообще за данные и какие у них свойства и методы». - 📐 Объект метаданных — элемент дерева конфигурации: конкретный справочник, документ, регистр. Его вы видите в Конфигураторе слева в дереве. Объект метаданных описывает структуру: реквизиты, табличные части, формы.
- 📦 Экземпляр — одно конкретное значение этого типа в памяти в момент выполнения кода.
Аналогия из жизни. Объект метаданных «Справочник Номенклатура» — это бланк карточки товара с полями «Наименование», «Единица», «Ставка НДС». Тип СправочникОбъект.Номенклатура — описание того, как с заполненной карточкой работать. А экземпляр — конкретная заполненная карточка «Болт М6», которую вы держите в руках.
Отсюда простое правило: объект метаданных в конфигурации всегда один, а экземпляров по нему во время работы может быть сколько угодно — ноль, один или сотни тысяч.
Для встроенных типов (Структура, Соответствие, Запрос) объекта метаданных нет вообще — их описывает сама платформа. Но экземпляры у них такие же: Новый Соответствие — это экземпляр.
Как создаётся экземпляр объекта в 1С
Способов несколько, и выбор зависит от того, что именно вы создаёте.
1. Оператор Новый — для встроенных типов. Самый частый случай. Подходит для коллекций, запросов, построителей:
Список = Новый СписокЗначений;
Соответствие = Новый Соответствие;
ДатаДокумента = Новый Структура("Начало, Конец");
2. Динамическое создание через Новый(Тип(...)). Когда имя типа известно только в момент выполнения — например, пришло строкой из настройки:
ИмяТипа = "Массив";
Коллекция = Новый(Тип(ИмяТипа));
3. Методы менеджера — для прикладных объектов. Новый элемент справочника, документ или группу создаёт соответствующий менеджер:
НовыйТовар = Справочники.Номенклатура.СоздатьЭлемент();
НовыйТовар.Наименование = "Болт М6";
НовыйТовар.Записать();
НовыйЗаказ = Документы.ЗаказПокупателя.СоздатьДокумент();
До вызова Записать() экземпляр живёт только в памяти — в базе данных его ещё нет. Проверить это можно методом ЭтоНовый():
Если НовыйТовар.ЭтоНовый() Тогда
// элемент ещё не сохранён
КонецЕсли;
4. Получение объекта из ссылки — ПолучитьОбъект(). Когда элемент уже есть в базе и вы хотите его изменить:
ОбъектТовара = СсылкаНаТовар.ПолучитьОбъект();
ОбъектТовара.Комментарий = "Проверено";
ОбъектТовара.Записать();
Этот способ особый, и о нём — отдельный разговор ниже.
Ссылка и объект: два лица одного элемента
Это ключевое место, на котором спотыкаются почти все, кто приходит в 1С из других языков. Один и тот же элемент справочника существует в двух разных представлениях.
- 🔗 Ссылка (
СправочникСсылка.Номенклатура) — лёгкий неизменяемый указатель на запись в базе. По ссылке можно прочитать реквизиты, подставить её в другой документ, сравнить с другой ссылкой. Но изменить данные через ссылку нельзя. - 📝 Объект (
СправочникОбъект.Номенклатура) — «тяжёлое» представление. Это полностью загруженный в память экземпляр со всеми реквизитами и табличными частями. Его можно менять и записывать.
Ссылка превращается в объект вызовом ПолучитьОбъект(). Обратно — через свойство Ссылка объекта.
// есть ссылка, нужно поменять поле
Объект = Ссылка.ПолучитьОбъект(); // загрузили тяжёлый экземпляр
Объект.Комментарий = "Изменено";
Объект.Записать(); // сохранили в базу
Почему так сделано. Ссылок в типовой базе миллионы, они мелькают в каждом документе и отчёте. Держать для каждой полный объект в памяти было бы расточительно. Поэтому для чтения и связей используется лёгкая ссылка, а полный экземпляр поднимается в память только тогда, когда его действительно нужно изменить.
Попытка присвоить значение реквизиту прямо через ссылку (Ссылка.Комментарий = "...") приведёт к ошибке: у ссылки нет изменяемых реквизитов. Сначала ПолучитьОбъект(), потом изменение.
Когда что брать — короткая подсказка:
Жизненный цикл экземпляра: от создания до выгрузки
Экземпляр проходит несколько стадий, и понимание этих стадий убирает большинство «мистических» ошибок.
- Создание. В памяти выделяется место, вызывается оператор
Новыйили метод менеджера. Экземпляр готов к работе. - Заполнение и использование. Вы присваиваете реквизиты, вызываете методы, передаёте экземпляр в процедуры. Всё это время он живёт в памяти.
- Фиксация (для прикладных объектов). Вызов
Записать()переносит состояние экземпляра в базу данных. Без этого вызова все изменения останутся только в памяти и пропадут. - Освобождение. Когда на экземпляр не остаётся ни одной ссылки, платформа убирает его из памяти.
Последний пункт стоит пояснить. Память под объекты в 1С освобождается автоматически, по подсчёту ссылок: как только последняя переменная, указывавшая на экземпляр, вышла из области видимости или получила другое значение, объект уничтожается.
Процедура Посчитать()
Запрос = Новый Запрос; // экземпляр создан
// ... работа с запросом ...
КонецПроцедуры // процедура завершилась — Запрос освобождён
В длинной процедуре, которая обрабатывает большие данные, бывает полезно освободить тяжёлый экземпляр раньше, не дожидаясь конца:
БольшаяТаблица = Выгрузка();
// ... использовали таблицу ...
БольшаяТаблица = Неопределено; // память освободится сразу
Циклические ссылки (объект А держит объект Б, а Б держит А) подсчёт ссылок распутывает хуже. Если в длительном фоновом задании память растёт, проверьте, не замкнулись ли ваши структуры сами на себя.
ООП в 1С: что от него есть, а чего нет
Раз речь зашла об экземплярах и типах, логично спросить: 1С — это объектно-ориентированный язык? Ответ — «частично», и важно понимать границы.
Что есть:
- ✅ Инкапсуляция. У прикладных объектов есть свойства и методы. Поведение элемента справочника описывается в его модуле объекта — обработчики
ПередЗаписью,ПриЗаписи,ОбработкаПроведения. Данные и логика лежат рядом. - ✅ Экземпляры по типу. Как мы и разбираем: тип один, экземпляров много.
Чего нет:
- ❌ Пользовательских классов. Вы не можете во встроенном языке объявить свой класс со своими полями и методами, как в C# или Python. Набор типов задаёт платформа и дерево метаданных.
- ❌ Классического наследования. Один справочник не наследует реквизиты и методы другого. Ближайший по духу механизм — расширения конфигурации (появились в платформе 8.3.6): они позволяют добавлять свойства и переопределять поведение существующих объектов, не меняя исходную конфигурацию. Но это не наследование классов в привычном смысле.
- ❌ Полиморфизма через иерархию классов. Общий код для разных объектов обычно пишут в общих модулях и передают туда ссылки, а не строят цепочки классов.
Поэтому точнее называть встроенный язык 1С не объектно-ориентированным, а объектным: вы работаете с готовыми объектами платформы, но не проектируете собственную систему классов. Термин «экземпляр» здесь означает ровно то же, что и в ООП, — конкретное значение типа, — а вот «класс» заменяется на «тип» и «объект метаданных».
Типичные ошибки при работе с экземплярами
Собрал ситуации, которые чаще всего всплывают в логах и на этапе отладки.
«Значение не является значением объектного типа
». Самая частая. Возникает, когда вы обращаетесь к свойству или методу у значения, у которого их нет, — у числа, строки или у Неопределено.
Данные = НайтиЗначение(); // вернуло Неопределено
Сообщить(Данные.Наименование); // ошибка: у Неопределено нет реквизитов
Лечится проверкой перед обращением: Если Данные <> Неопределено Тогда ....
Изменение через ссылку вместо объекта. Попытка Ссылка.Реквизит = Значение не компилируется или падает — ссылка неизменяема. Нужно ПолучитьОбъект().
Забыли Записать(). Изменили реквизиты объекта, но не вызвали запись. В памяти всё поменялось, а в базе — нет. После перезапуска изменений не видно, потому что их там и не было.
Блокировка объекта. Если два сеанса одновременно получили один объект через ПолучитьОбъект() и пытаются его записать, второй получит сообщение о том, что запись заблокирована другим пользователем. Это штатное поведение, а не сбой: объектная блокировка защищает данные от затирания.
Лишние объекты в цикле. Поднимать ПолучитьОбъект() для каждого элемента ради чтения пары полей — частая причина тормозов. Если менять данные не нужно, читайте запросом.
Как не перегружать память экземплярами
Экземпляры прикладных объектов тяжёлые: вместе с ними в память загружаются все реквизиты и табличные части. На больших объёмах это заметно.
- 📉 Читайте запросом, а не объектом. Если нужны наименование и артикул тысячи товаров, не вызывайте
ПолучитьОбъект()в цикле. Один запрос вернёт нужные поля в разы быстрее и без загрузки полных экземпляров. - 🧹 Освобождайте тяжёлые экземпляры. Большую таблицу значений или выгрузку после использования присвойте
Неопределено, если до конца процедуры ещё далеко. - 🔁 Не создавайте одно и то же в цикле. Экземпляр
ЗапросаилиСтруктуры-шаблона, который не меняется от итерации к итерации, создавайте один раз до цикла. - 📦 Обрабатывайте порциями. Если нужно перебрать и изменить десятки тысяч элементов, делите на пакеты и записывайте по частям, а не держите всё в памяти сразу.
Правило простое: объект поднимаем в память только тогда, когда собираемся его менять. Для всего остального хватает ссылки и запроса.
Частые вопросы
«Экземпляр объекта» и «объект» — это одно и то же?
Почему при присваивании двух переменных меняются обе?
Скопировать()), а не простое присваивание.Как узнать, записан ли уже элемент в базу?
ЭтоНовый(). Он возвращает Истина, пока экземпляр создан в памяти, но ещё не сохранён вызовом Записать().Чем отличается `Новый` от `СоздатьЭлемент()`?
Новый создаёт экземпляр встроенного типа (коллекцию, запрос). СоздатьЭлемент() — метод менеджера справочника, он создаёт новый экземпляр прикладного объекта, готовый к заполнению и записи в базу.