Objective-C

Поделись знанием:
Перейти к: навигация, поиск
Objective-C
Класс языка:

объектно-ориентированный, мультипарадигмальный: рефлексивно-ориентированный

Появился в:

1983

Автор:

Бред Кокс

Система типов:

нестрогая, статическая / динамическая

Основные реализации:

Cocoa, Cocoa Touch, gcc, LLVM + Clang

Испытал влияние:

Smalltalk, C

Повлиял на:

Java, Objective-J, Swift

Objective-C — компилируемый объектно-ориентированный язык программирования, используемый корпорацией Apple, построенный на основе языка Си и парадигм Smalltalk. В частности, объектная модель построена в стиле Smalltalk — то есть объектам посылаются сообщения.

Язык Objective-C является надмножеством языка Си, поэтому Си-код полностью понятен компилятору Objective-C.

Компилятор Objective-C входит в GCC и доступен на большинстве основных платформ. Язык используется в первую очередь для Mac OS X (Cocoa) и GNUstep — реализаций объектно-ориентированного интерфейса OpenStep. Также язык используется для iOS (Cocoa Touch).





История

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

Многие[кто?] увидели в объектно-ориентированном программировании потенциальное решение возникшей проблемы. С одной стороны, Smalltalk использовали почти все более-менее сложные системы. С другой — использование виртуальных машин повышало требования к ресурсам.

Objective-C был создан Брэдом Коксом в начале 1980-х в его компании Stepstone. Он пытался решить проблему повторного использования кода.

Целью Кокса было создание языка, поддерживающего концепцию software IC, подразумевающей возможность собирать программы из готовых компонентов (объектов), подобно тому как сложные электронные устройства могут быть собраны из набора готовых интегральных микросхем.

При этом язык должен быть простым и основанным на языке С, чтобы облегчить переход разработчиков на него.

Одной из целей было также создание модели, в которой сами классы являются полноценными объектами, поддерживалась бы интроспекция и динамическая обработка сообщений.

Objective-C является расширением С: любая программа на С является программой на Objective-C.

Одной из отличительных черт Objective-C является динамичность: решения, обычно принимаемые на этапе компиляции, здесь откладываются до этапа выполнения.

Objective-C — message-oriented-язык, в то время как C++ — function-oriented: в Objective-C вызовы метода интерпретируются не как вызов функции (хотя к этому обычно все сводится), а как посылка сообщения (с именем и аргументами) объекту, подобно тому, как это происходит в Smalltalk.

Любому объекту можно послать любое сообщение. Объект может вместо обработки сообщения переслать его другому объекту для обработки (делегирование), в частности, так можно реализовать распределённые (то есть находящиеся в различных адресных пространствах и даже на разных компьютерах) объекты.

Привязка сообщения к соответствующей функции происходит на этапе выполнения.

Язык Objective-C поддерживает работу с метаинформацией — так, на этапе выполнения можно узнать класс объекта, список его методов (с типами передаваемых аргументов) и instance-переменных, проверить, является ли класс потомком заданного и поддерживает ли он заданный протокол и т. п.

В языке есть поддержка протоколов (понятия интерфейса объекта и протокола четко разделены). Поддерживается наследование (не множественное); для протоколов поддерживается множественное наследование. Объект может быть унаследован от другого объекта и сразу нескольких протоколов (хотя это скорее не наследование протокола, а его поддержка).

На данный момент язык Objective-C поддерживается компиляторами Clang и GCC (под управлением Windows используется в составе MinGW или cygwin).

Некоторые функции языка перенесены в runtime-библиотеку и сильно зависят от неё. Вместе с компилятором gcc поставляется минимальный вариант такой библиотеки. Также можно свободно скачать runtime-библиотеку компании Apple: Apple’s Objective-C runtime.

Эти две runtime-библиотеки похожи (основные отличия в именах методов). Далее примеры будут ориентироваться на runtime-библиотеку Apple.

Синтаксис языка

В языке Objective-C для обозначения объектов используется специальный тип id (это аналог типа Object в Java). Переменная типа id фактически является указателем на произвольный объект. Для обозначения нулевого указателя на объект используется константа nil (= NULL).

При этом вместо id можно использовать и более привычное обозначение с явным указанием класса. В частности последнее позволяет компилятору осуществлять некоторую проверку поддержки сообщения объектами — если компилятор из типа переменной не может сделать вывод о поддержке объектом данного сообщения, то он выдаст предупреждение.

Тем самым язык поддерживает проверку типов, но в нестрогой форме (то есть найденные несоответствия возвращаются как предупреждения, а не ошибки).

Для посылки сообщений используется следующий синтаксис:

   [receiver message];

В этой конструкции receiver является указателем на объект, а message — именем метода.

В отличие от языка C++, посылка сообщения nil’у является законной операцией, всегда возвращающей нулевое значение (nil).

Сообщение может также содержать параметры:

   [myRect setOrigin:30.0 :50.0];

В этом примере именем метода (сообщения) является setOrigin::. Обратите внимание, что каждому передаваемому аргументу соответствует ровно одно двоеточие. При этом в приведенном примере первый аргумент имеет метку (текст перед двоеточием), а второй — нет.

Язык Objective-C позволяет снабжать метками каждый аргумент, что заметно повышает читаемость кода и снижает вероятность передачи неправильного параметра. Именно такой стиль принят большинством разработчиков.

   [myRect setWidth:10.0 height:20.0];

В этом примере в качестве имени сообщения выступает setWidth: height:.

Также поддерживается возможность передачи произвольного количества аргументов в сообщении:

   [myObject makeGroup: obj1, obj2, obj3, obj4, nil];

Как и функции, сообщения могут возвращать значения, при этом в отличие от языка С, типом значения, возвращаемым по умолчанию, является id.

   float area = [myRect area];

Результат одного сообщения можно сразу же использовать в другом сообщении:

   [myRect setColor:[otherRect color]];

Как уже говорилось, в Objective-C классы сами являются объектами. Основной задачей таких объектов (называемых class objects) является создание экземпляров данного класса (это очень похоже на паттерн Abstract Factory).

При этом само имя класса играет двойную роль — с одной стороны оно выступает как тип данных (то есть он может быть использован для описания указателей на объекты данного класса). А с другой стороны имя класса может выступать в качестве объекта, которому посылается сообщение (в сообщениях имя класса может принимать участие только как receiver).

   Rect * myRect = [[Rect alloc] init];

В языке Objective-C нет встроенного типа для булевских величин, поэтому обычно такой тип вводится искусственно. Далее для логических величин будет использоваться тип BOOL с возможными значениями YES и NO (как это делается в операционных системах NextStep, Mac OS X).

Первым достаточно серьёзным применением языка Objective-C было его использование в операционной системе NextStep. Для этой системы было написано большое количество различных классов на Objective-C, многие из которых до сих пор используются в Mac OS X.

Имена всех этих классов начинаются с префикса NS, обозначающего свою принадлежность к операционной системе NextStep. Сейчас они входят в библиотеку Foundation, на которой строятся приложения для OS X и iOS.

С одним из них — NSString — мы столкнемся в данной статье. Этот класс служит для работы со строками (при этом в качестве внутреннего представления символов используется Юникод).

Компилятор поддерживает данный тип, автоматически переводя конструкции вида @"my string" в указатель на объект класса NSString, содержащий данную строку (точнее его подкласса, соответствующего константным строкам).

Свойства

Допустим, в классе Company существует instance-переменная name.

@interface Company : NSObject
{
     NSString *name;
}

Для доступа к ней извне лучше всего воспользоваться свойствами, которые появились в Objective-C 2.0. Для объявления свойств используется ключевое слово @property.

@property (retain) NSString *name;

В скобках перечисляются атрибуты доступа к instance-переменной. Атрибуты разделяются на 3 основные группы.

Имена акцессора и мутатора

  • getter=getterName, используется для задания имени функции, используемой для извлечения значения instance-переменной.
  • setter=setterName, используется для задания имени функции, используемой для установки значения instance-переменной.

Ограничение чтения/записи

  • readwrite — у свойства есть как акцессор, так и мутатор. Является атрибутом по умолчанию.
  • readonly — у свойства есть только акцессор.

Эти атрибуты взаимоисключают друг друга. И последняя группа атрибуты мутатора.

  • assign — для задания нового значения используется оператор присваивания. Используется только для POD-типов либо для объектов, которыми мы не владеем.
  • retain — указывает на то, что для объекта, используемого в качестве нового значения instance-переменной, управление памятью происходит вручную (не забываем потом освободить память).
  • copy — указывает на то, что для присваивания будет использована копия переданного объекта.
  • weak — аналог assign при применении режима автоматического подсчёта ссылок. (ARC должен поддерживаться компилятором)
  • strong — аналог retain при применении режима автоматического подсчёта ссылок. (ARC должен поддерживаться компилятором)

При работе под GC никакой разницы между использованием assign, retain, copy нет. Для создания кода свойств, в соответствии с тем, как они описаны в объявлении, можно воспользоваться автогенерацией кода:

@synthesize name;

Автоматически созданный код — не всегда подходящее решение и может потребоваться создание методов доступа к instance-переменным вручную.

Язык часто критикуют за перегруженный, по сравнению с другими языками, синтаксис. Однако при этом нередко отмечается его более высокая читабельность.

Создание новых классов

Все ключевые слова языка Objective-C, отсутствующие в С, начинаются с символа @.

Как и в C++, описание класса и его реализация разделены (обычно описание помещается в заголовочные файлы с расширением h, а реализации — в файлы с расширением m).

Ниже приводится общая структура описания нового класса:

@interface ClassName : SuperClass
{
    instance variable declarations
}

method declarations
@end

В версии runtime от Apple все классы имеют общего предка — класс NSObject, содержащий целый ряд важных методов.

Описание переменных ничем не отличается от описания переменных в структурах в языке С:

