ПРОГРАММИРОВАНИЕ ПОД IPHONE, IPAD OBJECTIVE-C Часть 1

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

Наследование в Objective-C является одним из важнейших принципов програмирования. Оно позволяет значительно расширить функциональность классов которые мы создаем при этом упрощает процесс программирования.

В первую очередь рассмотри что это такое и как работает.

Что такое наследование? В программировании это означает практически то же самое что и в жизни, только здесь вместо денег мы наследуем функции (методы) и переменные. Наследование передается от вышестоящего класса к нижестоящему классу и только в одном направлении от суперкласса к дочернему классу (подклассу).

При этом есть ряд ограничений и правил которые мы сейчас и рассмотрим.

Вот как это выглядит на схеме

Class1 называется корневым классом, т.к. у него нет суперкласса и он стоит на самом вверху иерархической цепочки так-же в схеме видно кто для кого является суперклассом или подклассом. Зелеными стрелками обозначено направление наследования, оно передается только вниз от суперкласса к подклассу.  Это значит что все функции (методы), переменные из Class1 наследуются вClass2, затем из Class2, все это наследуется в Class3. При этом есть два важных момента:

  1. Kлассу Class3 передается “наследство” не только от Class2, но и от Class1. Если бы у Class3 был подкласс Class4 ему бы наследовались функции и переменные всех трех вышестоящих классов.
  2. Поскольку наследование передается только в одну сторону от суперкласса к подклассу, Class1 не может воспользоваться функциями и переменными нижестоящих классов Class2 и Class3. Так-же Class2 не может наследовать Class3.

Давайте посмотрим как это выглядит на практике. Создаем новый проект, на базе шаблона View-based Application с именемinheritance. И добавим в него новый класс. Для этого щелкните правой кнопкой на группе inheritance выберите New File далее как на скринах.

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

Дальше присвоим имя нашему классу — Class1

 

В итоге должно появиться два файла заголовочный Class1.h и исполняемый файл Class1.m. Точно так же создайте еще один класс c именем Class2.

В Xcode4 есть очень удобная функция просмотра иерархии классов, в ней видно что наши классы Class1 и Class2 являются подклассом NSObject. В нижней части можно настраивать фильтр для отображения.  Если кликать по классам в окне появиться код этого класса. Как видите все очень удобно.

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

<code data-result="[object Object]">#import &lt;Foundation/Foundation.h&gt;

@interface Class1 : NSObject {

}

@end</code>

@intarface – в этой секции описывается класс, его переменные и функции, секция закрывается директивой @end.

Class1 – является именем класса который создаем.

NSObject – А вот это то что нам нужно, именно здесь после двоеточия указывается суперкласс (его имя). NSObject это основной класс от которого наследуются все классы в Objective-C.

Теперь, когда мы разобрались как определять суперкласс давайте переназначим его у Class2. Для этого в в файле Class2.h вместоNSObject пропишите Class1 (не забыв перед этим импортировать класс Class1). Если посмотрите на иерархию классов она должна видоизмениться и выглядеть так:

Добавим в только что созданные классы переменные и методы. В Class1 добавим переменную var1 и метод setVariableприсваивающий значение  для этой переменной:

Class1.h:

<code data-result="[object Object]">#import &lt;Foundation/Foundation.h&gt;

@interface Class1 : NSObject {
    int var1;
}

-(void) setVariable;

@end</code>

Class1.m:

<code data-result="[object Object]">#import "Class1.h"

@implementation Class1

-(void) setVariable{
    var1 = 5;
}

@end</code>

В классе Class2 добавим метод printVariable который будет выводить в консоль значение его единственной переменной.

Class2.h:

<code data-result="[object Object]">#import &lt;Foundation/Foundation.h&gt;
#import "Class1.h"

@interface Class2 : Class1 {

}

-(void) printVariable;

@end</code>

Class2.m:

<code data-result="[object Object]">#import "Class2.h"

@implementation Class2

-(void) printVariable{
    NSLog(@"var1 = %d", var1);
}

@end</code>

Теперь мы можем использовать наши классы, их методы и переменные. Давайте сделаем это в файле inheritanceViewController.m. В первую очередь импортируем класс Class2. Затем изменим метод viewDidLoad:

<code data-result="[object Object]">- (void)viewDidLoad
{
    [super viewDidLoad];

    Class2 *cl2 = [[Class2 alloc] init]; 
    [cl2 setVariable];
    [cl2 printVariable];
    [cl2 release];
}</code>

Здесь мы создали экземпляр класса Class2, затем ниже обращаясь к только  что созданному экземпляру cl2 и вызываем нужные нам методы. Вспомните, что класс Class2 не содержит в себе метода setVariable однако он исправно запускается и выполняются благодаря механизму наследования.  Чтобы убедиться в этом запустите проект и в консоли вы увидите Var1 = 5.

В данном случае, при вызове метода  setVariable у cl2 компилятор начинает поиск этого метода сначала в классе Class2, затем, не найдя, переходит к его родительскому классу Class1 и уже найдя там нужный метод выполняет его. После этого точно так же идет поиск метода print только он находится сразу без наследования. Если компилятор не найдет метода, который вы хотите вызвать ни в одном из классов — Xcode выдаст ошибку и проект не будет скомпилирован.

Методы родительского класса можно переопределять. К примеру, добавим в класс Class2 метод setVariable (при этом в интерфейсе класса его не нужно объявлять). Только в его реализации напишите var1 = 10;. Если теперь скомпилировать проект — в консоль будет выведена строка Var1 = 10. Это произойдет потому, что компилятор в первую очередь ищет вызываемый метод в самой нижней ветке иерархии классов. И продолжается этот поиск пока не будет найден такой метод. То есть, когда компилятор находит нужный setVariable в Class2 — вместо дальнейшего поиска выполняется тело найденного метода.

Обратите внимание на строку Class2 *cl2 = [[Class2 alloc] init]; Здесь мы вызываем два системных метода alloc и init, этих методов мы не писали, как же компилятор их находит? Если вы усвоили вышеописанный материал то должны догадаться сами.

Метод init так же является ярким примером переопределения методов родительского класса. Когда вы создаете новый класс, родительским классом которого является NSObject — Xcode сам добавляет в реализацию вашего нового класса переопределение метода init.

<code data-result="[object Object]">- (id)init
{
    self = [super init];
    if (self) {
        // Initialization code here.
    }

    return self;
}</code>

В первой строчке этого метода (self = [super init];) выполняется  присваивание текущему объекту результата инициализации родительского класса. Затем следует проверка. Если метод init родительского класса выполнился успешно — для переменных текущего объекта можно указать какие-то значения.

Теперь полезная информация.

Зайдите в файл Class1.h и зажав альт кликните на NSObject, всплывет окно.

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

В нем нас интересует первая строка в ней написано от кого наследуется класс NSObject, как видим он не от кого не наследуется это корневой класс root. Для небольшой практики посмотрите на родительский класс контроллера представления и посмотрите от кого он наследуется.

Теперь попробуем нажать на вторую кнопочку.

Откроется заголовочный файл класса NSObject, здесь мы можем увидеть все его методы включая те самые alloc и init.

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

Подведем итог. Наследование это мощное средство позволяющее расширять существующее определение классов (это переменные и методы) используя ваши и системные классы.

Исходный код этого проекта можно скачать здесь.

Comments are closed.