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

Синглтон (Singleton)

Singleton в переводе с английского означает «одиночка». Такое имя ему дано не с проста. Дело в том, что в приложении может существовать максимум один экземпляр такого класса. Для чего это нужно? К примеру вы пишете игру, и у вас есть класс, который отвечает за количество очков, уровень игрока или количество жизней главного героя. Конечно, хранить эти данные можно в каждом классе, где используется объект, отвечающий за вышеперечисленные значения. Но почему бы для этих целей не создать отдельный класс, который будет доступен во всем приложении. Другим хорошим примером использования синглтона является аудиоплеер. Вам не нужно каждый раз писать код для проигрывания звука, создавать и убивать аудиоплеер. Достаточно это сделать в одном классе и обращаться к нему в нужный момент.

Ярким примером использования синглтона может быть AppDelegate. Мы уже работали с ним в примере Проверка соединения с Internet. Как я уже говорил, экземпляр этого класса всегда присутствует в программе и всегда один. Логично, что для воспроизведения звука или хранения общих значений приложения мы можем использовать его. Но это будет нарушать модель работы с данными, а если приложение большое — этот класс будет нагружаться кодом, что понижает его удобочитаемость и редактируемость.

Давайте посмотрим как это выглядит на практике. Для чистоты эксперемента нам следует создать приложение, в котором будет много контроллеров представления. Добавить в это приложение один класс, который будет выполнять роль синглтона и каждый контроллер будет изменять какую-то переменную этого класса (и выводить ее на экран). Но дело в том, что такой проект у нас уже есть, мы создавали его в примере Более детально о навигации. Скачайте код этого проекта и добавьте в него новый класс (точно так же как мы это делали в уроке наследования), только имя этому классу дайте MySingleton. Пока что это обычный класс, синглтоном его сделает один метод, который мы в него добавим.

MySingleton.h:

<code data-result="[object Object]">@interface MySingleton : NSObject {
    NSString *example;
}
@property (nonatomic, retain) NSString *example;

+(MySingleton *)sharedMySingleton;

@end</code>

MySingleton.m:

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

@implementation MySingleton

@synthesize example;

static MySingleton * sharedMySingleton = NULL;
+(MySingleton *)sharedMySingleton {
    if (!sharedMySingleton || sharedMySingleton == NULL) {
		sharedMySingleton = [MySingleton new];
	}
	return sharedMySingleton;
}

- (void)dealloc {
    self.example = nil;
    [super dealloc];
}

@end</code>

Как видно из кода, все задачи по контролю того, чтобы этот класс был единственным в программе берет на себя метод +(MySingleton *)sharedMySingleton; (почему этот метод с плюсом, а не минус, как мы привыкли я расскажу позже в уроках по управлению памятью). Кроме непосредственно метода, я добавил переменную строкового типа example. В ней мы будем хранить какое-то значение, которое будут изменять и отображать другие классы.

Теперь нам следовало бы импортировать класс MySingleton в каждый контроллер, в котором мы собераемся его вызывать. Но вместо этого, мы импортируем его в так называемый Precompiled Prefix Header. В каждом проекте присутствует файл PRODUCT_NAME-Prefix.pch (в нашем случае DetailNavigation-Prefix.pch). Чаще всего этот файл находится в папке Supporting Files. Он используется для ускорения компиляции кода. Давайте посмотрим как выглядит файл нашего проекта:

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

#ifndef __IPHONE_3_0
#warning "This project uses features only available in iPhone SDK 3.0 and later."
#endif

#ifdef __OBJC__
    #import &lt;UIKit/UIKit.h&gt;
    #import &lt;Foundation/Foundation.h&gt;
#endif</code>

Так вот, все библиотеки и классы, которые импортируются внутри условия #ifdef __OBJC__ … #endif будут автоматически импортированы во все классы проекта. То есть, нам следует написать вот так:

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

#ifndef __IPHONE_3_0
#warning "This project uses features only available in iPhone SDK 3.0 and later."
#endif

#ifdef __OBJC__
    #import &lt;UIKit/UIKit.h&gt;
    #import &lt;Foundation/Foundation.h&gt;
    #import "MySingleton.h"
#endif</code>

Теперь мы смело можем вызывать класс MySingleton из любого контроллера и не беспокоиться за то, что он не был импортирован в него. Здесь следует сделать небольшое отступление. У файлов Precompiled Prefix Header есть один небольшой недостаток. Дело в том, что если вы внесете какие-то изменения в класс, который импортировали в этот файл — весь проект будет перекомпилирован. То есть, если вы в класс MySingleton добавим еще один метод — при первом запуске приложения Xcode будет собирать его чуть дольше. Давайте это и сделаем. Перейдем в реализацию класса RootViewController и изменим метод viewDidLoad:

<code data-result="[object Object]">- (void)viewDidLoad
{
    [super viewDidLoad];
    self.title = @"Main page";

    [MySingleton sharedMySingleton].example = @"This is first value";
}</code>

Теперь кроме установки надписи на TopBar мы обращаемся к классу MySingleton с помощью добавленного в него метода и устанавливаем начальное значение переменной example. Попробуем прочитать и изменить это значение в классе PageOneViewController. Изменим его метод viewDidLoad:

<code data-result="[object Object]">- (void)viewDidLoad
{
    [super viewDidLoad];
    self.title = @"One";

    NSLog(@"%@", [MySingleton sharedMySingleton].example);
    [MySingleton sharedMySingleton].example = @"This is PageOneViewController";
}</code>

В результате выполнения этого кода в консоль будет выведена строка:

2011-08-11 09:56:36.780 DetailNavigation[971:11703] This is first value

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

MySingleton.h:

<code data-result="[object Object]">@interface MySingleton : NSObject {
    NSString *example;
}
@property (nonatomic, retain) NSString *example;

- (void)showExample;

+(MySingleton *)sharedMySingleton;

@end</code>

MySingleton.m:

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

@implementation MySingleton

@synthesize example;

static MySingleton * sharedMySingleton = NULL;
+(MySingleton *)sharedMySingleton {
    if (!sharedMySingleton || sharedMySingleton == NULL) {
		sharedMySingleton = [MySingleton new];
	}
	return sharedMySingleton;
}

- (void)showExample {
    NSLog(@"%@", example);
}

- (void)dealloc {
    self.example = nil;
    [super dealloc];
}

@end</code>

Задача метода showExample не сложная — вывести в консоль значение переменной example.

Вызвать этот метод я предлагаю в классе PageTwoViewController, в методе viewDidLoad:

<code data-result="[object Object]">- (void)viewDidLoad
{
    [super viewDidLoad];
    self.title = @"Two";

    [[MySingleton sharedMySingleton] showExample];
}</code>

После перехода на этот контроллер в консоль будет выведено новое значение переменной example.

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

 

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

Comments are closed.