Если у вас не Apple, то скорее всего вместо NSObject вам потребуется Object (#import <objc/Object.h>).

@interface Rect : NSObject
{
	float     width;
	float     height;
	BOOL      isFilled;
	NSColor * color;
}
@end

Описания же методов заметно отличаются от принятых в C++ и очень сильно похожи на описания методов в языке Smalltalk.

Каждое описание начинается со знака плюс или минус. Знак плюс обозначает, что данный метод является методом класса (то есть его можно посылать только class object’у, а не экземплярам данного класса). Фактически методы класса являются аналогами статических методов в классах в языке C++.

Знак минус служит для обозначения методов объектов — экземпляров данного класса. Обратите внимание, что в Objective-C все методы являются виртуальными, то есть могут быть переопределены.

Ниже приводятся описания возможных методов для класса Rect.

@interface Rect : NSObject
{
	float     x, y;
	float     width;
	float     height;
	BOOL      isFilled;
	NSColor * color;
}
+ newRect;
- (void) display;
- (float) width;
- (float) height;
- (float) area;
- (void) setWidth: (float) theWidth;
- (void) setHeight: (float) theHeight;
- (void) setX: (float) theX y: (float) theY;
@end

Обратите внимание, что имя метода может совпадать с именем instance-переменной данного класса (например, width и height).

Тип возвращаемого методом значения указывается в круглых скобках сразу же после знака плюс или минус (но перед названием метода). Если тип не указан, то считается, что возвращается значение типа id.

Далее идет имя метода, где после каждого двоеточия задается тип аргумента (в круглых скобках) и сам аргумент.

Язык Objective-C позволяет для аргументов метода задавать также один из следующих описателей — oneway, in, out, inout, bycopy и byref. Данные описатели служат для задания направления передачи данных и способа передачи. Их наличие заметно упрощает реализацию и работу с распределенными объектами (которые были реализованы в операционной системе NextStep к началу 90-х годов прошлого века).

Метод, принимающий произвольное количество параметров, может быть описан следующим образом:

- makeGroup: (id) object, ...;

Для подключения заголовочного файла в Objective-C вместо директивы #include используется директива #import, аналогичная #include, но гарантирующая, что данный файл будет подключен всего один раз.

В ряде случаев возникает необходимость в объявлении того, что данное имя является именем класса, но без явного его описания (такая необходимость возникает при описании двух классов, каждый из которых ссылается на другой класс).

В этом случае можно воспользоваться директивой @class, объявляющей, что следующие за ней имена являются именами классов.

@class Shape, Rect, Oval;

Реализация методов класса выглядит следующим образом:

#import "ClassName.h"

@implementation ClassName

    method implementations

@end

Ниже приводится пример реализации методов класса Rect, описанного выше.

#import "Rect.h"

@implementation Rect

+ newRect
{
	Rect * rect = [[Rect alloc] init];

	[rect setWidth:  1.0f];
	[rect setHeight: 1.0f];
	[rect setX: 0.0f y: 0.0f];

	return rect;
}

- (float) width
{
   return width;
}

- (float) height
{
   return height;
}

- (float) area
{
   return [self width] * [self height];
}

- (void) setWidth: (float) theWidth
{
   width = theWidth;
}

- (void) setHeight: (float) theHeight
{
   height = theHeight;
}

- (void) setX: (float) theX y: (float) theY
{
   x = theX;
   y = theY;
}

@end

Как видно из примера выше, в методах доступны все instance-переменные. Однако, как и в C++, есть возможность управлять видимостью переменных (видимостью методов управлять нельзя) при помощи директив @private, @protected и @public (действующих полностью аналогично языку C++).

@interface Worker : NSObject
{
	char * name;
@private
	int    age;
	char * evaluation;
@protected
	int    job;
	float  wage;
@public
	id     boss
}

При этом к public переменным класса можно обращаться непосредственно, используя оператор -> (например objPtr -> fieldName).

Как работает механизм сообщений

Компилятор переводит каждую посылку сообщения, то есть конструкцию вида [object msg] в вызов функции objc_msgSend. Эта функция в качестве своего первого параметра принимает указатель на объект-получатель сообщения, в качестве второго параметра выступает т. н. селектор, служащий для идентификации посылаемого сообщения. Если в сообщении присутствуют аргументы, то они также передаются objc_msgSend как третий, четвёртый и т. д. параметры.

Каждый объект Objective-C содержит в себе атрибут isa — указатель на class object для данного объекта. class object автоматически создается компилятором и существует как один экземпляр, на который через isa ссылаются все экземпляры данного класса.

Каждый class object обязательно содержит в себе указатель на class object для родительского класса (superclass) и dispatch table. Последняя представляет собой словарь, сопоставляющий селекторам сообщений фактические адреса реализующих их методов (функций).

Таким образом, функция objc_msgSend ищет метод с данным селектором в dispatch table для данного объекта. Если его там нет, то поиск продолжается в dispatch table для его родительского класса и т. д.

Если метод (то есть соответствующая ему функция) находится, то осуществляется его вызов с передачей всех необходимых аргументов.

В противном случае объекту дается последний шанс обработать сообщение перед вызовом исключения — селектор сообщения вместе с параметрами «заворачивается» в специальный объект типа NSInvocation и объекту посылается сообщение forwardInvocation:, где в качестве параметра выступает объект класса NSInvocation.

Если объект поддерживает forwardInvocation:, то он может либо сам обработать посылаемое сообщение, либо переслать другому объекту для обработки:

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    if ( [someOtherObject respondsToSelector: [anInvocation selector]] )
        [anInvocation invokeWithTarget: someOtherObject];
    else
       ..........
}

Для ускорения поиска сообщений по dispatch table используется кэширование, позволяющее заметно снизить затраты на пересылку сообщений. Также облегчает поиск метода по таблицам использование так называемых селекторов вместо обычных имен. Обычно селектор представляет собой 32-битовую величину, позволяющую однозначно идентифицировать метод.

Тип селектора обозначается как SEL и существует ряд функций и конструкций, позволяющих осуществлять преобразование имени в селектор и обратно.

Так для получения селектора сообщения непосредственно по имени служит конструкция @selector():

   SEL setWidth = @selector(setWidth:);
   SEL setPos   = @selector(setPosition:y:);

Для получения селектора по строке символов (на этапе выполнения) и перевода селектора в строку служат функции NSSelectorFromString и NSStringFromSelector:

   SEL        setWidth   = NSSelectorFromString ( @"setWidth:" );
   NSString * methodName = NSStringFromSelector  ( setPos );

Мощная поддержка метаинформации в Objective-C позволяет прямо на этапе выполнения проверить поддерживает ли объект метод с данным селектором при помощи посылки ему сообщения respondsToSelector::

    if ( [anObject respondsToSelector: @selector(setWidth:)] )
         [anObject setWidth: 200.0];

Довольно легко можно послать сообщение, соответствующее данному селектору (без аргументов, с одним, двумя или тремя аргументами), при помощи метода performSelector:, performSelector: withObject:, performSelector: withObject: withObject:, performSelector: withObject: withObject: withObject: и так далее.

    [myObject performSelector:sel withObject: nil];

Обратите внимание, что методы performSelector: всегда возвращают значение типа id.

Можно получить класс для данного объекта, послав ему сообщение class. Это сообщение возвращает класс в виде указателя на объект типа Class.

    Class    * cls     = [anObject class];
    NSString * clsName = NSStringFromClass ( cls );

С другой стороны также можно легко получить соответствующий class object по имени класса:

    Class * cls = NSClassFromString ( clsName );

Каждый метод фактически представляет собой функцию с двумя невидимыми аргументами — self и _cmd.

Первый является аналогом this, то есть указывает на сам объект — получатель сообщения. Второй — содержит селектор данного метода.

Аргумент self может использоваться для посылки сообщений самому себе, как например в следующем методе:

- (float) area
{
   return [self width] * [self height];
}

Однако кроме self есть ещё одна величина, которой могут посылаться сообщения — super. На самом деле super не является нормальной переменной — это всего лишь ещё одно обозначение для указателя на текущий объект. Но при посылке сообщения super поиск метода начинается не с dispatch table текущего объекта, а с dispatch table родительского объекта.

Таким образом, посылая сообщения super мы тем самым вызываем старые версии методов, переопределенные данным классом.

В языке Objective-C можно по селектору метода получить адрес реализующей его функции (именно как функции языка С).

Такая функция отличается от описания метода только вставкой в начало списка аргументов двух дополнительных параметров — указателя на сам объект (self) и селектора данного метода (_cmd).

Послав объекту сообщение methodForSelector: мы получаем в ответ адрес реализующей этот метод функции.

typedef float (*WidthFunc)( id, SEL );
typedef void  (*SetWidthFunc)( id, SEL, float );

WidthFunc    widthFunc    = (WidthFunc)    [myRect methodForSelector: @selector (width)];
SetWidthFunc setWidthFunc = (SetWidthFunc) [myRect methodForSelector: @selector (setWidth:)];

(*setWidthFunc)( myRect, @selector (setWidth:), 27.5f );

Это позволяет при необходимости многократного вызова одного и того же метода у заданного объекта полностью избежать всех расходов, связанных с пересылкой сообщений.

Протоколы

Язык Objective-C содержит полноценную поддержку протоколов (это аналог интерфейса в Java и абстрактного класса в C++, который также иногда принято называть интерфейсом). Протокол представляет собой просто список описаний методов. Объект реализует протокол, если он содержит реализации всех методов, описанных в протоколе.

Протоколы удобны тем, что позволяют выделять общие черты у разнородных объектов и передавать информацию об объектах заранее неизвестных классов.

Простейшее описание протокола выглядит следующим образом:

@protocol ProtocolName
method declarations
@end

Так протокол Serializable может быть описан следующим образом:

@protocol Serializable
- (id)   initWithCoder: (NSCoder *) coder;
- (void) encodeWithCoder: (NSCoder *) coder;
@end

Протокол может быть унаследован от произвольного количества других протоколов:

@protocol MyProto <Protocol1, Protocol2, Serializable, Drawable>

Точно также можно при описании класса задать не только родительский класс, но и набор протоколов:

@interface MyClass : SuperClass <Protocol1, Protocol2, Serializable, Drawable>

Для проверки во время выполнения программы, поддерживается ли объектом заданный протокол объектов, можно использовать сообщение conformsToProtocol::

if ( [myObject conformsToProtocol: @protocol (Serializable)] )
     [myObject encodeWithCoder: myCoder];

Кроме того, имя протокола (протоколов) можно использовать при описании переменных для явного указания компилятору о поддержке соответствующими объектами протокола (протоколов).

Так, если переменная myObject содержит указатель на объект заранее неизвестного класса, но при этом удовлетворяющий протоколам Serializable и Drawable, то её можно описать следующим образом:

id <Serializable,Drawable>  myObject;

Точно так же, если заранее известно, что myObject будет содержать указатель на объект, унаследованный от класса Shape и поддерживающего протокол Serializable, то эту переменную можно описать следующим образом:

Shape <Serializable>  *myObject;

Обратите внимание, что подобное описание служит только для сообщения компилятору, какие сообщения поддерживает данный объект.

Как и классы, все протоколы в Objective-C представлены при помощи объектов (класса Protocol):

Protocol * myProto = @protocol ( Serializable );

Для предварительного объявления протоколов можно использовать следующую конструкцию:

@protocol MyProto, Serializable, Drawable;

Эта конструкция сообщает компилятору о том, что MyProto, Serializable и Drawable являются именами протоколов, которые будут определены позже.

Обработка исключений

В языке Objective-C поддерживается обработка исключений, очень похожая на используемую в языках C++ и Java.

