Загрузка пакетов
Класс NSBundle обеспечивает методы для загрузки пакетов Какао. В этом разделе описываются основы пакета, загружающегося в приложении Какао. Также покрытый загружают пакеты некакао из приложения Какао. Этот материал важен для любого разработчика, использующего загружаемые пакеты в их приложении.
Загрузка пакетов какао с NSBundle
Класс NSBundle обеспечивает методы для загрузки исполняемого кода и ресурсов от пакетов Какао. Это обрабатывает все подробности загрузки, включая взаимодействие с Мужественным загрузчиком dyld
и загрузка символов Objective C во время выполнения Objective C.
Для получения информации об использовании NSBundle для загрузки ресурсов некода см. Руководство по программированию Ресурса.
Загрузка пакетов Какао состоит из пяти основных шагов:
Найдите пакет.
Создайте объект NSBundle представлять пакет.
Загрузите исполняемый код пакета.
Запросите пакет для его основного класса.
Инстанцируйте объекта основного класса.
Следующие разделы покрывают каждый из этих шагов подробно.
Определение местоположения пакетов
Ваше приложение может загрузить пакеты из любого расположения, но если они сохранены в стандартных расположениях, можно использовать функции и методы, предоставленные Какао для нахождения их легко.
Загружаемые пакеты, упаковывающиеся с Вашими приложениями, обычно включаются в комплекте приложений в Contents/PlugIns
. Для получения сменного каталога для пакета главного приложения используйте NSBundle’s builtInPlugInsPath
метод.
Этот фрагмент кода показывает, как использовать NSBundle для получения сменного каталога приложения, который можно назвать PlugIns
или Plug-ins
(прежний заменяет последнего):
NSBundle *appBundle; |
NSString *plugInsPath; |
appBundle = [NSBundle mainBundle]; |
plugInsPath = [appBundle builtInPlugInsPath]; |
Несмотря на то, что это не стандартное расположение, можно получить некоторое удобство путем хранения загружаемых пакетов в комплекте приложений Resources
каталог. Тогда можно использовать NSBundle’s pathsForResourcesOfType:inDirectory:
метод для нахождения их. Этот фрагмент кода находит все файлы и каталоги с расширением .bundle
в приложении Resources/PlugIns
каталог:
NSBundle *appBundle; |
NSArray *bundlePaths; |
appBundle = [NSBundle mainBundle]; |
bundlePaths = [appBundle pathsForResourcesOfType:@"bundle" |
inDirectory:@"PlugIns"]; |
Ваше приложение может также поддерживать пакеты в каталогах поддержки приложений в Library
каталог в многократных доменах: специфичный для пользователя (~/Library
), в масштабе всей системы (/Library
), сеть (/Network/Library
). Для поиска этих и других стандартных каталогов используйте NSSearchPathForDirectoriesInDomains
функция.
Этот фрагмент кода создает массив путей поиска для Вашего приложения для нахождения пакетов, которые можно тогда искать отдельные плагины:
NSString *appSupportSubpath = @"Application Support/KillerApp/PlugIns"; |
NSArray *librarySearchPaths; |
NSEnumerator *searchPathEnum; |
NSString *currPath; |
NSMutableArray *bundleSearchPaths = [NSMutableArray array]; |
// Find Library directories in all domains except /System |
librarySearchPaths = NSSearchPathForDirectoriesInDomains( |
NSLibraryDirectory, NSAllDomainsMask - NSSystemDomainMask, YES); |
// Copy each discovered path into an array after adding |
// the Application Support/KillerApp/PlugIns subpath |
searchPathEnum = [librarySearchPaths objectEnumerator]; |
while(currPath = [searchPathEnum nextObject]) |
{ |
[bundleSearchPaths addObject: |
[currPath stringByAppendingPathComponent:appSupportSubpath]]; |
} |
Создание объекта NSBundle
Для создания NSBundle возражают для пакета, который Вы хотите загрузить, любой выделяет объект и использует initWithPath:
инициализатор или использование метод создания удобства bundleWithPath:
. Если экземпляр уже существует для пакета, оба из этих методов возвращают существующий экземпляр вместо того, чтобы создать новый.
Этот фрагмент кода получает пакет, расположенный в fullPath
:
NSString *fullPath; // Assume this exists. |
NSBundle *bundle; |
bundle = [NSBundle bundleWithPath:fullPath]; |
Загрузка кода
Для загрузки исполняемого кода пакета используйте NSBundle’s load
метод. Этот метод возвраты YES
если код был уже загружен, и, если загрузка была успешна или NO
иначе.
Этот фрагмент кода загружает код для пакета в fullPath
:
NSString *fullPath; // Assume this exists. |
NSBundle *bundle; |
bundle = [NSBundle bundleWithPath:fullPath]; |
[bundle load]; |
Получение основного класса
Каждый пакет Какао содержит код для основного класса, обычно служащего точкой входа приложения в пакет. Вы получаете основной класс пакета с NSBundle’s principalClass
метод, загружающий пакет, если это уже не загружается. Этот фрагмент кода получает основной класс для пакета, расположенного в fullPath
:
NSString *fullPath; // Assume this exists. |
NSBundle *bundle; |
Class principalClass; |
bundle = [NSBundle bundleWithPath:fullPath]; |
principalClass = [bundle principalClass]; |
Можно также получить объекты класса по имени с classNamed:
метод. Этот фрагмент кода получает класс KillerAppController от пакета в fullPath
:
NSString *fullPath; // Assume this exists. |
NSBundle *bundle; |
Class someClass; |
bundle = [NSBundle bundleWithPath:fullPath]; |
someClass = [bundle classNamed:@"KillerAppController"]; |
Инстанцирование основного класса
Как только Вы получили основной класс от загружаемого пакета, Вы обычно создаете экземпляр класса для использования в приложении. (Если класс обеспечивает всю свою функциональность через методы класса, этот шаг не необходим.) Чтобы сделать это, Вы используете a Class
переменная таким же образом Вы использовали бы любое имя класса.
Этот фрагмент кода получает основной класс пакета в fullPath
и создает экземпляр основного класса:
NSString *fullPath; // Assume this exists. |
NSBundle *bundle; |
Class principalClass; |
id instance; |
bundle = [NSBundle bundleWithPath:fullPath]; |
principalClass = [bundle principalClass]; |
instance = [[principalClass alloc] init]; |
Загрузка пакетов какао: пример кода
В большинстве приложений пять шагов загрузки пакета имеют место во время процесса запуска, поскольку это ищет и загружает плагины. Перечисление 1 показывает реализацию для пары методов, определяющих местоположение пакетов, создающих объекты NSBundle, загружающих их код, и находящих и инстанцирующих основного класса каждого обнаруженного пакета. Объяснение следует за перечислением.
Реализации метода перечисления 1 для загрузки пакетов от различных расположений
NSString *ext = @"bundle"; |
NSString *appSupportSubpath = @"Application Support/KillerApp/PlugIns"; |
// ... |
- (void)loadAllBundles |
{ |
NSMutableArray *instances; // 1 |
NSMutableArray *bundlePaths; |
NSEnumerator *pathEnum; |
NSString *currPath; |
NSBundle *currBundle; |
Class currPrincipalClass; |
id currInstance; |
bundlePaths = [NSMutableArray array]; |
if(!instances) |
{ |
instances = [[NSMutableArray alloc] init]; |
} |
[bundlePaths addObjectsFromArray:[self allBundles]]; // 2 |
pathEnum = [bundlePaths objectEnumerator]; |
while(currPath = [pathEnum nextObject]) |
{ |
currBundle = [NSBundle bundleWithPath:currPath]; // 3 |
if(currBundle) |
{ |
currPrincipalClass = [currBundle principalClass]; // 4 |
if(currPrincipalClass) |
{ |
currInstance = [[currPrincipalClass alloc] init]; // 5 |
if(currInstance) |
{ |
[instances addObject:[currInstance autorelease]]; |
} |
} |
} |
} |
} |
- (NSMutableArray *)allBundles |
{ |
NSArray *librarySearchPaths; |
NSEnumerator *searchPathEnum; |
NSString *currPath; |
NSMutableArray *bundleSearchPaths = [NSMutableArray array]; |
NSMutableArray *allBundles = [NSMutableArray array]; |
librarySearchPaths = NSSearchPathForDirectoriesInDomains( |
NSLibraryDirectory, NSAllDomainsMask - NSSystemDomainMask, YES); |
searchPathEnum = [librarySearchPaths objectEnumerator]; |
while(currPath = [searchPathEnum nextObject]) |
{ |
[bundleSearchPaths addObject: |
[currPath stringByAppendingPathComponent:appSupportSubpath]]; |
} |
[bundleSearchPaths addObject: |
[[NSBundle mainBundle] builtInPlugInsPath]]; |
searchPathEnum = [bundleSearchPaths objectEnumerator]; |
while(currPath = [searchPathEnum nextObject]) |
{ |
NSDirectoryEnumerator *bundleEnum; |
NSString *currBundlePath; |
bundleEnum = [[NSFileManager defaultManager] |
enumeratorAtPath:currPath]; |
if(bundleEnum) |
{ |
while(currBundlePath = [bundleEnum nextObject]) |
{ |
if([[currBundlePath pathExtension] isEqualToString:ext]) |
{ |
[allBundles addObject:[currPath |
stringByAppendingPathComponent:currBundlePath]]; |
} |
} |
} |
} |
return allBundles; |
} |
Вот то, как работает код:
instances
массив содержит все объекты, которые инстанцируют от основных классов обнаруженных пакетов. Этот объект показан в методе для ясности, но обычно был бы переменной экземпляра класса контроллера.loadAllBundles
вызовы методаallBundles
метод для получения всех файлов, заканчивающихся расширением.bundle
.allBundles
метод просто перечисляет через все стандартные пути для загружаемых пакетов (в комплекте приложений и в пользователе, локальном, и сетевомLibrary
каталоги).Для каждого возвращенного пути создается объект NSBundle. Если файл с a
.bundle
расширение не было фактически допустимым пакетом, возвратами NSBundlenil
и остальная часть итерации пропускается.Эта строка получает основной класс текущего пакета. Вызов
principalClass
неявно загружает код сначала.Наконец, метод инстанцирует основного класса. Пока
init
не возвращаетсяnil
, новый экземпляр добавляется кinstances
массив. Если Вы пишете приложение со сменной архитектурой (в противоположность приложению с несколькими известными загружаемыми пакетами), необходимо выполнить некоторую проверку на плагинах прежде, чем создать экземпляр основного класса.
Загрузка пакетов некакао с CFBundle
В некоторых случаях Вы, возможно, должны загрузить пакеты некакао из приложения Какао. Вы используете подпрограммы CFBundle в Базовой Основе для загрузки пакетов некакао: CFBundleCreate
создать объекты CFBundle; CFBundleLoadExecutable
загрузить исполняемый код пакета; и CFBundleGetFunctionPointerForName
искать адрес загруженной подпрограммы. Посмотрите, что Базовая Основа Программирует Руководство по программированию Пакета Темы для получения дополнительной информации об этих методах и других методах, предоставленных CFBundle.
Для интеграции кода более чисто с приложением Какао можно записать класс обертки для инкапсуляции данных и указателей функции, искавших через CFBundle.
Перечисление 2 показывает интерфейс для класса обертки Какао для CFBundle, и Перечисление 3 показывает свою реализацию. Объяснение следует за каждым перечислением.
Загрузка перечисления 2 и использование кода от пакета некакао
#import <CoreFoundation/CoreFoundation.h> |
typedef long (*DoSomethingPtr)(long); // 1 |
typedef void (*DoSomethingElsePtr)(void); |
@interface MyBundleWrapper : NSObject |
{ |
DoSomethingPtr doSomething; // 2 |
DoSomethingElsePtr doSomethingElse; |
CFBundleRef cfBundle; // 3 |
} |
- (long)doSomething:(long)arg; // 4 |
- (void)doSomethingElse; |
@end |
Интерфейс содержит четыре элемента:
Введите определения для указателей функции, один для каждой функции в пакете
Переменные экземпляра указателя функции
A
CFBundleRef
переменная экземпляраМетоды Objective C для обертывания функций C
Загрузка перечисления 3 и использование кода от пакета некакао
#import "MyBundleWrapper.h" |
@implementation MyBundleWrapper |
- (id)init |
{ |
NSString *bundlePath; |
NSURL *bundleURL; |
self = [super init]; |
bundlePath = [[[NSBundle mainBundle] builtInPlugInsPath] // 1 |
stringByAppendingPathComponent:@"MyCFBundle.bundle"]; |
bundleURL = [NSURL fileURLWithPath:bundlePath]; |
cfBundle = CFBundleCreate(kCFAllocatorDefault, (CFURLRef)bundleURL); |
return self; |
} |
- (void)dealloc |
{ |
CFRelease(cfBundle); |
} |
- (long)doSomething:(long)arg |
{ |
if(!doSomething) // 2 |
{ |
doSomething = CFBundleGetFunctionPointerForName(cfBundle, |
CFSTR("DoSomething")); |
} |
return doSomething(arg); // 3 |
} |
- (void)doSomethingElse |
{ |
if(!doSomethingElse) // 2 |
{ |
doSomethingElse = CFBundleGetFunctionPointerForName(cfBundle, |
CFSTR("DoSomethingElse")); |
} |
doSomethingElse(); // 3 |
} |
@end |
Вот то, что делает реализация:
Инициализирует
cfBundle
переменная экземпляра с URL к пакету в каталоге плагинов приложения. Пакет может находиться где угодно на диске; каталог плагинов является просто типичным расположением для встроенных загружаемых пакетов.Когда метод вызывают, лениво инициализирует указатель функции, связанный с методом. Вызов к
CFBundleGetFunctionPointerForName
неявно загружает исполняемый код пакета перед поиском указателя функции.Возвращает значение, возвращенное загруженной функцией.