Singleton (Превод от английски на главата "Singleton" от книгата "Pro Objective-C Design Patterns for

Един единствен клас в обектно-ориентирано приложение винаги връща един и същ екземпляр от себе си. Той осигурява глобална точка за достъп за ресурсите, които обектът на класа излага. Модел с тази функционалност се нарича Singleton. В тази глава ще проучим внедряването и използването на модела Singleton в Objective-C и рамката Cocoa Touch в iOS.

Какво представлява моделът Singleton?

Моделът Singleton е може би най-простият от моделите. Целта му е да направи обекта на класа единствения екземпляр в системата. На първо място, трябва да забраните създаването на повече от едно копие на класа. За да направите това, можете да използвате фабричен метод (Глава 4), който трябва да е статичен, защото няма смисъл да позволявате на екземпляр на клас да създаде друг единичен екземпляр. Фигура 7-1 показва структурата на прост единичен клас.

превод
Фигура 7-1. Статичната структура на модела Singleton.

Статичният uniqueInstance е единичен екземпляр на класа Singleton, представен като променлива на класа, която статичният метод sharedInstance ще върне на клиентите. Обикновено sharedInstance ще провери дали uniqueInstance е създаден. Ако не, методът ще го създаде, преди да се върне.

Забележка.Singleton Pattern:Проверява, че има само един екземпляр на клас и предоставя единична точка за достъп до него.* *Оригинална дефиниция, както е предоставена в "Design Patterns" GoF (Addison-Wesley, 1994).

Кога можете да използвате модела Singleton?

Има смисъл да се обмисли използването на модела Singleton, ако:

  • Системата може да имасамо един екземпляр от класа, който трябва да бъде достъпен чрез добре позната точка за достъп, като фабричен метод.
  • Един екземпляр може да бъде разширен само чрез наследяване и клиентският код няма да се повреди от използването на разширения обект.

Моделът Singleton предоставя познат начин за създаване на уникален екземпляр с удобен начин за достъп до споделен ресурс. Методът за използване на препратка към статичен глобален обект не предотвратява създаването на друг екземпляр от същия клас. Подходът на метода на класа, въпреки че може да осигури глобална точка за достъп, му липсва гъвкавостта на разделянето на кода. Статичната глобална променлива съдържа една препратка към екземпляр на клас. Друг клас или метод, който има достъп до него, всъщност споделя същото копие с други класове или методи, които използват същата променлива. Изглежда като това, от което се нуждаем. Всичко изглежда страхотно, стига една и съща глобална променлива да се използва в цялото приложение. По този начин всъщност моделът Singleton не е необходим. Но почакай! Ами ако някой от вашия екип дефинира същата глобална променлива в кода като вашата? Тогава ще има две копия на един и същ глобален обект в едно и също приложение - така че глобалната променлива всъщност не решава проблема.

Методът на клас предоставя възможност за споделяне, без да се създава обект от него. Един екземпляр на ресурс се поддържа в метод на клас. Този подход обаче има недостатъка, че е гъвкав в случай, че класът изисква наследяване, за да осигури повече функционалност.

Класът Singleton може да гарантира единична, последователна и добре позната точка за достъп за създаване и достъп до един обект на клас.Моделът осигурява такава гъвкавост, че всеки от неговите подкласове може да замени метода за инстанциране и да има пълен контрол върху създаването на обекта на класа, без да променя клиентския код. Още по-добре, внедряването на метода за инстанциране може да обработва динамично създаването на обект. Действителният тип клас може да бъде определен по време на изпълнение, за да се гарантира, че се създава правилният обект. Тази техника ще бъде обсъдена допълнително.

Има и гъвкава версия на модела Singleton, в която фабричният метод винаги връща един и същ екземпляр, но можете да разпределите и инициализирате други екземпляри в допълнение. Тази по-малко ограничителна версия на модела се обсъжда в раздела "Използване на клас NSFileManager" по-късно в тази глава.

Имплементация на Singleton в Objective-C

Има някои неща, за които трябва да помислите, за да проектирате правилно класа Singleton. Първият въпрос, който трябва да зададете, е как да се уверите, че може да бъде създадено само едно копие на клас? Клиентите в приложение, написано на други обектно-ориентирани езици, като C++ и Java, не могат да създадат обект от клас, ако неговият конструктор е деклариран като частен. А как стоят нещата в Objective-C?

Всеки метод на Objective-C е публичен и самият език е динамично въведен, така че всеки клас може да изпрати съобщение до друг (извикване на метод в C++ и Java) без значителни проверки по време на компилиране (само предупреждения на компилатора, ако методът не е деклариран). Освен това рамката на Cocoa (включително Cocoa Touch) използва управление на паметта с преброяване на препратки, за да поддържа живота на обект в паметта. Всички тези функции правят внедряването на Singleton в Objective-C доста трудно. В оригиналния пример на Design Patterns, примерътC++ Singleton изглеждаше като листинг 7-1.

Списък 7-1. Оригиналният C++ пример на модела Singleton от Design Patterns.

Както е описано в книгата, внедряването в C++ е просто и ясно. В статичния метод Instance() статичната променлива _instance се проверява срещу 0 (NULL). Ако е така, нов обект от клас Singleton се създава и след това се връща. Някои от вас може да си помислят, че версията на Objective-C не се различава много от своя аналог и трябва да изглежда като листинги 7-2 и 7-3.

Списък 7-2. Декларация на клас Singleton в Singleton.h

Списък 7-3. Внедряване на метода sharedInstance Singleton.m

Ако е така, тогава това е много проста глава и вече сте научили един модел, имплементиран в Objective-C. Всъщност има няколко трудности, които трябва да бъдат преодолени, за да бъде изпълнението достатъчно стабилно, за да се използва в реално приложение. Ако е необходима "строга" версия на модела Singleton, тогава има два основни проблема, които трябва да бъдат решени, така че да може да се използва в реален код:

  • Повикващият не може да създаде обект Singleton чрез други средства за разпределение. В противен случай ще бъде възможно да се създадат множество екземпляри на класа Singleton.
  • Ограниченията за създаване на обект Singleton трябва да са в съответствие с модела за преброяване на препратки.

Списък 7-4 показва изпълнение, което е близко до това, към което се стремим. Кодът е доста дълъг, така че ще го разделим на няколко части за удобство на обсъждането.

Списък 7-4. По-подходящо изпълнение на Singleton в Objective-C

Вътре в метода sharedInstance, точно както в първия пример, той първо проверява дали единственият екземпляр на класа е създаден, в противен случай се създава нов и се връща. Но този път тойизвиква [[super allocWithZone:NULL] init], за да създаде нов екземпляр, вместо да използва други методи като alloc. Защо супер, а не себе си? Това е така, защото методите за разпределение на базов обект в нашия клас са заменени, така че трябва да „заемем“ тази функционалност от базовия клас, в този случай NSObject, за да ни помогне да направим задачите за разпределяне на памет на ниско ниво вместо нас.

Има няколко метода, свързани с управлението на паметта в класа Singleton, върху които трябва да помислите. Методът allocWithZone:(NSZone *)zone просто връща екземпляр на класа, който се връща от метода sharedInstance. В рамката Cocoa Touch, когато се извика методът на класа allocWithZone:(NSZone *)zone, паметта за екземпляра ще бъде разпределена, броят на препратките му ще бъде зададен на 1 и екземплярът ще бъде върнат. Видяхме, че методът alloc се използва в много ситуации; всъщност alloc извиква allocWithZone: със зоната, зададена на NULL, за да разпредели памет за екземпляр в зоната по подразбиране. Подробностите за създаването на обекти и управлението на паметта са извън обхвата на тази книга. Можете да се обърнете към документацията за допълнителни разяснения.

Други методи, като retain, release и autorelease, се отменят, за да се гарантира, че не правят нищо (в модела за управление на паметта с преброяване на препратки), освен return self. Методът retainCount връща NSUIntegerMax (4 294 967 295), за да предотврати премахването на екземпляра от паметта по време на живота на приложението.

Защо да извикваме retain на Singleton?Може да сте забелязали, че извикваме retain на Singleton обекта, върнат от sharedInstance метода в allocWithZone:, но retain се отменя и всъщност се игнорира в нашата реализация. Вземайки предвид това,ще можем да направим класа Singleton по-малко "строг" (т.е. ще бъде възможно да се разпредели памет за допълнителни инстанции и да се инициализират, но фабричният метод sharedInstance винаги връща една и съща инстанция или обектът Singleton става разрушим). Подкласовете могат отново да заменят методите retain, release и autorelease, за да осигурят подходящо изпълнение на управлението на паметта. Гъвкава версия на модела Singleton се обсъжда в раздела "Използване на клас NSFileManager" по-късно в тази глава.

Вече разгледахме достатъчно подробно как трябва да изглежда моделът Singleton в Objective-C. Има обаче още нещо, за което трябва да помислите внимателно, преди да можете да го използвате. Ами ако искаме да наследим от оригиналния клас Singleton? Нека да видим как да го направим.

Наследство от Сингълтън

Извикването на alloc се пренасочва към super, така че класът NSObject се грижи за разпределянето на памет за обекта. Ако наследим от класа Singleton без модификация, тогава върнатият екземпляр винаги ще бъде от тип Singleton. Тъй като класът Singleton отменя всички методи, свързани с инстанциране, не е лесно да се наследи от него. Но ние сме късметлии; можете да използвате някои от функциите на Foundation, за да създадете всеки обект въз основа на неговия тип клас. Един от тях е id NSAllocateObject (Class aClass, NSUInteger extraBytes, NSZone *zone) . Следователно, ако искаме да инстанцираме обект от клас, наречен "Singleton", можем да направим следното:

Първият параметър е типът на класа Singleton. Вторият параметър е за произволен брой допълнителни байтове за индексирани променливи на екземпляр, който винаги е 0. Третият параметър е обозначението на разпределената зона на паметта; почти винагиизползва се зоната по подразбиране (параметърът е NULL). Следователно можете да създадете екземпляр на всякакви обекти, като използвате тази функция, като знаете типа на класа. Какво трябва да се направи, за да се наследи от класа Singleton? Нека си припомним, че оригиналната версия на метода sharedInstance изглежда така:

Ако използвате трика NSAllocateObject, за да създадете екземпляр, той става следният:

Сега няма значение дали създаваме класа Singleton или някой от неговите подкласове, тази версия ще направи всичко правилно.

безопасност на резбата

Класът Singleton в примера е добър само за обща употреба. Ако трябва да използвате единичен обект в многонишкова среда, тогава трябва да го направите безопасен за нишки. Това може да стане чрез вмъкване на блокове @synchronized() или чрез използване на екземпляри на NSLock около проверка за нула за статична променлива на екземпляра sharedSingleton_. Ако има някакви други свойства, които също трябва да бъдат защитени, тогава можете да ги направите атомни.

Използване на Singletons в Cocoa Touch Framework

Докато четете документацията на Cocoa Touch Framework, ще срещнете много различни единични класове. Ще говорим за някои от тях в този раздел - UIApplication, UIAccelerometer и NSFileManager.

Използване на класа UIApplication

Един от най-използваните сингълтън класове в рамката е класът UIApplication. Той осигурява централна точка за контрол и координация за iOS приложения.

Всяко приложение има един екземпляр на UIApplication. Създава се като единичен обект от функцията UIApplicationMain при стартиране на приложението и е достъпен по време на изпълнение чрез метода на клас sharedApplication.

Обектът UIApplication изпълнява много задачи за поддържане на програмата, включително първоначалнатамаршрутизиране на входящи потребителски съобщения и изпращане на съобщения за действие за обекти от класа UIControl до съответните целеви обекти. Той поддържа списък на всички отворени обекти на UIWindow. Обектът на приложението е свързан с обекта на делегат на приложението UIApplicationDelegate, който се информира за всички значими събития по време на изпълнение на програмата, като например стартиране, предупреждения за недостиг на памет, прекратяване на приложение и работещи процеси във фонов режим. Манипулаторите за тези събития позволяват на делегата да персонализира поведението на приложението.

Използване на класа UIAccelerometer
Използване на класа NSFileManager

Класът NSFileManager някога е бил "строга" реализация на модела Singleton преди Mac OS X 10.5 и iOS 2.0. Извикването на неговия init метод не прави нищо и единственият му екземпляр може да бъде създаден и достъпен само чрез метода на класа defaultManager. Въпреки това, тъй като изпълнението на singleton не е безопасно за нишки, трябва да се създадат нови екземпляри на NSFileManager, за да се гарантира тази безопасност. Този подход се разглежда като по-гъвкава реализация на Singleton, при която фабричният метод винаги връща един и същ екземпляр, но могат да бъдат разпределени и инициализирани и допълнителни екземпляри.

Ако искате да приложите "строг" сингълтон, имате нужда от изпълнение, подобно на примера, описан в предишните раздели. В противен случай не отменяйте allocWithZone: и други свързани методи.

Моделът Singleton е един от най-широко използваните шаблони в почти всеки тип приложение, а не само в iOS. Singleton може да бъде полезен, когато имате нужда от централизиран клас за координиране на приложни услуги. Тази глава бележи края на тази част за създаване на обекти. В следващата частще видим някои дизайнерски модели, които се фокусират върху адаптиране/консолидиране на обекти с различни интерфейси.