Для этого служат директивы @try, @catch, @finally и @throw.

 Cup * cup = [[Cup alloc] init];

 @try
 {
     [cup fill];
 }
 @catch ( NSException * exc )
 {
    NSLog ( @"Exception caught:%@", exc );
 }
 @catch ( id exc )
 {
    NSLog ( @"Unknown exception caught" );
 }
 @finally
 {
    [cup release];
 }

Для запуска исключения используется директива @throw, в качестве аргумента берущая указатель на объект-исключение. Обычно в Mac OS X/NextStep для этой цели используются объекты класса NSException.

 NSException * exc = [NSException exceptionWithName: @"my-exception" reason: @"unknown-error"
                                  userInfo: nil];
 @throw exc;

Внутри @catch-блоков директива @throw может использоваться без параметра для повторного запуска обрабатываемого исключения (rethrowing exception).

Синхронизация

Язык Objective-C поддерживает синхронизацию для многопоточных приложений. При помощи директивы @synchronized () можно защитить фрагмент кода от одновременного выполнения сразу несколькими потоками.

@synchronized () берёт на вход указатель на объект языка Objective-C (можно использовать для этой цели любой объект, в том числе и self), который играет роль мьютекса (mutex).

При попытке потока начать выполнение защищенного фрагмента проверяется, выполняется ли уже этот фрагмент каким-либо потоком. Если да, то сравниваются объекты, переданные этими потоками в @synchronized ().

Если эти указатели совпадают, то поток, пытающийся войти в защищенный блок, будет приостановлен (suspended) до тех пор, пока первый поток не выйдет из блока. Тогда выполнение второго потока продолжится, и уже он «запрёт» этот блок для всех остальных потоков.

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

- (void) criticalMethod
{
    @synchronized ( self )
    {
         // perform modifications to shared objects
         . . .
    }
}

В качестве мьютекса (т. е. параметра инструкции @synchronized) рекомендуется указывать объект, недоступный извне, поскольку это может привести к взаимной блокировке, если один и тот же объект используется в качестве мьютекса двумя взаимозависимыми потоками. В частности, не рекомендуется @synchronized(self).

Создание и уничтожение объектов

В самом языке Objective-C нет специальных команд для создания и уничтожения объектов (подобных new и delete). Эта задача ложится на runtime-библиотеку и реализуется при помощи механизма посылки сообщений.

Реально используемой и наиболее широко распространенной схемой создания и уничтожения объектов в Objective-C является используемая в операционных системах NextStep и Mac OS X, которая и будет описана ниже.

Создание нового объекта разбивается на два шага — выделение памяти и инициализация объекта. Первый шаг реализуется методом класса alloc (реализованном в классе NSObject), который выделяет необходимое количество памяти (данный метод используется для выделения памяти не только для объектов класса NSObject, но и любого унаследованного от него класса). При этом в атрибут isa записывается указатель на class object соответствующего класса.

Обратите внимание, что сообщение alloc посылается class object-у требуемого класса и это сообщение возвращает указатель на выделенную под объект память.

Собственно сама инициализация объекта (то есть установка значений его instance-переменных, выделение дополнительных ресурсов и т. п.) осуществляется другими методами, по традиции имена этих методов начинаются с init. Обычно такое сообщение посылается сразу же после сообщение alloc, по адресу, возвращенному этим сообщением.

id anObject = [[Rectangle alloc] init];

Приведённая выше конструкция является правильным способом создания объекта. Обратите внимание, что следующая конструкция может в ряде случаев не работать:

id anObject = [Rectangle alloc];

[anObject init];

Это связано с тем, что для ряда классов метод init может вернуть совсем другой указатель (а не self).

Простейшими примерами того, когда может возникать подобная ситуация, являются синглтоны (тогда, если один экземпляр класса уже существует, то метод init освободит выделенную alloc’ом память и вернет указатель на уже созданный единственный экземпляр) и кэширование объектов, когда для увеличения производительности, выделение объектов происходит сразу блоками и объекты не уничтожаются, а сохраняются для переиспользования.

При создании нового класса обычно нет необходимости переопределять метод alloc, а вот необходимость переопределения метода init возникает достаточно часто.

Обратите внимание, что метод(ы) init является обычным методом, ничем не выделяющимся среди остальных (в отличие от C++, где конструктор — это особый метод, у которого, например, нельзя взять адрес).

Поэтому при создании нового класса и метода init вызов переопределенного метода init (при помощи [super init]) должен быть произведен явно в самом начале метода.

Довольно часто у объектов бывает сразу несколько методов, начинающихся с init, например init, initWithName:, initWithContentsOfFile: и т. д.

Установившейся практикой в таком случае является выделение среди всех init-методов одного, называемого designated initializer. Все остальные init-методы должны вызывать его и только он вызывает унаследованный init метод.

- (id) initWithName: (const char *) theName   // designated initializer
{
    self = [super init];                        // call inherited method
    if (self)
    {
        name = strdup ( theName );
    }
    return self;
}

- (id) init
{
    return [self initWithName: ""];
}

В ряде случаев оказывается удобным совместить выделение памяти и инициализацию объекта в один метод (класса), например в классе NSString есть ряд методов класса, возвращающих уже готовый (проинициализированный) объект:

+ (id) stringWithCString:(const char *)cString encoding:(NSStringEncoding)enc
+ (id) stringWithFormat:(NSString *)format, ...

Mac OS X (как и NextStep) для управления временем жизни объектов используют reference counting — каждый объект содержит внутри себя некоторый счетчик, при создании устанавливаемый в единицу.

Посылка объекту сообщения retain увеличивает значение этого счетчика на единицу (так все контейнерные классы библиотеки Foundation при помещении в них объекта, посылают ему сообщение retain).

Установившейся практикой является посылка объекту сообщения retain всеми, заинтересованными в нём сторонами (объектами), то есть если вы запоминаете ссылку на объект, то следует послать ему сообщение retain.

Когда объект перестает быть нужен, то ему просто посылается сообщение release.

Данное сообщение уменьшает значение счетчика на единицу и, если это значение стало меньше единицы, уничтожает данный объект.

Перед уничтожением объекта ему посылается сообщение dealloc, позволяющее объекту произвести свою деинициализацию. При этом это также является обычным сообщением и в нём Вы явно должны в конце вызвать унаследованную реализацию через [super dealloc].

- (void) dealloc
{
    . . .
    [super dealloc];
}

Управление памятью

Базовые принципы

Управление памятью в Objective-C базируется на принципе «владения объектом». Основные правила управления памятью в Objective-C можно записать так:

  • Для получения объекта во владение необходимо вызвать метод, содержащий в названии «alloc», «new» либо «copy». Например, alloc, newObject, mutableCopy.
  • Для освобождения объекта, который был получен при помощи перечисленных выше функций, необходимо вызвать функцию «release» либо «autorelease». Во всех остальных случаях освобождение объекта не требуется.
  • Если полученный объект должен быть сохранен, необходимо либо стать его владельцем (вызвав retain), либо создать его копию (вызов, содержащий в названии «copy»).

Данные правила базируются на соглашении по именованию в Objective-C и, в то же время, сами являются основой этого соглашения.

Базовые принципы на практике

Предположим, в программе существует класс Company, у которого есть метод workers.

@interface Company : NSObject
{
    NSArray *workers;
}
-(NSArray*)workers;
@end

Рассмотрим небольшой пример использования такого класса:

Company *company = [[Company alloc] init];
// ...
NSArray *workers = [company workers];
// ...
[company release];

Так как объект класса Company создается явно, он должен быть удален по окончании использования ([company release]). В то же время, название метода workers не говорит о том, кто должен удалять массив. В такой ситуации считается, что списком работников управляет объект Компания и его удалять не требуется.

Convenience конструкторы

Многие классы позволяют совместить создание объекта с его инициализацией при помощи методов, называемых convenience конструкторы; такие методы обычно называются +className… Можно предположить, что вызывающая сторона ответственна за управление временем жизни объекта, но подобное поведение противоречило бы соглашению по именованию в Objective-C.

Company *company = [Company company];
[company release];

В приведенном коде вызов [company release] недопустим, так как в данном случае управление временем жизни объекта должно осуществляться при помощи autorelease пула.

Ниже приводится пример корректной реализации метода company:

+(Company*)company
{
     id ret = [[Company alloc] init];
     return [ret autorelease];
}
autorelease

Вернемся к методу workers класса Company. Так как возвращается массив, временем жизни которого вызывающая сторона не управляет, реализация метода workers будет выглядеть приблизительно так:

-(NSArray*)workers
{
     NSArray *copy = [[NSArray alloc] initWithArray:workers];
     return [copy autorelease];
}

Вызов autorelease добавляет объект copy в autorelease пул, вследствие чего возвращаемый объект получит сообщение release при удалении пула, в который он был добавлен. Если объекту, добавленному в autorelease пул, послать сообщение release самостоятельно, при удалении autorelease пула возникнет ошибка.

Возвращение объекта по ссылке

В ряде случаев объекты возвращаются по ссылке, например, метод класса NSData initWithContentsOfURL:options: error: в качестве параметра error принимает (NSError **)errorPtr. В этом случае так же работает соглашение по именованию, из которого следует, что явного запроса на владение объектом нет, соответственно, удалять его не требуется.

Удаление объектов

Когда счетчик ссылок объекта становится равным нулю, объект удаляется. При этом у объекта вызывается метод -(void)dealloc. Если в объекте содержатся какие-то данные, их необходимо удалить в этой функции.

-(void)dealloc
{
    [workers release];
    [super dealloc];
}

После того, как всем переменным класса было послано сообщение release, необходимо вызвать метод dealloc базового класса. Это единственный случай, в котором допустим вызов метода dealloc напрямую.

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

Autorelease pool

Autorelease пул используется для хранения объектов, которым будет послано сообщение release при удалении пула. Для того, чтобы добавить объект в autorelease пул, ему необходимо отправить сообщение autorelease.

В приложениях Cocoa autorelease пул всегда доступен по умолчанию. Для не-AppKit приложений необходимо создавать и управлять временем жизни autorelease пула самостоятельно.

Autorelease пул реализуется классом NSAutoreleasePool.

int main (int argc, const char * argv[])
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

     Company *company = [Company company];
     NSArray *workers = [company workers];

    [pool drain];
    return 0;
}

Удалить объекты из autorelease пула можно не только посредством отправки пулу сообщения release, но и с помощью сообщения drain. Поведение release и drain в среде с подсчетом ссылок идентично. Но в случае работы в GC среде drain вызывает функцию objc_collect_if_needed.

Autorelease пул в многопоточной среде

В Cocoa для каждого из потоков создается свой собственный autorelease пул. По завершении потока autorelease пул уничтожается и всем содержащимся в нём объектам посылается сообщение release.

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

Копирование объектов

