Классы Cocoa

Для правильного применения каркасов Cocoa Touch требуется ясно понимать, каким образом в них организованы классы. Организация классов в среде Cocoa зависит от определенных языковых средств Objective-C, представленных в этой главе. Кроме того, в ней дается краткий обзор некоторых из наиболее употребительных служебных классов Cocoa, а также обсуждается корневой класс Cocoa.

 

Наследование

В среде Cocoa, по существу, предоставляется большое разнообразие объектов с заранее известным поведением, определенным требуемым образом. Например, объекту типа UIButton известно, как отобразить кнопку и как отреагировать на нажатие пальцем, производимое пользователем, а объекту типа UITextField известно, как отобразить поле редактируемого текста, обратиться к клавиатуре и принять вводимые с нее данные.

Зачастую стандартное поведение или внешний вид объекта, обеспечиваемые средой Cocoa, не вполне отвечают искомым, и поэтому объект требует специальной настройки. Однако это совсем не означает, что непременно необходимо наследование! Классы Cocoa наделены немалым количеством методов, которые можно вызывать, а также свойств, которые можно устанавливать таким образом, чтобы специально настроить экземпляр класса. Именно к этой мере и следует прибегать в первую очередь. Для того чтобы выяснить, готовы ли экземпляры классов выполнить то, что от них требуется, следует всегда обращаться за справкой к документации. Например, в документации на класс UIButton поясняется, что надпись на кнопке, цвет надписи, внутреннее изображение и много других свойств и видов поведения кнопки можно установить, не прибегая к наследованию.

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

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

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

Рассмотрим для примера класс UlView. В среде Cocoa Touch имеется немало встроенных подклассов, производных от класса UlView (UIButton, UITextField и т.д.), воспроизводящих и ведущих себя нужным образом и редко требующих своего наследования. С другой стороны, можно создать свой подкласс UlView, воспроизводящий себя совершенно по-новому. Для того чтобы воспроизвести представление типа UlView, достаточно вызвать метод drawRect: из класса UlView. Для того чтобы сделать то же самое совершенно по-другому, придется выполнить наследование класса UlView и реализовать метод в drawRect: в его подклассе. По этому поводу в документации говорится следующее: “Реализуйте этот метод, если в представлении воспроизводится особое содержимое” Слово “реализуйте” может означать лишь одно: реализацию в подклассе, т.е. наследование и замещение. Следовательно, в документации подчеркивается, что наследование класса UlView требуется лишь для того, чтобы воспроизвести содержимое по-своему и совершенно иначе.

Допустим, требуется создать окно, содержащее горизонтальную линию. В среде Cocoa отсутствует интерфейсный элемент управления для горизонтальной линии, и поэтому ее придется начертить самостоятельно, прибегнув к наследованию класса UlView, как поясняется ниже.

1. Перейдя к примеру проекта Empty Window, выберите команду меню File^New^File и укажите сначала класс Cocoa Touch Objective-C, а затем конкретный подкласс, производный от класса UlView. Присвойте новому классу имя MyHorizLine. При этом в среде Xcode автоматически создаются файлы MyHorizLine .m и MyHorizLine . h. Непременно сделайте их частью цели приложения.

2. Замените в файле MyHorizLine .m содержимое раздела следующим фрагментом кода без дополнительных пояснений:

- (id)initWithCoder:(NSCoder *)decoder {

self = [super initWithCoder:decoder];

if (self) {

self.backgroundColor « [UlColor clearColor];

}

return self;

}

- (void)drawRect:(CGRect)rect {

CGContextRef с = UIGraphicsGetCurrentContext ();

CGContextMoveToPoint(c, 0, 0);

CGContextAddLineToPoint(c, self.bounds.size.width, 0);

CGContextStrokePath(c);

}

3. Отредактируйте раскадровку. Найдите класс UlView в библиотеке Object, где он просто называется View, и перетащите его на объект View, находящийся на холсте. При желании можете изменить его размеры, чтобы сделать менее высоким.