Все объекты в Objective-C потенциально поддерживают копирование. Для того, чтобы создать копию объекта, необходимо вызвать метод copy, определённый в классе NSObject. Для создания копии будет вызван метод copyWithZone протокола NSCopying. NSObject не имеет поддержки этого протокола и при необходимости протокол NSCopying должен быть реализован в классах-наследниках.

Копии бывают двух видов: поверхностная копия (shallow copy) и полная копия (deep copy). Разница между этими копиями состоит в том, что при создании поверхностной копии копируются не данные, а ссылка на объект с данными. В случае полной копии копируется объект с данными.

Пример реализации

Реализация копирования может различаться в зависимости от того, поддерживает ли класс-родитель протокол NSCopying. Пример кода для ситуации, когда родитель не реализует протокол NSCopying:

@interface Company : NSObject <NSCopying>
{
     NSString *name;
}
@property(retain) NSString *name;
-(id)copyWithZone:(NSZone *)zone;
@end

@implementation Company
@synthesize name;
-(id)copyWithZone:(NSZone *)zone
{
     id copy = [[[self class] allocWithZone:zone] init];
     [copy setName:[self name]];
     return copy;
}
@end

Если родитель поддерживает протокол NSCopying, реализация будет несколько иной: вызов allocWithZone заменяется на copyWithZone.

id copy = [super copyWithZone:zone];
Копирование неизменяемых объектов

Для immutable объектов создание копии нецелесообразно, и можно ограничиться отправкой самому себе сообщения retain.

-(id)copyWithZone:(NSZone *)zone
{
     return [self retain];
}

Категории

Язык Objective-C обладает возможностью добавлять новые методы к уже существующим классам. Аналогичной возможностью обладают языки Ruby, C#, JavaScript и другие. При этом не требуется исходников класса и добавленные методы автоматически становятся доступными всем классам, унаследованным от изменяемого. Так можно добавить новый метод классу NSObject и этот метод автоматически добавится во все остальные классы.

Механизм, позволяющий расширять уже существующие классы (путём добавления новых методов, новые instance-переменные добавить таким образом нельзя), называется категорией.

Категория имеет своё имя, список методов и имя класса, который она расширяет. Описание категории имеет следующий вид:

#import "ClassName.h"
@interface ClassName ( CategoryName )
  methods declarations
@end

Реализация категории выглядит следующим образом:

#import "CategoryName.h"
@implementation ClassName ( CategoryName )
  methods bodies
@end

С помощью категорий можно создавать свойства (property), которые будут доступны только для чтения другим классам и readwrite внутри своего класса:

@interface ClassName
{
  BOOL flag;
}
@property (assign, readonly) BOOL flag;
@end

#import "ClassName"
@implementation ClassName () // Пустая категория
@property (assign, readwrite) BOOL flag;
@end

@implementation ClassName
@synthesize flag;

-(void) someAction
{
  self.flag = YES;
}
@end

Кроме всего прочего категории можно использовать для того, чтобы обеспечить реализацию классом какого-либо нового протокола, например:

@protocol Printable // сущности, которые можно распечатать
-(void) print;
@end

@interface NSString(Printable) <Printable> // добавляем системному классу NSString возможность быть распечатанным
@end

@implementation NSString(Printable) // реализуем новую функциональность
-(void) print
{
  NSLog(@"Меня распечатали %@!", self);
}
@end

Это избавляет от необходимости писать класс-адаптер PrintableString для NSString.

Class objects и Objective-C runtime

При компиляции программы на языке Objective-C компилятор для каждого введённого класса автоматически создаёт так называемый class object — полноценный объект, содержащий в себе всю информацию о данном классе, включая название, суперкласс, список методов и instance-переменных.

При этом такой объект является полноценным объектом, то есть ему можно посылать сообщения, передавать в качестве параметра.

Одной из особенностей class object’а является поддержка всех методов класса NSObject. То есть при отправке сообщения поиск по селектору сначала ведётся среди методов класса, и если метод не найден, поиск продолжается среди instance-методов класса NSObject.

Ещё одной особенностью является возможность инициализации class object’ов — в начале работы приложения каждому class object’у посылается сообщение (класса) initialize.

Это сообщение гарантированно посылается каждому class object’у, причём всего один раз и до того, как ему будет послано любое другое сообщение. Простейшим примером применения такого сообщения является реализация Singleton’ов — именно в методе initialize следует создать тот самый единственный экземпляр объекта и запомнить его в static-переменной.

Objective-C runtime от Apple содержит большое количество С-функций, служащих для работы с классами (непосредственно во время выполнения программы).

Наиболее интересными являются следующие:

Method   class_getInstanceMethod( Class aClass, SEL aSelector );
Method   class_getClassMethod   ( Class aClass, SEL aSelector );
struct objc_method_list * class_nextMethodList(Class theClass, void ** iterator);
void     class_addMethods       ( Class aClass, struct objc_method_list * methodList );
void     class_removeMethods    ( Class aClass, struct objc_method_list * methodList );
unsigned method_getNumberOfArguments ( Method method );
unsigned method_getSizeOfArguments   ( Method method );
unsigned method_getArgumentInfo      ( Method method, int argIndex, const
                                       char ** type, int *  offset );
Ivar     class_getInstanceVariable   ( Class aClass, const char * aVariableName );

Функция class_getInstanceMethod возвращает указатель на структуру (objc_method), описывающую заданный instance-метод данного класса.

Функция class_getClassMethod возвращает указатель на структуру (objc_method), описывающую заданный метод данного класса.

Функция class_nextMethodList возвращает один из списков методов для заданного класса. Приводимый ниже фрагмент кода позволяет перебрать все методы для данного класса.

    void                    * iterator = 0;
    struct objc_method_list * methodList;

    //
    // Each call to class_nextMethodList returns one methodList
    //

    methodList = class_nextMethodList( classObject, &iterator  )

    while ( methodList != nil )
    {
        // …do something with the method list here…

        methodList = class_nextMethodList ( classObject, &iterator  );
    }

Функция class_addMethods позволяет добавлять новые методы к заданному классу.

Функция class_removeMethods позволяет убирать методы из заданного класса.

Функция method_getNumberOfArguments Возвращает количество аргументов для заданного метода.

Функция method_getSizeOfArguments возвращает размер места на стеке, занимаемого всеми аргументами данного метода.

Функция method_getArgumentInfo возвращает информацию об одном из аргументов для заданного метода.

Функция class_getInstanceVariable возвращает информацию об instance-переменной класса в виде указателя на структуру objc_ivar.

Для кодирования информации о типах используется специальное строковое представление, однозначно сопоставляющее каждому типу данных некоторую строку. Явно получить такую строку для произвольного типа можно при помощи конструкции @encode ().

char * buf1 = @encode ( int ** );
char * buf2 = @encode ( struct key );
char * buf3 = @encode ( Rectangle );

Разное

Официальный сайт Apple[1] - главный источник информации о языке. Форум разработчиков, примеры кода и полная версия документации доступны только зарегистрированным разработчикам.

IDE Xcode - основное средство разработки на языке Objective-C. IDE поддерживает только ОС Mac OS X и распространяется бесплатно через магазин приложений Apple App Store.

Полезную информации по языку Objective-C можно найти в news-группе[2] и архивах списка рассылки[3].

Проект GNUstep[4] - попытка создания аналогов закрытых библиотек Foundation и AppKit, используемых в NextStep и Mac OS X. Исходный код библиотек написан на языке Objective-C и распространяется бесплатно. На сайте проекта доступны примеры использования языка и исходный код нескольких приложений.

Objective-C доступен практически в каждом дистрибутиве GNU/Linux благодаря компилятору gobjc, созданному проектом gcc.

Для работы с Objective-C под ОС Windows используют эмуляторы среды POSIX (бесплатные):

Напишите отзыв о статье "Objective-C"

Примечания

  1. [developer.apple.com/ Apple developer]
  2. [groups.google.com/group/comp.lang.objective-c comp.lang.objective-c]
  3. [lists.apple.com/mailman/listinfo Objc-language]
  4. [www.gnustep.org Официальный сайт проекта GNUstep]

Литература

  • Мэтт Нойбург. Программирование для iOS 7. Основы Objective-C, Xcode и Cocoa = iOS 7 Programming Fundamentals: Objective-C, Cocoa, and Xcode Basics. — М.: «Вильямс», 2014. — 384 с. — ISBN 978-5-8459-1895-6.
  • Скотт Кнастер, Вакар Малик, Марк Далримпл. Objective-C и программирование для Mac OS X и iOS, 2-е издание = Learn Objective-C on the Mac: For OS X and iOS, 2nd edition. — М.: «Вильямс», 2013. — 416 с. — ISBN 978-5-8459-1826-0.
  • Майкл Приват, Роберт Уорнер. Разработка приложений для Mac OS X Lion. Программирование на Objective-C в Xcode = Beginning Mac OS X Lion Apps Development. — М.: Вильямс, 2012. — 384 с. — ISBN 978-5-8459-1789-8.

Ссылки

Ресурсы
  • [classroomm.com/objective-c/ Форум Стивена Кохана, посвященный Objective-C 2.0]  (англ.)
  • [code.google.com/p/nobjective/ Мост для работы с Objective-C из среды выполнения .NET (Macintosh, Mono)]
Статьи
  • [developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/ Introduction to The Objective-C Programming Language (Apple Developer Connection)] [web.archive.org/web/20030704024148/developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/ObjC.pdf (PDF)]  (англ.)
  • [www.insk.org/objectivecprimer/ Изучаем Objective-C: Букварь] (рус.)
  • [steps3d.narod.ru/tutorials/objective-c-tutorial.html Язык программирования Objective-C] (рус.)
  • [www.informit.com/articles/article.aspx?p=1353398 Advanced Flow Control for Objective-C]  (англ.)
  • [www.informit.com/articles/article.aspx?p=1331446 Debugging C-Family Languages]  (англ.)
  • [www.informit.com/articles/article.aspx?p=1353397 Fun with the Objective-C Runtime]  (англ.)
  • [www.informit.com/articles/article.aspx?p=1272496 Objective-C for C++ Programmers]  (англ.) и [netsago.org/ru/docs/1/15/ перевод]  (рус.)
  • [www.informit.com/articles/article.aspx?p=1353401 Steve Kochan on the Evolution of Objective-C]  (англ.)
  • [www.informit.com/articles/article.aspx?p=1322281 The Dynamic Languages Renaissance]  (англ.)

Отрывок, характеризующий Objective-C

Лет тридцать Богучаровым управлял староста Дрон, которого старый князь звал Дронушкой.
Дрон был один из тех крепких физически и нравственно мужиков, которые, как только войдут в года, обрастут бородой, так, не изменяясь, живут до шестидесяти – семидесяти лет, без одного седого волоса или недостатка зуба, такие же прямые и сильные в шестьдесят лет, как и в тридцать.
Дрон, вскоре после переселения на теплые реки, в котором он участвовал, как и другие, был сделан старостой бурмистром в Богучарове и с тех пор двадцать три года безупречно пробыл в этой должности. Мужики боялись его больше, чем барина. Господа, и старый князь, и молодой, и управляющий, уважали его и в шутку называли министром. Во все время своей службы Дрон нн разу не был ни пьян, ни болен; никогда, ни после бессонных ночей, ни после каких бы то ни было трудов, не выказывал ни малейшей усталости и, не зная грамоте, никогда не забывал ни одного счета денег и пудов муки по огромным обозам, которые он продавал, и ни одной копны ужи на хлеба на каждой десятине богучаровских полей.
Этого то Дрона Алпатыч, приехавший из разоренных Лысых Гор, призвал к себе в день похорон князя и приказал ему приготовить двенадцать лошадей под экипажи княжны и восемнадцать подвод под обоз, который должен был быть поднят из Богучарова. Хотя мужики и были оброчные, исполнение приказания этого не могло встретить затруднения, по мнению Алпатыча, так как в Богучарове было двести тридцать тягол и мужики были зажиточные. Но староста Дрон, выслушав приказание, молча опустил глаза. Алпатыч назвал ему мужиков, которых он знал и с которых он приказывал взять подводы.
Дрон отвечал, что лошади у этих мужиков в извозе. Алпатыч назвал других мужиков, и у тех лошадей не было, по словам Дрона, одни были под казенными подводами, другие бессильны, у третьих подохли лошади от бескормицы. Лошадей, по мнению Дрона, нельзя было собрать не только под обоз, но и под экипажи.
Алпатыч внимательно посмотрел на Дрона и нахмурился. Как Дрон был образцовым старостой мужиком, так и Алпатыч недаром управлял двадцать лет имениями князя и был образцовым управляющим. Он в высшей степени способен был понимать чутьем потребности и инстинкты народа, с которым имел дело, и потому он был превосходным управляющим. Взглянув на Дрона, он тотчас понял, что ответы Дрона не были выражением мысли Дрона, но выражением того общего настроения богучаровского мира, которым староста уже был захвачен. Но вместе с тем он знал, что нажившийся и ненавидимый миром Дрон должен был колебаться между двумя лагерями – господским и крестьянским. Это колебание он заметил в его взгляде, и потому Алпатыч, нахмурившись, придвинулся к Дрону.
– Ты, Дронушка, слушай! – сказал он. – Ты мне пустого не говори. Его сиятельство князь Андрей Николаич сами мне приказали, чтобы весь народ отправить и с неприятелем не оставаться, и царский на то приказ есть. А кто останется, тот царю изменник. Слышишь?
– Слушаю, – отвечал Дрон, не поднимая глаз.
Алпатыч не удовлетворился этим ответом.
– Эй, Дрон, худо будет! – сказал Алпатыч, покачав головой.
– Власть ваша! – сказал Дрон печально.
– Эй, Дрон, оставь! – повторил Алпатыч, вынимая руку из за пазухи и торжественным жестом указывая ею на пол под ноги Дрона. – Я не то, что тебя насквозь, я под тобой на три аршина все насквозь вижу, – сказал он, вглядываясь в пол под ноги Дрона.
Дрон смутился, бегло взглянул на Алпатыча и опять опустил глаза.
– Ты вздор то оставь и народу скажи, чтобы собирались из домов идти в Москву и готовили подводы завтра к утру под княжнин обоз, да сам на сходку не ходи. Слышишь?
Дрон вдруг упал в ноги.
– Яков Алпатыч, уволь! Возьми от меня ключи, уволь ради Христа.
– Оставь! – сказал Алпатыч строго. – Под тобой насквозь на три аршина вижу, – повторил он, зная, что его мастерство ходить за пчелами, знание того, когда сеять овес, и то, что он двадцать лет умел угодить старому князю, давно приобрели ему славу колдуна и что способность видеть на три аршина под человеком приписывается колдунам.
Дрон встал и хотел что то сказать, но Алпатыч перебил его:
– Что вы это вздумали? А?.. Что ж вы думаете? А?
– Что мне с народом делать? – сказал Дрон. – Взбуровило совсем. Я и то им говорю…
– То то говорю, – сказал Алпатыч. – Пьют? – коротко спросил он.
– Весь взбуровился, Яков Алпатыч: другую бочку привезли.
– Так ты слушай. Я к исправнику поеду, а ты народу повести, и чтоб они это бросили, и чтоб подводы были.
– Слушаю, – отвечал Дрон.
Больше Яков Алпатыч не настаивал. Он долго управлял народом и знал, что главное средство для того, чтобы люди повиновались, состоит в том, чтобы не показывать им сомнения в том, что они могут не повиноваться. Добившись от Дрона покорного «слушаю с», Яков Алпатыч удовлетворился этим, хотя он не только сомневался, но почти был уверен в том, что подводы без помощи воинской команды не будут доставлены.
И действительно, к вечеру подводы не были собраны. На деревне у кабака была опять сходка, и на сходке положено было угнать лошадей в лес и не выдавать подвод. Ничего не говоря об этом княжне, Алпатыч велел сложить с пришедших из Лысых Гор свою собственную кладь и приготовить этих лошадей под кареты княжны, а сам поехал к начальству.