4. Если класс UlView, который вы только что перетащили на холст, все еще выделен, воспользуйтесь инспектором идентичности, чтобы изменить этот класс на MyHorizLine.

 

Соберите и выполните приложение в симуляторе. В итоге должна появиться горизонтальная линия, соответствующая местоположению верхнего края экземпляра класса MyHorizLine в nib-файле. Таким образом, представление воспроизвело себя в виде горизонтальной линии, поскольку для этого было выполнено наследование его класса UlView.

Данный пример был начат с пустого класса UlView, не имевшего функциональных возможностей для рисования. Именно поэтому не потребовалось делать вызов метода суперкласса. По умолчанию в реализации метода drawRect: из класса UlView ничего не делается, но можно было бы также выполнить вывод подкласса встроенного класса UlView, чтобы изменить уже имеющийся порядок его воспроизведения. Например, из документации на класс UILabel следует, что для одной и той же цели в нем имеются два разных метода, drawTextlnRect: и textRectForBounds:limitedToNumberOfLinesпричем вызывать их непосредственно не следует, а достаточно лишь заместить их в подклассах. Следовательно, эти методы будут вызываться автоматически из Cocoa при воспроизведении метки. Итак, выполнив наследование класса UILabel, можно реализовать эти методы в подклассе, чтобы изменить порядок воспроизведения метки.

Рассмотрим еще один пример из одного из моих приложений, где я выполняю наследование класса UILabel и замещаю метод drawTextlnRect:, чтобы воспроизвести метку в собственной прямоугольной рамке. Как поясняется в документации: “В замещаемом методе можно дополнительно настроить текущий [графический] контекст и затем сделать вызов метода суперкласса для конкретного воспроизведения [текста]” Ниже поясняется, как это делается.

1. Создайте новый файл класса в проекте Empty Window, выполните наследование класса UILabel и присвойте новому подклассу имя MyBoundedLabel.

2. Введите следующий фрагмент кода в разделе реализации файла MyBoundedLabel. m:

- (void)drawTextlnRect:(CGRect)rect {

CGContextRef context = UIGraphicsGetCurrentContext();

CGContextStrokeRect(context, CGRectInset(self.bounds, 1.0, 1.0));

[super drawTextlnRect:CGRectlnset(rect, 5.0, 5.0)];

}

3. Отредактируйте раскадровку. Введите класс UILabel в интерфейс, изменив его имя на MyBoundedLabel в инспекторе идентичности.

Соберите и выполните приложение. В итоге появится метка с текстовой надписью в прямоугольной рамке. (Для удобства сравнения воспроизводимых по-разному меток полезно ввести в интерфейс похожую метку из класса UILabel.)

Наследование редко выполняется в среде Cocoa еще и потому, что во многих встроенных в эту среду классах применяется механизм делегатов (см. главу 11) как один из способов специальной настройки их поведения. Вряд ли стоит выполнять наследование класса UIApplication (т.е. класса одиночного разделяемого экземпляра приложения) только для того, чтобы отреагировать на окончание запуска приложения, поскольку механизм делегатов предоставляет возможность сделать это (с помощью метода application: didFinishLau nchingWithOptions:). Именно поэтому в шаблонах предоставляется класс AppDelegate, который не является подклассом, производным от класса UIApplication. С другой стороны, если требуется произвести сложную в некотором роде специальную настройку основного поведения приложения при передаче сообщений о происходящих событиях, можно выполнить наследование класса UIApplication, чтобы заместить метод sendEvent:. О том же говорится и в документации, но там же подчеркивается, что делается это крайне редко.

 

Для наследования класса UIApplication требуется заменить значение nil третьего аргумента в вызове функции UIApplicationMain () из файла main .m на имя типа NSString получаемого в итоге подкласса. В противном — случае экземпляр подкласса, производного от класса UIApplication, не будет получен как разделяемый экземпляр приложения (см. главу 6).


 

 

 

Добавить комментарий