Х
После похорон отца княжна Марья заперлась в своей комнате и никого не впускала к себе. К двери подошла девушка сказать, что Алпатыч пришел спросить приказания об отъезде. (Это было еще до разговора Алпатыча с Дроном.) Княжна Марья приподнялась с дивана, на котором она лежала, и сквозь затворенную дверь проговорила, что она никуда и никогда не поедет и просит, чтобы ее оставили в покое.
Окна комнаты, в которой лежала княжна Марья, были на запад. Она лежала на диване лицом к стене и, перебирая пальцами пуговицы на кожаной подушке, видела только эту подушку, и неясные мысли ее были сосредоточены на одном: она думала о невозвратимости смерти и о той своей душевной мерзости, которой она не знала до сих пор и которая выказалась во время болезни ее отца. Она хотела, но не смела молиться, не смела в том душевном состоянии, в котором она находилась, обращаться к богу. Она долго лежала в этом положении.
Солнце зашло на другую сторону дома и косыми вечерними лучами в открытые окна осветило комнату и часть сафьянной подушки, на которую смотрела княжна Марья. Ход мыслей ее вдруг приостановился. Она бессознательно приподнялась, оправила волоса, встала и подошла к окну, невольно вдыхая в себя прохладу ясного, но ветреного вечера.
«Да, теперь тебе удобно любоваться вечером! Его уж нет, и никто тебе не помешает», – сказала она себе, и, опустившись на стул, она упала головой на подоконник.
Кто то нежным и тихим голосом назвал ее со стороны сада и поцеловал в голову. Она оглянулась. Это была m lle Bourienne, в черном платье и плерезах. Она тихо подошла к княжне Марье, со вздохом поцеловала ее и тотчас же заплакала. Княжна Марья оглянулась на нее. Все прежние столкновения с нею, ревность к ней, вспомнились княжне Марье; вспомнилось и то, как он последнее время изменился к m lle Bourienne, не мог ее видеть, и, стало быть, как несправедливы были те упреки, которые княжна Марья в душе своей делала ей. «Да и мне ли, мне ли, желавшей его смерти, осуждать кого нибудь! – подумала она.
Княжне Марье живо представилось положение m lle Bourienne, в последнее время отдаленной от ее общества, но вместе с тем зависящей от нее и живущей в чужом доме. И ей стало жалко ее. Она кротко вопросительно посмотрела на нее и протянула ей руку. M lle Bourienne тотчас заплакала, стала целовать ее руку и говорить о горе, постигшем княжну, делая себя участницей этого горя. Она говорила о том, что единственное утешение в ее горе есть то, что княжна позволила ей разделить его с нею. Она говорила, что все бывшие недоразумения должны уничтожиться перед великим горем, что она чувствует себя чистой перед всеми и что он оттуда видит ее любовь и благодарность. Княжна слушала ее, не понимая ее слов, но изредка взглядывая на нее и вслушиваясь в звуки ее голоса.
– Ваше положение вдвойне ужасно, милая княжна, – помолчав немного, сказала m lle Bourienne. – Я понимаю, что вы не могли и не можете думать о себе; но я моей любовью к вам обязана это сделать… Алпатыч был у вас? Говорил он с вами об отъезде? – спросила она.
Княжна Марья не отвечала. Она не понимала, куда и кто должен был ехать. «Разве можно было что нибудь предпринимать теперь, думать о чем нибудь? Разве не все равно? Она не отвечала.
– Вы знаете ли, chere Marie, – сказала m lle Bourienne, – знаете ли, что мы в опасности, что мы окружены французами; ехать теперь опасно. Ежели мы поедем, мы почти наверное попадем в плен, и бог знает…
Княжна Марья смотрела на свою подругу, не понимая того, что она говорила.
– Ах, ежели бы кто нибудь знал, как мне все все равно теперь, – сказала она. – Разумеется, я ни за что не желала бы уехать от него… Алпатыч мне говорил что то об отъезде… Поговорите с ним, я ничего, ничего не могу и не хочу…
– Я говорила с ним. Он надеется, что мы успеем уехать завтра; но я думаю, что теперь лучше бы было остаться здесь, – сказала m lle Bourienne. – Потому что, согласитесь, chere Marie, попасть в руки солдат или бунтующих мужиков на дороге – было бы ужасно. – M lle Bourienne достала из ридикюля объявление на нерусской необыкновенной бумаге французского генерала Рамо о том, чтобы жители не покидали своих домов, что им оказано будет должное покровительство французскими властями, и подала ее княжне.
– Я думаю, что лучше обратиться к этому генералу, – сказала m lle Bourienne, – и я уверена, что вам будет оказано должное уважение.
Княжна Марья читала бумагу, и сухие рыдания задергали ее лицо.
– Через кого вы получили это? – сказала она.
– Вероятно, узнали, что я француженка по имени, – краснея, сказала m lle Bourienne.
Княжна Марья с бумагой в руке встала от окна и с бледным лицом вышла из комнаты и пошла в бывший кабинет князя Андрея.
– Дуняша, позовите ко мне Алпатыча, Дронушку, кого нибудь, – сказала княжна Марья, – и скажите Амалье Карловне, чтобы она не входила ко мне, – прибавила она, услыхав голос m lle Bourienne. – Поскорее ехать! Ехать скорее! – говорила княжна Марья, ужасаясь мысли о том, что она могла остаться во власти французов.
«Чтобы князь Андрей знал, что она во власти французов! Чтоб она, дочь князя Николая Андреича Болконского, просила господина генерала Рамо оказать ей покровительство и пользовалась его благодеяниями! – Эта мысль приводила ее в ужас, заставляла ее содрогаться, краснеть и чувствовать еще не испытанные ею припадки злобы и гордости. Все, что только было тяжелого и, главное, оскорбительного в ее положении, живо представлялось ей. «Они, французы, поселятся в этом доме; господин генерал Рамо займет кабинет князя Андрея; будет для забавы перебирать и читать его письма и бумаги. M lle Bourienne lui fera les honneurs de Богучарово. [Мадемуазель Бурьен будет принимать его с почестями в Богучарове.] Мне дадут комнатку из милости; солдаты разорят свежую могилу отца, чтобы снять с него кресты и звезды; они мне будут рассказывать о победах над русскими, будут притворно выражать сочувствие моему горю… – думала княжна Марья не своими мыслями, но чувствуя себя обязанной думать за себя мыслями своего отца и брата. Для нее лично было все равно, где бы ни оставаться и что бы с ней ни было; но она чувствовала себя вместе с тем представительницей своего покойного отца и князя Андрея. Она невольно думала их мыслями и чувствовала их чувствами. Что бы они сказали, что бы они сделали теперь, то самое она чувствовала необходимым сделать. Она пошла в кабинет князя Андрея и, стараясь проникнуться его мыслями, обдумывала свое положение.
Требования жизни, которые она считала уничтоженными со смертью отца, вдруг с новой, еще неизвестной силой возникли перед княжной Марьей и охватили ее. Взволнованная, красная, она ходила по комнате, требуя к себе то Алпатыча, то Михаила Ивановича, то Тихона, то Дрона. Дуняша, няня и все девушки ничего не могли сказать о том, в какой мере справедливо было то, что объявила m lle Bourienne. Алпатыча не было дома: он уехал к начальству. Призванный Михаил Иваныч, архитектор, явившийся к княжне Марье с заспанными глазами, ничего не мог сказать ей. Он точно с той же улыбкой согласия, с которой он привык в продолжение пятнадцати лет отвечать, не выражая своего мнения, на обращения старого князя, отвечал на вопросы княжны Марьи, так что ничего определенного нельзя было вывести из его ответов. Призванный старый камердинер Тихон, с опавшим и осунувшимся лицом, носившим на себе отпечаток неизлечимого горя, отвечал «слушаю с» на все вопросы княжны Марьи и едва удерживался от рыданий, глядя на нее.
Наконец вошел в комнату староста Дрон и, низко поклонившись княжне, остановился у притолоки.
Княжна Марья прошлась по комнате и остановилась против него.
– Дронушка, – сказала княжна Марья, видевшая в нем несомненного друга, того самого Дронушку, который из своей ежегодной поездки на ярмарку в Вязьму привозил ей всякий раз и с улыбкой подавал свой особенный пряник. – Дронушка, теперь, после нашего несчастия, – начала она и замолчала, не в силах говорить дальше.
– Все под богом ходим, – со вздохом сказал он. Они помолчали.
– Дронушка, Алпатыч куда то уехал, мне не к кому обратиться. Правду ли мне говорят, что мне и уехать нельзя?
– Отчего же тебе не ехать, ваше сиятельство, ехать можно, – сказал Дрон.
– Мне сказали, что опасно от неприятеля. Голубчик, я ничего не могу, ничего не понимаю, со мной никого нет. Я непременно хочу ехать ночью или завтра рано утром. – Дрон молчал. Он исподлобья взглянул на княжну Марью.
– Лошадей нет, – сказал он, – я и Яков Алпатычу говорил.
– Отчего же нет? – сказала княжна.
– Все от божьего наказания, – сказал Дрон. – Какие лошади были, под войска разобрали, а какие подохли, нынче год какой. Не то лошадей кормить, а как бы самим с голоду не помереть! И так по три дня не емши сидят. Нет ничего, разорили вконец.
Княжна Марья внимательно слушала то, что он говорил ей.
– Мужики разорены? У них хлеба нет? – спросила она.
– Голодной смертью помирают, – сказал Дрон, – не то что подводы…
– Да отчего же ты не сказал, Дронушка? Разве нельзя помочь? Я все сделаю, что могу… – Княжне Марье странно было думать, что теперь, в такую минуту, когда такое горе наполняло ее душу, могли быть люди богатые и бедные и что могли богатые не помочь бедным. Она смутно знала и слышала, что бывает господский хлеб и что его дают мужикам. Она знала тоже, что ни брат, ни отец ее не отказали бы в нужде мужикам; она только боялась ошибиться как нибудь в словах насчет этой раздачи мужикам хлеба, которым она хотела распорядиться. Она была рада тому, что ей представился предлог заботы, такой, для которой ей не совестно забыть свое горе. Она стала расспрашивать Дронушку подробности о нуждах мужиков и о том, что есть господского в Богучарове.
– Ведь у нас есть хлеб господский, братнин? – спросила она.
– Господский хлеб весь цел, – с гордостью сказал Дрон, – наш князь не приказывал продавать.
– Выдай его мужикам, выдай все, что им нужно: я тебе именем брата разрешаю, – сказала княжна Марья.
Дрон ничего не ответил и глубоко вздохнул.
– Ты раздай им этот хлеб, ежели его довольно будет для них. Все раздай. Я тебе приказываю именем брата, и скажи им: что, что наше, то и ихнее. Мы ничего не пожалеем для них. Так ты скажи.
Дрон пристально смотрел на княжну, в то время как она говорила.
– Уволь ты меня, матушка, ради бога, вели от меня ключи принять, – сказал он. – Служил двадцать три года, худого не делал; уволь, ради бога.
Княжна Марья не понимала, чего он хотел от нее и от чего он просил уволить себя. Она отвечала ему, что она никогда не сомневалась в его преданности и что она все готова сделать для него и для мужиков.


Через час после этого Дуняша пришла к княжне с известием, что пришел Дрон и все мужики, по приказанию княжны, собрались у амбара, желая переговорить с госпожою.
– Да я никогда не звала их, – сказала княжна Марья, – я только сказала Дронушке, чтобы раздать им хлеба.
– Только ради бога, княжна матушка, прикажите их прогнать и не ходите к ним. Все обман один, – говорила Дуняша, – а Яков Алпатыч приедут, и поедем… и вы не извольте…
– Какой же обман? – удивленно спросила княжна
– Да уж я знаю, только послушайте меня, ради бога. Вот и няню хоть спросите. Говорят, не согласны уезжать по вашему приказанию.
– Ты что нибудь не то говоришь. Да я никогда не приказывала уезжать… – сказала княжна Марья. – Позови Дронушку.
Пришедший Дрон подтвердил слова Дуняши: мужики пришли по приказанию княжны.
– Да я никогда не звала их, – сказала княжна. – Ты, верно, не так передал им. Я только сказала, чтобы ты им отдал хлеб.
Дрон, не отвечая, вздохнул.
– Если прикажете, они уйдут, – сказал он.
– Нет, нет, я пойду к ним, – сказала княжна Марья
Несмотря на отговариванье Дуняши и няни, княжна Марья вышла на крыльцо. Дрон, Дуняша, няня и Михаил Иваныч шли за нею. «Они, вероятно, думают, что я предлагаю им хлеб с тем, чтобы они остались на своих местах, и сама уеду, бросив их на произвол французов, – думала княжна Марья. – Я им буду обещать месячину в подмосковной, квартиры; я уверена, что Andre еще больше бы сделав на моем месте», – думала она, подходя в сумерках к толпе, стоявшей на выгоне у амбара.
Толпа, скучиваясь, зашевелилась, и быстро снялись шляпы. Княжна Марья, опустив глаза и путаясь ногами в платье, близко подошла к ним. Столько разнообразных старых и молодых глаз было устремлено на нее и столько было разных лиц, что княжна Марья не видала ни одного лица и, чувствуя необходимость говорить вдруг со всеми, не знала, как быть. Но опять сознание того, что она – представительница отца и брата, придало ей силы, и она смело начала свою речь.
– Я очень рада, что вы пришли, – начала княжна Марья, не поднимая глаз и чувствуя, как быстро и сильно билось ее сердце. – Мне Дронушка сказал, что вас разорила война. Это наше общее горе, и я ничего не пожалею, чтобы помочь вам. Я сама еду, потому что уже опасно здесь и неприятель близко… потому что… Я вам отдаю все, мои друзья, и прошу вас взять все, весь хлеб наш, чтобы у вас не было нужды. А ежели вам сказали, что я отдаю вам хлеб с тем, чтобы вы остались здесь, то это неправда. Я, напротив, прошу вас уезжать со всем вашим имуществом в нашу подмосковную, и там я беру на себя и обещаю вам, что вы не будете нуждаться. Вам дадут и домы и хлеба. – Княжна остановилась. В толпе только слышались вздохи.
– Я не от себя делаю это, – продолжала княжна, – я это делаю именем покойного отца, который был вам хорошим барином, и за брата, и его сына.
Она опять остановилась. Никто не прерывал ее молчания.
– Горе наше общее, и будем делить всё пополам. Все, что мое, то ваше, – сказала она, оглядывая лица, стоявшие перед нею.
Все глаза смотрели на нее с одинаковым выражением, значения которого она не могла понять. Было ли это любопытство, преданность, благодарность, или испуг и недоверие, но выражение на всех лицах было одинаковое.
– Много довольны вашей милостью, только нам брать господский хлеб не приходится, – сказал голос сзади.
– Да отчего же? – сказала княжна.
Никто не ответил, и княжна Марья, оглядываясь по толпе, замечала, что теперь все глаза, с которыми она встречалась, тотчас же опускались.
– Отчего же вы не хотите? – спросила она опять.
Никто не отвечал.
Княжне Марье становилось тяжело от этого молчанья; она старалась уловить чей нибудь взгляд.
– Отчего вы не говорите? – обратилась княжна к старому старику, который, облокотившись на палку, стоял перед ней. – Скажи, ежели ты думаешь, что еще что нибудь нужно. Я все сделаю, – сказала она, уловив его взгляд. Но он, как бы рассердившись за это, опустил совсем голову и проговорил:
– Чего соглашаться то, не нужно нам хлеба.
– Что ж, нам все бросить то? Не согласны. Не согласны… Нет нашего согласия. Мы тебя жалеем, а нашего согласия нет. Поезжай сама, одна… – раздалось в толпе с разных сторон. И опять на всех лицах этой толпы показалось одно и то же выражение, и теперь это было уже наверное не выражение любопытства и благодарности, а выражение озлобленной решительности.
– Да вы не поняли, верно, – с грустной улыбкой сказала княжна Марья. – Отчего вы не хотите ехать? Я обещаю поселить вас, кормить. А здесь неприятель разорит вас…
Но голос ее заглушали голоса толпы.
– Нет нашего согласия, пускай разоряет! Не берем твоего хлеба, нет согласия нашего!
Княжна Марья старалась уловить опять чей нибудь взгляд из толпы, но ни один взгляд не был устремлен на нее; глаза, очевидно, избегали ее. Ей стало странно и неловко.
– Вишь, научила ловко, за ней в крепость иди! Дома разори да в кабалу и ступай. Как же! Я хлеб, мол, отдам! – слышались голоса в толпе.
Княжна Марья, опустив голову, вышла из круга и пошла в дом. Повторив Дрону приказание о том, чтобы завтра были лошади для отъезда, она ушла в свою комнату и осталась одна с своими мыслями.


Долго эту ночь княжна Марья сидела у открытого окна в своей комнате, прислушиваясь к звукам говора мужиков, доносившегося с деревни, но она не думала о них. Она чувствовала, что, сколько бы она ни думала о них, она не могла бы понять их. Она думала все об одном – о своем горе, которое теперь, после перерыва, произведенного заботами о настоящем, уже сделалось для нее прошедшим. Она теперь уже могла вспоминать, могла плакать и могла молиться. С заходом солнца ветер затих. Ночь была тихая и свежая. В двенадцатом часу голоса стали затихать, пропел петух, из за лип стала выходить полная луна, поднялся свежий, белый туман роса, и над деревней и над домом воцарилась тишина.
Одна за другой представлялись ей картины близкого прошедшего – болезни и последних минут отца. И с грустной радостью она теперь останавливалась на этих образах, отгоняя от себя с ужасом только одно последнее представление его смерти, которое – она чувствовала – она была не в силах созерцать даже в своем воображении в этот тихий и таинственный час ночи. И картины эти представлялись ей с такой ясностью и с такими подробностями, что они казались ей то действительностью, то прошедшим, то будущим.
То ей живо представлялась та минута, когда с ним сделался удар и его из сада в Лысых Горах волокли под руки и он бормотал что то бессильным языком, дергал седыми бровями и беспокойно и робко смотрел на нее.
«Он и тогда хотел сказать мне то, что он сказал мне в день своей смерти, – думала она. – Он всегда думал то, что он сказал мне». И вот ей со всеми подробностями вспомнилась та ночь в Лысых Горах накануне сделавшегося с ним удара, когда княжна Марья, предчувствуя беду, против его воли осталась с ним. Она не спала и ночью на цыпочках сошла вниз и, подойдя к двери в цветочную, в которой в эту ночь ночевал ее отец, прислушалась к его голосу. Он измученным, усталым голосом говорил что то с Тихоном. Ему, видно, хотелось поговорить. «И отчего он не позвал меня? Отчего он не позволил быть мне тут на месте Тихона? – думала тогда и теперь княжна Марья. – Уж он не выскажет никогда никому теперь всего того, что было в его душе. Уж никогда не вернется для него и для меня эта минута, когда бы он говорил все, что ему хотелось высказать, а я, а не Тихон, слушала бы и понимала его. Отчего я не вошла тогда в комнату? – думала она. – Может быть, он тогда же бы сказал мне то, что он сказал в день смерти. Он и тогда в разговоре с Тихоном два раза спросил про меня. Ему хотелось меня видеть, а я стояла тут, за дверью. Ему было грустно, тяжело говорить с Тихоном, который не понимал его. Помню, как он заговорил с ним про Лизу, как живую, – он забыл, что она умерла, и Тихон напомнил ему, что ее уже нет, и он закричал: „Дурак“. Ему тяжело было. Я слышала из за двери, как он, кряхтя, лег на кровать и громко прокричал: „Бог мой!Отчего я не взошла тогда? Что ж бы он сделал мне? Что бы я потеряла? А может быть, тогда же он утешился бы, он сказал бы мне это слово“. И княжна Марья вслух произнесла то ласковое слово, которое он сказал ей в день смерти. «Ду ше нь ка! – повторила княжна Марья это слово и зарыдала облегчающими душу слезами. Она видела теперь перед собою его лицо. И не то лицо, которое она знала с тех пор, как себя помнила, и которое она всегда видела издалека; а то лицо – робкое и слабое, которое она в последний день, пригибаясь к его рту, чтобы слышать то, что он говорил, в первый раз рассмотрела вблизи со всеми его морщинами и подробностями.
«Душенька», – повторила она.
«Что он думал, когда сказал это слово? Что он думает теперь? – вдруг пришел ей вопрос, и в ответ на это она увидала его перед собой с тем выражением лица, которое у него было в гробу на обвязанном белым платком лице. И тот ужас, который охватил ее тогда, когда она прикоснулась к нему и убедилась, что это не только не был он, но что то таинственное и отталкивающее, охватил ее и теперь. Она хотела думать о другом, хотела молиться и ничего не могла сделать. Она большими открытыми глазами смотрела на лунный свет и тени, всякую секунду ждала увидеть его мертвое лицо и чувствовала, что тишина, стоявшая над домом и в доме, заковывала ее.
– Дуняша! – прошептала она. – Дуняша! – вскрикнула она диким голосом и, вырвавшись из тишины, побежала к девичьей, навстречу бегущим к ней няне и девушкам.


17 го августа Ростов и Ильин, сопутствуемые только что вернувшимся из плена Лаврушкой и вестовым гусаром, из своей стоянки Янково, в пятнадцати верстах от Богучарова, поехали кататься верхами – попробовать новую, купленную Ильиным лошадь и разузнать, нет ли в деревнях сена.
Богучарово находилось последние три дня между двумя неприятельскими армиями, так что так же легко мог зайти туда русский арьергард, как и французский авангард, и потому Ростов, как заботливый эскадронный командир, желал прежде французов воспользоваться тем провиантом, который оставался в Богучарове.
Ростов и Ильин были в самом веселом расположении духа. Дорогой в Богучарово, в княжеское именье с усадьбой, где они надеялись найти большую дворню и хорошеньких девушек, они то расспрашивали Лаврушку о Наполеоне и смеялись его рассказам, то перегонялись, пробуя лошадь Ильина.
Ростов и не знал и не думал, что эта деревня, в которую он ехал, была именье того самого Болконского, который был женихом его сестры.
Ростов с Ильиным в последний раз выпустили на перегонку лошадей в изволок перед Богучаровым, и Ростов, перегнавший Ильина, первый вскакал в улицу деревни Богучарова.
– Ты вперед взял, – говорил раскрасневшийся Ильин.
– Да, всё вперед, и на лугу вперед, и тут, – отвечал Ростов, поглаживая рукой своего взмылившегося донца.
– А я на французской, ваше сиятельство, – сзади говорил Лаврушка, называя французской свою упряжную клячу, – перегнал бы, да только срамить не хотел.
Они шагом подъехали к амбару, у которого стояла большая толпа мужиков.
Некоторые мужики сняли шапки, некоторые, не снимая шапок, смотрели на подъехавших. Два старые длинные мужика, с сморщенными лицами и редкими бородами, вышли из кабака и с улыбками, качаясь и распевая какую то нескладную песню, подошли к офицерам.
– Молодцы! – сказал, смеясь, Ростов. – Что, сено есть?
– И одинакие какие… – сказал Ильин.
– Развесе…oo…ооо…лая бесе… бесе… – распевали мужики с счастливыми улыбками.
Один мужик вышел из толпы и подошел к Ростову.
– Вы из каких будете? – спросил он.
– Французы, – отвечал, смеючись, Ильин. – Вот и Наполеон сам, – сказал он, указывая на Лаврушку.
– Стало быть, русские будете? – переспросил мужик.
– А много вашей силы тут? – спросил другой небольшой мужик, подходя к ним.
– Много, много, – отвечал Ростов. – Да вы что ж собрались тут? – прибавил он. – Праздник, что ль?
– Старички собрались, по мирскому делу, – отвечал мужик, отходя от него.
В это время по дороге от барского дома показались две женщины и человек в белой шляпе, шедшие к офицерам.
– В розовом моя, чур не отбивать! – сказал Ильин, заметив решительно подвигавшуюся к нему Дуняшу.
– Наша будет! – подмигнув, сказал Ильину Лаврушка.
– Что, моя красавица, нужно? – сказал Ильин, улыбаясь.
– Княжна приказали узнать, какого вы полка и ваши фамилии?
– Это граф Ростов, эскадронный командир, а я ваш покорный слуга.
– Бе…се…е…ду…шка! – распевал пьяный мужик, счастливо улыбаясь и глядя на Ильина, разговаривающего с девушкой. Вслед за Дуняшей подошел к Ростову Алпатыч, еще издали сняв свою шляпу.
– Осмелюсь обеспокоить, ваше благородие, – сказал он с почтительностью, но с относительным пренебрежением к юности этого офицера и заложив руку за пазуху. – Моя госпожа, дочь скончавшегося сего пятнадцатого числа генерал аншефа князя Николая Андреевича Болконского, находясь в затруднении по случаю невежества этих лиц, – он указал на мужиков, – просит вас пожаловать… не угодно ли будет, – с грустной улыбкой сказал Алпатыч, – отъехать несколько, а то не так удобно при… – Алпатыч указал на двух мужиков, которые сзади так и носились около него, как слепни около лошади.
– А!.. Алпатыч… А? Яков Алпатыч!.. Важно! прости ради Христа. Важно! А?.. – говорили мужики, радостно улыбаясь ему. Ростов посмотрел на пьяных стариков и улыбнулся.
– Или, может, это утешает ваше сиятельство? – сказал Яков Алпатыч с степенным видом, не заложенной за пазуху рукой указывая на стариков.
– Нет, тут утешенья мало, – сказал Ростов и отъехал. – В чем дело? – спросил он.
– Осмелюсь доложить вашему сиятельству, что грубый народ здешний не желает выпустить госпожу из имения и угрожает отпречь лошадей, так что с утра все уложено и ее сиятельство не могут выехать.
– Не может быть! – вскрикнул Ростов.
– Имею честь докладывать вам сущую правду, – повторил Алпатыч.
Ростов слез с лошади и, передав ее вестовому, пошел с Алпатычем к дому, расспрашивая его о подробностях дела. Действительно, вчерашнее предложение княжны мужикам хлеба, ее объяснение с Дроном и с сходкою так испортили дело, что Дрон окончательно сдал ключи, присоединился к мужикам и не являлся по требованию Алпатыча и что поутру, когда княжна велела закладывать, чтобы ехать, мужики вышли большой толпой к амбару и выслали сказать, что они не выпустят княжны из деревни, что есть приказ, чтобы не вывозиться, и они выпрягут лошадей. Алпатыч выходил к ним, усовещивая их, но ему отвечали (больше всех говорил Карп; Дрон не показывался из толпы), что княжну нельзя выпустить, что на то приказ есть; а что пускай княжна остается, и они по старому будут служить ей и во всем повиноваться.
В ту минуту, когда Ростов и Ильин проскакали по дороге, княжна Марья, несмотря на отговариванье Алпатыча, няни и девушек, велела закладывать и хотела ехать; но, увидав проскакавших кавалеристов, их приняли за французов, кучера разбежались, и в доме поднялся плач женщин.
– Батюшка! отец родной! бог тебя послал, – говорили умиленные голоса, в то время как Ростов проходил через переднюю.
Княжна Марья, потерянная и бессильная, сидела в зале, в то время как к ней ввели Ростова. Она не понимала, кто он, и зачем он, и что с нею будет. Увидав его русское лицо и по входу его и первым сказанным словам признав его за человека своего круга, она взглянула на него своим глубоким и лучистым взглядом и начала говорить обрывавшимся и дрожавшим от волнения голосом. Ростову тотчас же представилось что то романическое в этой встрече. «Беззащитная, убитая горем девушка, одна, оставленная на произвол грубых, бунтующих мужиков! И какая то странная судьба натолкнула меня сюда! – думал Ростов, слушяя ее и глядя на нее. – И какая кротость, благородство в ее чертах и в выражении! – думал он, слушая ее робкий рассказ.
Когда она заговорила о том, что все это случилось на другой день после похорон отца, ее голос задрожал. Она отвернулась и потом, как бы боясь, чтобы Ростов не принял ее слова за желание разжалобить его, вопросительно испуганно взглянула на него. У Ростова слезы стояли в глазах. Княжна Марья заметила это и благодарно посмотрела на Ростова тем своим лучистым взглядом, который заставлял забывать некрасивость ее лица.
– Не могу выразить, княжна, как я счастлив тем, что я случайно заехал сюда и буду в состоянии показать вам свою готовность, – сказал Ростов, вставая. – Извольте ехать, и я отвечаю вам своей честью, что ни один человек не посмеет сделать вам неприятность, ежели вы мне только позволите конвоировать вас, – и, почтительно поклонившись, как кланяются дамам царской крови, он направился к двери.
Почтительностью своего тона Ростов как будто показывал, что, несмотря на то, что он за счастье бы счел свое знакомство с нею, он не хотел пользоваться случаем ее несчастия для сближения с нею.
Княжна Марья поняла и оценила этот тон.
– Я очень, очень благодарна вам, – сказала ему княжна по французски, – но надеюсь, что все это было только недоразуменье и что никто не виноват в том. – Княжна вдруг заплакала. – Извините меня, – сказала она.
Ростов, нахмурившись, еще раз низко поклонился и вышел из комнаты.


– Ну что, мила? Нет, брат, розовая моя прелесть, и Дуняшей зовут… – Но, взглянув на лицо Ростова, Ильин замолк. Он видел, что его герой и командир находился совсем в другом строе мыслей.
Ростов злобно оглянулся на Ильина и, не отвечая ему, быстрыми шагами направился к деревне.
– Я им покажу, я им задам, разбойникам! – говорил он про себя.
Алпатыч плывущим шагом, чтобы только не бежать, рысью едва догнал Ростова.
– Какое решение изволили принять? – сказал он, догнав его.
Ростов остановился и, сжав кулаки, вдруг грозно подвинулся на Алпатыча.
– Решенье? Какое решенье? Старый хрыч! – крикнул он на него. – Ты чего смотрел? А? Мужики бунтуют, а ты не умеешь справиться? Ты сам изменник. Знаю я вас, шкуру спущу со всех… – И, как будто боясь растратить понапрасну запас своей горячности, он оставил Алпатыча и быстро пошел вперед. Алпатыч, подавив чувство оскорбления, плывущим шагом поспевал за Ростовым и продолжал сообщать ему свои соображения. Он говорил, что мужики находились в закоснелости, что в настоящую минуту было неблагоразумно противуборствовать им, не имея военной команды, что не лучше ли бы было послать прежде за командой.
– Я им дам воинскую команду… Я их попротивоборствую, – бессмысленно приговаривал Николай, задыхаясь от неразумной животной злобы и потребности излить эту злобу. Не соображая того, что будет делать, бессознательно, быстрым, решительным шагом он подвигался к толпе. И чем ближе он подвигался к ней, тем больше чувствовал Алпатыч, что неблагоразумный поступок его может произвести хорошие результаты. То же чувствовали и мужики толпы, глядя на его быструю и твердую походку и решительное, нахмуренное лицо.
После того как гусары въехали в деревню и Ростов прошел к княжне, в толпе произошло замешательство и раздор. Некоторые мужики стали говорить, что эти приехавшие были русские и как бы они не обиделись тем, что не выпускают барышню. Дрон был того же мнения; но как только он выразил его, так Карп и другие мужики напали на бывшего старосту.
– Ты мир то поедом ел сколько годов? – кричал на него Карп. – Тебе все одно! Ты кубышку выроешь, увезешь, тебе что, разори наши дома али нет?
– Сказано, порядок чтоб был, не езди никто из домов, чтобы ни синь пороха не вывозить, – вот она и вся! – кричал другой.
– Очередь на твоего сына была, а ты небось гладуха своего пожалел, – вдруг быстро заговорил маленький старичок, нападая на Дрона, – а моего Ваньку забрил. Эх, умирать будем!
– То то умирать будем!
– Я от миру не отказчик, – говорил Дрон.
– То то не отказчик, брюхо отрастил!..
Два длинные мужика говорили свое. Как только Ростов, сопутствуемый Ильиным, Лаврушкой и Алпатычем, подошел к толпе, Карп, заложив пальцы за кушак, слегка улыбаясь, вышел вперед. Дрон, напротив, зашел в задние ряды, и толпа сдвинулась плотнее.
– Эй! кто у вас староста тут? – крикнул Ростов, быстрым шагом подойдя к толпе.
– Староста то? На что вам?.. – спросил Карп. Но не успел он договорить, как шапка слетела с него и голова мотнулась набок от сильного удара.
– Шапки долой, изменники! – крикнул полнокровный голос Ростова. – Где староста? – неистовым голосом кричал он.
– Старосту, старосту кличет… Дрон Захарыч, вас, – послышались кое где торопливо покорные голоса, и шапки стали сниматься с голов.
– Нам бунтовать нельзя, мы порядки блюдем, – проговорил Карп, и несколько голосов сзади в то же мгновенье заговорили вдруг:
– Как старички пороптали, много вас начальства…
– Разговаривать?.. Бунт!.. Разбойники! Изменники! – бессмысленно, не своим голосом завопил Ростов, хватая за юрот Карпа. – Вяжи его, вяжи! – кричал он, хотя некому было вязать его, кроме Лаврушки и Алпатыча.
Лаврушка, однако, подбежал к Карпу и схватил его сзади за руки.
– Прикажете наших из под горы кликнуть? – крикнул он.
Алпатыч обратился к мужикам, вызывая двоих по именам, чтобы вязать Карпа. Мужики покорно вышли из толпы и стали распоясываться.
– Староста где? – кричал Ростов.
Дрон, с нахмуренным и бледным лицом, вышел из толпы.
– Ты староста? Вязать, Лаврушка! – кричал Ростов, как будто и это приказание не могло встретить препятствий. И действительно, еще два мужика стали вязать Дрона, который, как бы помогая им, снял с себя кушан и подал им.
– А вы все слушайте меня, – Ростов обратился к мужикам: – Сейчас марш по домам, и чтобы голоса вашего я не слыхал.
– Что ж, мы никакой обиды не делали. Мы только, значит, по глупости. Только вздор наделали… Я же сказывал, что непорядки, – послышались голоса, упрекавшие друг друга.
– Вот я же вам говорил, – сказал Алпатыч, вступая в свои права. – Нехорошо, ребята!
– Глупость наша, Яков Алпатыч, – отвечали голоса, и толпа тотчас же стала расходиться и рассыпаться по деревне.
Связанных двух мужиков повели на барский двор. Два пьяные мужика шли за ними.
– Эх, посмотрю я на тебя! – говорил один из них, обращаясь к Карпу.
– Разве можно так с господами говорить? Ты думал что?
– Дурак, – подтверждал другой, – право, дурак!
Через два часа подводы стояли на дворе богучаровского дома. Мужики оживленно выносили и укладывали на подводы господские вещи, и Дрон, по желанию княжны Марьи выпущенный из рундука, куда его заперли, стоя на дворе, распоряжался мужиками.
– Ты ее так дурно не клади, – говорил один из мужиков, высокий человек с круглым улыбающимся лицом, принимая из рук горничной шкатулку. – Она ведь тоже денег стоит. Что же ты ее так то вот бросишь или пол веревку – а она потрется. Я так не люблю. А чтоб все честно, по закону было. Вот так то под рогожку, да сенцом прикрой, вот и важно. Любо!
– Ишь книг то, книг, – сказал другой мужик, выносивший библиотечные шкафы князя Андрея. – Ты не цепляй! А грузно, ребята, книги здоровые!
– Да, писали, не гуляли! – значительно подмигнув, сказал высокий круглолицый мужик, указывая на толстые лексиконы, лежавшие сверху.

Ростов, не желая навязывать свое знакомство княжне, не пошел к ней, а остался в деревне, ожидая ее выезда. Дождавшись выезда экипажей княжны Марьи из дома, Ростов сел верхом и до пути, занятого нашими войсками, в двенадцати верстах от Богучарова, верхом провожал ее. В Янкове, на постоялом дворе, он простился с нею почтительно, в первый раз позволив себе поцеловать ее руку.
– Как вам не совестно, – краснея, отвечал он княжне Марье на выражение благодарности за ее спасенье (как она называла его поступок), – каждый становой сделал бы то же. Если бы нам только приходилось воевать с мужиками, мы бы не допустили так далеко неприятеля, – говорил он, стыдясь чего то и стараясь переменить разговор. – Я счастлив только, что имел случай познакомиться с вами. Прощайте, княжна, желаю вам счастия и утешения и желаю встретиться с вами при более счастливых условиях. Ежели вы не хотите заставить краснеть меня, пожалуйста, не благодарите.
Но княжна, если не благодарила более словами, благодарила его всем выражением своего сиявшего благодарностью и нежностью лица. Она не могла верить ему, что ей не за что благодарить его. Напротив, для нее несомненно было то, что ежели бы его не было, то она, наверное, должна была бы погибнуть и от бунтовщиков и от французов; что он, для того чтобы спасти ее, подвергал себя самым очевидным и страшным опасностям; и еще несомненнее было то, что он был человек с высокой и благородной душой, который умел понять ее положение и горе. Его добрые и честные глаза с выступившими на них слезами, в то время как она сама, заплакав, говорила с ним о своей потере, не выходили из ее воображения.