Динамические инструкции по использованию библиотеки
Динамические функции совместимости загрузчика обеспечивают переносимый и эффективный способ загрузить код во время выполнения. Однако использование функций неправильно может ухудшить производительность приложения. Эта статья показывает, как правильно загрузить и пользоваться динамическими библиотеками в Ваших приложениях.
Динамические библиотеки помогают распределить функциональность приложения в отличные модули, которые могут быть загружены, поскольку они необходимы. Могут быть загружены динамические библиотеки или когда приложение запускается или когда оно работает. Библиотеки, загружающиеся во время запуска, вызывают зависимыми библиотеками. Библиотеки, загружающиеся во время выполнения, вызывают динамично загруженными библиотеками. Вы указываете, от каких динамических библиотек Ваше приложение зависит путем соединения приложения с ними. Однако более эффективно пользоваться динамическими библиотеками как динамично загруженными библиотеками вместо зависимых библиотек. Т.е. необходимо открыть библиотеки, когда Вы собираетесь использовать символы, они экспортируют и закрывают их, когда Вы сделаны. В некоторых случаях система разгружает динамично загруженные библиотеки, когда она решает, что они не используются.
Эта статья использует изображение слова для обращения к файлу приложения или динамической библиотеке. Двоичные файлы приложения содержат код приложения и код от статических библиотек использование приложения. Динамические библиотеки загрузки приложения во время запуска или время выполнения являются отдельными изображениями.
Открытие динамических библиотек
Когда изображение открыто, динамический загрузчик загружает зависимые библиотеки изображения; т.е. когда приложение загружается или когда открыта динамическая библиотека. Динамический загрузчик связывает ссылки на символы, экспортируемые зависимыми библиотеками лениво. Ленивый обязательный означает, что ссылки символа связываются только, когда изображение фактически использует символы. Как мера по отладке, можно указать, что все ссылки на экспортируемые символы библиотеки связываются, когда динамический загрузчик открывает библиотеку. Вы используете компилятор -bind_at_load
параметр командной строки при генерации динамической библиотеки.
Для пользований динамической библиотекой, которая не является зависимой библиотекой изображения используйте dlopen(3) OS X Developer Tools Manual Page
функция. Эта функция говорит динамическому загрузчику загружать определенную динамическую библиотеку в адресное пространство текущего процесса. Эта функция также позволяет Вам указывать, когда динамический загрузчик связывает ссылки библиотеки на соответствующие экспортируемые символы в ее зависимых библиотеках и поместить ли экспортируемые символы библиотеки в глобальную область видимости текущего процесса или локальный объем. Эта функция возвращается, дескриптор вызвал дескриптор библиотеки. Этот дескриптор представляет динамично загруженную библиотеку в вызовах к dlsym
(для использования экспортируемого символа) и dlclose
(для закрытия библиотеки). Дескриптор библиотеки обеспечивает dlsym
ограниченный домен, в котором можно искать символ (см. Используя Символы для подробных данных). Клиент должен вызвать dlclose
когда это закончило пользоваться динамично загруженной библиотекой (например, когда модуль, открывший библиотеку, закончил свою задачу).
Динамическая библиотека может самостоятельно иметь зависимые библиотеки. Для обнаружения, от которого зависят библиотеки динамическая библиотека используйте otool -L <library>
команда. Перед использованием библиотеки необходимо гарантировать, что все ее зависимые библиотеки присутствуют в компьютере. Иначе, когда библиотека открыта с, динамический загрузчик не загружает Ваше приложение или библиотеку, когда требуется во время запуска или dlopen
.
Процесс может несколько раз открывать ту же динамическую библиотеку, не закрывая его. dlopen
функционируйте возвращается, та же библиотека обрабатывают его, возвратился в первом вызове, но это также постепенно увеличивает подсчет ссылок, связанный с дескриптором. Вызовы к dlclose
постепенно уменьшите подсчет ссылок дескриптора библиотеки. Поэтому необходимо сбалансировать каждый вызов к dlopen
с вызовом к dlclose
. Когда подсчет ссылок для дескриптора библиотеки достигает 0, динамический загрузчик может удалить библиотеку из адресного пространства приложения.
Процесс поиска библиотеки
Первый параметр к dlopen(3) OS X Developer Tools Manual Page
имя динамической библиотеки для открытия. Это может быть именем файла или частично или полностью определенный путь. Например, libCelsus.dylib
, lib/libCelsus.dylib
, или /usr/local/libCelsus.dylib
.
Динамический загрузчик ищет библиотеки в каталогах, указанных рядом переменных окружения и текущего рабочего каталога процесса. Эти переменные, когда определено, должны содержать разделенный от двоеточия список путей (абсолютный или относительный), в котором динамический загрузчик ищет библиотеки. Таблица 1 перечисляет переменные.
Переменная окружения | Значение по умолчанию |
---|---|
| Никакое значение по умолчанию |
| Никакое значение по умолчанию |
|
|
Когда имя библиотеки является именем файла (т.е. когда оно не включает имена каталогов), динамический загрузчик ищет библиотеку в нескольких расположениях, пока оно не находит его в следующем порядке:
$LD_LIBRARY_PATH
$DYLD_LIBRARY_PATH
Рабочий каталог процесса
$DYLD_FALLBACK_LIBRARY_PATH
Когда имя библиотеки содержит по крайней мере одно имя каталога, т.е. когда имя является путем (относительный или полностью определенный), динамический загрузчик ищет библиотеку в следующем порядке:
$DYLD_LIBRARY_PATH
использование имени файлаДанный путь
$DYLD_FALLBACK_LIBRARY_PATH
использование имени файла
Например, скажите установку переменных окружения, представленных ранее как показано в следующей таблице.
Переменная окружения | Значение |
---|---|
|
|
|
|
|
|
Принятие Ваших вызовов приложения dlopen
с именем файла libCelsus.dylib
, динамический загрузчик попытался бы открыть библиотеку с помощью следующих путей в порядке:
Путь | Описание |
---|---|
|
|
|
|
| Текущий рабочий каталог |
|
|
Если приложение вызывает dlopen
с путем /libs/libCelsus.dylib
, динамический загрузчик пытается найти библиотеку с помощью этих путей в порядке:
Путь | Описание |
---|---|
|
|
| Путь, как дали |
|
|
Указание объема и привязка поведения экспортируемых символов
Второй параметр dlopen(3) OS X Developer Tools Manual Page
функция указывает два свойства: объем экспортируемых символов библиотеки в текущем процессе и когда связать ссылки приложения те символы.
Объем символа непосредственно влияет на производительность приложений. Поэтому важно, чтобы Вы установили надлежащий объем для библиотеки, которую Ваше приложение открывает во время выполнения.
Экспортируемые символы динамично загруженной библиотеки могут быть на одном из двух уровней объема в текущем процессе: глобальная переменная и локальный. Основное различие между объемами - то, что символы в глобальной области видимости доступны всем изображениям в процессе, включая другие динамично загруженные библиотеки. Символы в локальном объеме могут использоваться только изображением, открывшим библиотеку. Посмотрите Используя Символы для получения дополнительной информации.
Когда динамический загрузчик ищет символы, он выполняет сравнения строк с каждым символом в поисковом объеме. Сокращение количества символов, которые динамический загрузчик должен пройти через для нахождения желаемого символа, улучшает производительность приложения. Открытие всех динамично загруженных библиотек в локальный объем вместо глобальной области видимости максимизирует производительность поиска символа.
Вы никогда не должны должны быть открывать динамическую библиотеку в глобальную область видимости процесса так, чтобы все модули в приложении имели доступ к его символам. Вместо этого каждый модуль, пользующийся библиотекой, должен открыть ее в свой локальный объем. Когда сделано, модуль должен закрыть библиотеку. Если Вы хотите, чтобы символы, экспортируемые библиотекой, были доступны всем изображениям в процессе, считайте создание библиотеки зависимой библиотекой приложения.
Параметр, используемый для указания объема символа, также используется для указания, когда неопределенные внешние символы динамично загруженной библиотеки разрешены (или связаны с их определениями в собственных зависимых библиотеках библиотеки). Неопределенные внешние символы динамично загруженных библиотек могут быть разрешены или сразу или лениво. Если клиентское приложение использует непосредственную привязку при открытии динамической библиотеки с dlopen
, динамический загрузчик связывает все неопределенные внешние символы динамично загруженной библиотеки перед возвращением управления к клиентскому приложению. Например, Перечисление 1 показывает, что журнал обменивается сообщениями, динамический загрузчик производит когда DYLD_PRINT_BINDINGS
переменная окружения установлена и клиент загрузки приложения динамическая вызванная библиотека libPerson.dylib
:
Привязка перечисления 1 решила во время вызова к dlopen
использование непосредственной привязки
dyld: lazy bind: client:0x107575050 = libdyld.dylib:_dlopen, *0x107575050 = 0x7FFF88740922 |
dyld: bind: libPerson.dylib:0x1075A9000 = libdyld.dylib:dyld_stub_binder, *0x1075A9000 = 0x7FFF887406A0 |
dyld: bind: libPerson.dylib:0x1075A9220 = libobjc.A.dylib:__objc_empty_cache, *0x1075A9220 = 0x7FFF7890EC10 |
dyld: bind: libPerson.dylib:0x1075A9248 = libobjc.A.dylib:__objc_empty_cache, *0x1075A9248 = 0x7FFF7890EC10 |
dyld: bind: libPerson.dylib:0x1075A9228 = libobjc.A.dylib:__objc_empty_vtable, *0x1075A9228 = 0x7FFF7890CF60 |
dyld: bind: libPerson.dylib:0x1075A9250 = libobjc.A.dylib:__objc_empty_vtable, *0x1075A9250 = 0x7FFF7890CF60 |
dyld: bind: libPerson.dylib:0x1075A9218 = CoreFoundation:_OBJC_CLASS_$_NSObject, *0x1075A9218 = 0x7FFF77C40BA8 |
dyld: bind: libPerson.dylib:0x1075A9238 = CoreFoundation:_OBJC_METACLASS_$_NSObject, *0x1075A9238 = 0x7FFF77C40B80 |
dyld: bind: libPerson.dylib:0x1075A9240 = CoreFoundation:_OBJC_METACLASS_$_NSObject, *0x1075A9240 = 0x7FFF77C40B80 |
dyld: bind: libPerson.dylib:0x1075A9260 = CoreFoundation:___CFConstantStringClassReference, *0x1075A9260 = 0x7FFF77C72760 |
dyld: bind: libPerson.dylib:0x1075A9280 = CoreFoundation:___CFConstantStringClassReference, *0x1075A9280 = 0x7FFF77C72760 |
Первое сообщение журнала указывает что клиентское приложение _dlopen
неопределенный символ был связан. Остающиеся сообщения являются привязкой, которую динамический загрузчик выполняет на динамической библиотеке как часть процесса загрузки перед возвращением управления к вызывающей подпрограмме. При использовании ленивой привязки динамический загрузчик разрешает только ссылку клиента на dlopen
функция, возвращая управление вызывающей подпрограмме намного раньше. Для получения дополнительной информации о динамическом журналировании загрузчика посмотрите Регистрирующие Динамические События Загрузчика.
Как только библиотека была открыта с dlopen
, объем, определенный для него, не может быть изменен последующими вызовами на dlopen
загрузить ту же библиотеку. Например, если процесс открывает библиотеку, не загруженную в локальный объем и позже открывающую ту же библиотеку в глобальную область видимости, открытая библиотека сохраняет свое локальное состояние. Т.е. символы экспорт библиотеки не становятся доступными в глобальной области видимости с последним вызовом. Это - истина, даже если библиотека закрывается прежде, чем вновь открыть ее в том же процессе.
Когда те библиотеки содержат много неопределенных внешних символов, непосредственная привязка замедляет загрузка динамических библиотек, особенно. Однако непосредственная привязка может помочь во время разработки и тестирования динамических библиотек, потому что, когда динамический загрузчик не может разрешить все неопределенные внешние символы динамично загруженной библиотеки, приложение завершается с ошибкой. При развертывании приложения, однако, необходимо использовать ленивую загрузку, потому что неопределенные внешние символы связываются только при необходимости. При загрузке динамических библиотек этот путь может помочь заставить приложение чувствовать себя более быстро реагирующим своим пользователям.
Внешние неопределенные символы в зависимых библиотеках связываются, когда они сначала используются, если клиентская строка компиляции изображения не включает -bind_at_load
опция. Посмотрите ld
страница справочника для подробных данных.
Используя символы
После открытия динамического использования библиотеки dlopen(3) OS X Developer Tools Manual Page
, изображение использует dlsym(3) OS X Developer Tools Manual Page
функция для получения адреса желаемого символа перед использованием его. Эта функция берет два параметра. Первый указывает, в которых библиотеках динамический загрузчик ищет символ. Второй параметр указывает имя символа. Например:
symbol_pointer = dlsym(library_handle, "my_symbol") |
Этот вызов говорит динамическому загрузчику искать названный символ my_symbol
среди символов, экспортируемых динамично загруженной библиотекой, представленной library_handle
переменная.
Существует три объема, динамический загрузчик может искать символ: определенная динамическая библиотека, зависимые библиотеки текущего изображения и глобальная область видимости процесса:
Локальный объем: искать символы, экспортируемые определенной динамической библиотекой, загруженной с помощью
dlopen
, Вы обеспечиваетеdlsym
с дескриптором к той библиотеке. Это - самая эффективная модель использования.Следующий объем: Этот поисковый объем полезен только, когда модуль вмешался символ, экспортируемый зависимой библиотекой. Например, Вы, возможно, должны прервать все вызовы к системной функции для выполнения бухгалтерского учета прежде, чем вызвать реальную реализацию. В этом случае, в Вашем пользовательском определении функции, Вы получаете адрес функции, которую Вы вмешались путем вызова
dlsym
сRTLD_NEXT
специальный дескриптор вместо дескриптора к определенной библиотеке. Такой вызов возвращает адрес функции, которая была бы выполнена, если бы Вы не кашировали ту реализацию со своим собственным. Поэтому только зависимые библиотеки текущего изображения ищутся; любые другие библиотеки, включая библиотеки, открытые изображением, делающимdlsym
вызовите, не ищутся. Кроме того, в плоском пространстве имен поиск запускается в первой зависимой библиотеке, перечисленной после текущей, когда было соединено приложение.Глобальная область видимости: Для поиска глобальной области видимости Вы вызываете
dlsym
сRTLD_DEFAULT
специальный дескриптор. Динамический загрузчик ищет зависимые библиотеки (загруженный во время запуска) и динамично загруженные библиотеки (загруженный во время выполненияRTLD_GLOBAL
) поскольку первое соответствие символа называет даннымиdlsym
. Необходимо избежать выполнять глобальные поиски символа, потому что они являются самыми неэффективными.
Для иллюстрирования понятий, представленных в этом разделе, возьмите приложение, изображенное в рисунке 1. Это показывает, что приложение имеет две зависимых библиотеки, libArt.dylib
и libBus.dylib
. libBus.dylib
сама библиотека имеет две зависимых библиотеки, libBus1.dylib
и libBus2.dylib
. libBus1.dylib
библиотека имеет одну зависимую библиотеку, libBus1a.dylib
. Кроме того, существует четыре динамических библиотеки, приложение не зависит от, libCar.dylib
, libCar1.dylib
, libDot.dylib
, и libDot1.dylib
. libCar1.dylib
библиотека является зависимой библиотекой libCar.dylib
и libDot1.dylib
зависимая библиотека libDot.dylib
. Все библиотеки кроме libArt.dylib
экспортируйте dependencies
функция. Каждая библиотека имеет уникальную реализацию ...name
функция.
Изображение приложения может получить доступ к экспортируемым символам в libArt.dylib
и libBus.dylib
непосредственно, как показано в Перечислении 2.
Изображение Приложения перечисления 2 с помощью символов экспортируется зависимыми библиотеками через неопределенные внешние ссылки
#include <stdio.h> |
extern char* A_name(); // libArt.dylib |
extern char* dependencies(); // libBus.dylib |
int main(void) { |
printf("[%s] libArt.A_name() = %s\n", __FILE__, A_name()); |
printf("[%s] libBus.dependencies() = %s\n", __FILE__, dependencies()); |
} |
Изображение приложения, однако, не может непосредственно получить доступ к символам, экспортируемым libBus1.dylib
, libBus1a.dylib
, и libBus2.dylib
потому что те библиотеки не являются зависимыми библиотеками изображения приложения. Для получения доступа к тем символам изображение приложения должно открыть соответствующее использование библиотек dlopen
, как показано в Перечислении 3.
Во время выполнения загрузилось изображение Приложения перечисления 3 с помощью символа, экспортируемого динамической библиотекой
#include <stdio.h> |
#include <dlfcn.h> |
int main(void) { |
void* Bus1a_handle = dlopen("libBus1a.dylib", RTLD_LOCAL); |
if (Bus1a_handle) { |
char* (*b1a_name)() = dlsym(Bus1a_handle, "B1a_name"); |
if (b1a_name) { |
printf("[%s] libBus1a.B1a_name() = %s\n", |
__FILE__, b1a_name()); |
} |
} |
else { |
printf("[%s] Unable to open libBus1a.dylib: %s\n", |
__FILE__, dlerror()); |
} |
dlclose(Bus1a_handle); |
} |
До сих пор Вы видели, как получить доступ к символам или через ссылки на импортированные символы или путем получения адреса желаемого символа путем вызова dlsym
с дескриптором соответствующей библиотеки или с RTLD_DEFAULT
специальный дескриптор. Как отмечалось ранее, вмешался, символы предлагают возможность для изменения определения символа, экспортируемого зависимой библиотекой.
Для доступа к исходному определению вмешавшихся символов Вы вызываете dlsym
с RTLD_NEXT
специальный дескриптор. Перечисление 4 показывает реализацию dependencies
функция в библиотеке Bus (реализация идентична в Bus1 и Bus1a). Функция в Шине возвращает имя библиотеки (содержавшийся в k_lib_name
переменная), конкатенировал со строкой разделителя и текстом, возвращенным следующим определением dependencies
, который найден в библиотеке Bus1. Определение в Bus1 связывает свое имя со строкой разделителя и текстом, возвращенным определением в Bus1a. Определение в Bus1a является последним, который был бы найден, если бы ни одно из клиентских изображений не определило их собственную версию. Поэтому, когда Bus1a вызывает dlsym(RTLD_NEXT, "dependencies")
никакие другие определения для dependencies
найдены. Это - конец иерархии вмешательства dependencies
функция.
Изображение Библиотеки перечисления 4 с помощью вмешавшегося символа
#include <string.h> |
static char* k_lib_name = "libBus"; |
char* dependencies(void) { |
char _dependencies[50] = ""; |
strcpy(_dependencies, k_lib_name); |
char* (*next_dependencies)() = |
dlsym(RTLD_NEXT, "dependencies");// look for next definition |
if (next_dependencies) { |
strncat(_dependencies, ", ", |
sizeof(_dependencies) - strlen(_dependencies) - 1); |
strncat(_dependencies, next_dependencies(), |
sizeof(_dependencies) - strlen(_dependencies) - 1); |
} |
return strdup(_dependencies); |
} |
Когда изображение вызывает функцию зависимостей в библиотеке Bus, это получает имена всех библиотек, библиотека Bus зависит от, как показано в Перечислении 5.
Изображение Приложения перечисления 5, вызывающее вмешавшуюся функцию
#include <stdio.h> |
extern char* dependencies(); // libBus.dylib |
int main(void) { |
printf("[%s] libBus.dependencies() = %s\n", |
__FILE__, dependencies()); |
} |
Используя слабо соединенные символы
Для продвижения совместимости с ранее или более поздние версии динамическая библиотека может экспортировать некоторых или все ее общедоступные символы как слабо соединенные символы. Слабо соединенный символ один, для которого компилятор генерирует слабую ссылку, когда клиент соединяется с библиотекой. Слабо соединенные символы могут иметь weak_import
атрибут в их объявлениях в заголовочных файлах библиотеки или разработчике библиотеки может иначе задокументировать, какой из общедоступных символов библиотеки слабо соединяется. Третий способ идентифицировать слабо соединенные символы это путем выполнения команды:
nm -m <client_file> | grep weak |
Это списки команд слабо соединенные символы импортируется из зависимых библиотек.
Слабо соединенный символ может или не может быть определен зависимой библиотекой. Т.е. несмотря на то, что символ объявляется в заголовочном файле, соответствующий динамический файл библиотеки может не содержать реализацию того символа. Перечисление 6 показывает, как слабо соединенный символ может быть объявлен в заголовочном файле для динамической библиотеки. Клиентам, использующим этот заголовочный файл в качестве их интерфейса к соответствующей зависимой библиотеке, гарантируют это name
и set_name
определяются. Однако clear_name
может не быть реализован. Зависимая библиотека загружается успешно, реализует ли она clear_name
. Но это не загружается, если это не определяет также name
или set_name
. Когда библиотека не реализует слабо соединенный символ, динамический загрузчик устанавливает в 0 любых клиентских ссылок на символ.
Заголовочный файл перечисления 6 со слабо соединенным объявлением символа
/* File: Person.h */ |
#define WEAK_IMPORT __attribute__((weak_import)) |
char* name(void); |
void set_name(char* name); |
WEAK_IMPORT |
void clear_name(void); |
Слабо соединенные символы используются разработчиками библиотеки для максимизации совместимости клиента с более ранними или более новыми версиями зависимой библиотеки. Например, символ, реализованный в определенной версии библиотеки, может не быть доступным в более поздней версии. Но клиент соединился с первой версией, также работает со второй версией. Разработчики клиента, однако, должны гарантировать существование символа в рабочем процессе прежде, чем выполнить его. Этот механизм также используется для обеспечения стандартного интерфейса для плагинов, которые могут или могут не реализовать весь интерфейс.
Перечисление 7 показывает код, гарантирующий, что определенная функция определяется перед использованием его. Когда функция не найдена, клиент использует различную функцию для выполнения желаемой задачи. В этом случае функция нейтрализации не является слабо соединенным символом, таким образом, не требуется никакой тест. Другие ситуации могут не предложить альтернативный интерфейс. В таких случаях клиент может не быть в состоянии выполнить желаемую задачу.
Перечисление 7 Используя слабо соединенный символ
// Clear the 'name' property. |
if (clear_name) { |
clear_name(); |
} |
else { |
set_name(" "); |
} |
Используя классы C++
То, как разработчики клиента используют класс C++, зависит от того, загружается ли динамическая библиотека, реализующая его, когда клиент загружается (зависимая библиотека), или позже (время выполнения загрузило библиотеку). Классы зависимой библиотеки могут использоваться непосредственно. Т.е. клиенты могут создать и удалить объекты с new
и delete
операторы. Классы, реализованные в библиотеках, загрузились во время выполнения с dlopen(3) OS X Developer Tools Manual Page
вызываются время выполнения загрузило классы.
Время выполнения загрузилось, класс должен инстанцировать клиент, использующий, что фабрика класса функционирует, объявленная как часть интерфейса класса. Функции фабрики создают и уничтожают экземпляры определенного класса: Функции конструктора инстанцируют объектов, и функции деструктора уничтожают их. Клиенты должны использовать функции фабрики вместо new
и delete
потому что динамический загрузчик не имеет доступа к конструкторам и деструкторам загруженного класса времени выполнения. Когда клиент вызывает функцию фабрики, библиотека вызывает соответствующего конструктора и деструктор от имени клиента. После создания экземпляра загруженного класса времени выполнения Вы вызываете его функции членства тем же путем, Вы вызвали бы их, если бы класс был определен локально.
Интерфейс для классов C++, реализованных в динамических библиотеках, составлен из, по крайней мере, объявления класса и ряда функций фабрики. Интерфейс класса включает одно определение типа на функцию конструктора. Для использования функции фабрики необходимо создать объект надлежащего типа и получить адрес функции с dlsym(3) OS X Developer Tools Manual Page
. Можно тогда вызвать функцию фабрики, чтобы создать или уничтожить объект класса.
Перечисление 8 показывает интерфейс Person
класс, реализованный в библиотеке Person.
Интерфейс класса C++ перечисления 8
/* File: Person.h */ |
class Person { |
private: |
char _person_name[30]; |
public: |
Person(); |
Person(char* name); |
virtual void set_name(char person_name[]); |
virtual char* name(); |
}; |
// Constructor functions and function types. |
extern "C" Person* NewPerson(void); |
typedef Person * Person_creator(void); |
extern "C" Person* NewPersonWithName(char name[]); |
typedef Person * PersonWithName_creator(char name[]); |
// Destructor function and function type. |
extern "C" void DeletePerson(Person* person); |
typedef void Person_disposer(Person*); |
Перечисление 9 показывает возможную реализацию Person
класс.
Реализация перечисления 9 Person
класс в библиотеке Person
/* File: Person.cpp */ |
#include <iostream> |
#include "Person.h" |
#define EXPORT __attribute__((visibility("default"))) |
EXPORT |
Person::Person() { |
char default_name[] = "<no value>"; |
this->set_name(default_name); |
} |
EXPORT |
Person::Person(char *name) { |
this->set_name(name); |
} |
EXPORT |
Person* NewPerson(void) { |
return new Person; |
} |
EXPORT |
Person* NewPersonWithName(char name[]) { |
return new Person(name); |
} |
EXPORT |
void DeletePerson(Person* person) { |
delete person; |
} |
void Person::set_name(char name[]) { |
strcpy(_person_name, name); |
} |
char* Person::name(void) { |
return _person_name; |
} |
Обратите внимание на то, что Person
класс имеет две функции конструктора, NewPerson
и NewPersonWithName
. Каждое объявление функции имеет соответствующий тип, Person_creator
и PersonWithName_creator
. Перечисление 10 и Перечисление 11 показывают, как клиент может пользоваться библиотекой Person.
Клиент перечисления 10, использующий зависимую библиотеку C++
/* File: Client.cpp */ |
#include <iostream> |
#include "Person.h" |
int main() { |
using std::cout; |
using std::cerr; |
// Create Person objects. |
Person* person1 = new Person(); |
char person_name[] = "Cendrine"; |
Person* person2 = new Person(person_name); |
cout << "[" << __FILE__ << "] person1->name() = " << person1->name() << "\n"; |
cout << "[" << __FILE__ << "] person2->name() = " << person2->name() << "\n"; |
// Use Person objects. |
char person1_name[] = "Floriane"; |
person1->set_name(person1_name); |
cout << "[" << __FILE__ << "] person1->name() = " << person1->name() << "\n"; |
char person2_name[] = "Marcelle"; |
person2->set_name(person2_name); |
cout << "[" << __FILE__ << "] person2->name() = " << person2->name() << "\n"; |
// Destroy Person objects. |
delete person1; |
delete person2; |
return 0; |
} |
Клиент перечисления 11, использующий C++ динамично, загрузил библиотеку
/* File: Client.cpp */ |
#include <iostream> |
#include <dlfcn.h> |
#include "Person.h" |
int main() { |
using std::cout; |
using std::cerr; |
// Open the library. |
void* lib_handle = dlopen("./libPerson.dylib", RTLD_LOCAL); |
if (!lib_handle) { |
exit(EXIT_FAILURE); |
} |
// Get the NewPerson function. |
Person_creator* NewPerson = (Person_creator*)dlsym(lib_handle, "NewPerson"); |
if (!NewPerson) { |
exit(EXIT_FAILURE); |
} |
// Get the NewPersonWithName function. |
PersonWithName_creator* NewPersonWithName = (PersonWithName_creator*)dlsym(lib_handle, "NewPersonWithName"); |
if (!NewPersonWithName) { |
exit(EXIT_FAILURE); |
} |
// Get the DeletePerson function. |
Person_disposer* DeletePerson = |
(Person_disposer*)dlsym(lib_handle, "DeletePerson"); |
if (!DeletePerson) { |
exit(EXIT_FAILURE); |
} |
// Create Person objects. |
Person* person1 = NewPerson(); |
char person_name[] = "Cendrine"; |
Person* person2 = NewPersonWithName(person_name); |
cout << "[" << __FILE__ << "] person1->name() = " << person1->name() << "\n"; |
cout << "[" << __FILE__ << "] person2->name() = " << person2->name() << "\n"; |
// Use Person objects. |
char person1_name[] = "Floriane"; |
person1->set_name(person1_name); |
cout << "[" << __FILE__ << "] person1->name() = " << person1->name() << "\n"; |
char person2_name[] = "Marcelle"; |
person2->set_name(person2_name); |
cout << "[" << __FILE__ << "] person2->name() = " << person2->name() << "\n"; |
// Destroy Person objects. |
DeletePerson(person1); |
DeletePerson(person2); |
// Close the library. |
if (dlclose(lib_handle) != 0) { |
exit(EXIT_FAILURE); |
} |
return 0; |
} |
Используя классы Objective C
Для использования класса Objective C или категории, реализованной в динамической библиотеке, у клиента должен быть интерфейс к классу или категории. Со знанием корректного интерфейса класса клиент может создать соответственно вводящиеся экземпляры класса. Иначе, компилятор производит предупреждения для методов с недостающими объявлениями.
Интерфейсы классов Objective C и категорий публикуются в заголовочных файлах библиотеки как протоколы. Инстанцирование класса, реализованного в зависимой библиотеке, не отличается от выполнения того же для локально определенного класса. Однако, когда Вы загружаете динамическую библиотеку при использовании во время выполнения dlopen(3) OS X Developer Tools Manual Page
, необходимо получить надлежащий класс путем вызова objc_getClass
функция.
Например, Перечисление 12 содержит интерфейсы для Person
класс и Titling
категория к тому классу, которые реализованы Лицом динамическая библиотека.
Интерфейс перечисления 12 к Person
класс и Titling
категория
/* File: Person.h */ |
#import <Foundation/Foundation.h> |
@protocol Person |
- (void)setName:(NSString*)name; |
- (NSString*)name; |
@end |
@interface Person : NSObject <Person> { |
@private |
NSString* _person_name; |
} |
@end |
/* File: Titling.h */ |
#import <Foundation/Foundation.h> |
#import "Person.h" |
@protocol Titling |
- (void)setTitle:(NSString*)title; |
@end |
@interface Person (Titling) <Titling> |
@end |
Клиент скомпилировал с этими интерфейсами и соединился с библиотекой Person, может создать объекты, реализующие интерфейсы очень прямым способом, как показано Перечислением 13.
Пример перечисления 13 клиента, пользующегося библиотекой Person как зависимой библиотекой
/* File: Client.m */ |
#import <Foundation/Foundation.h> |
#import "Person.h" |
#import "Titling.h" |
int main() { |
@autoreleasepool { |
// Create an instance of Person. |
Person<Titling>* person = [[Person alloc] init]; |
// Use person. |
[person setName:@"Perrine LeVan"]; |
[person setTitle:@"Ms."]; |
NSLog(@"[%s] main: [person name] = %@", __FILE__, [person name]); |
} |
return(EXIT_SUCCESS); |
} |
То, когда библиотека Person является временем выполнения, загрузило библиотеку, однако, клиент должен получить ссылку на класс Лица со времени выполнения Objective C после загрузки библиотеки, использования objc_getClass
. Это может тогда использовать ту ссылку для инстанцирования a Person
объект. Однако переменная, содержащая экземпляр, должна введенным как NSObject
это реализует Person
и Titling
протоколы для предотвращения предупреждений компилятора. Когда сделано, клиент закрывает библиотеку, как показано в Использовании Слабо Соединенных Символов.
Пример перечисления 14 клиента, пользующегося библиотекой Person как временем выполнения, загрузил библиотеку
/* File: Client.m */ |
#import <Foundation/Foundation.h> |
#import <objc/runtime.h> |
#import <dlfcn.h> |
#import "Person.h" |
#import "Titling.h" |
int main() { |
@autoreleasepool { |
// Open the library. |
void* lib_handle = dlopen("./libPerson.dylib", RTLD_LOCAL); |
if (!lib_handle) { |
NSLog(@"[%s] main: Unable to open library: %s\n", |
__FILE__, dlerror()); |
exit(EXIT_FAILURE); |
} |
// Get the Person class (required with runtime-loaded libraries). |
Class Person_class = objc_getClass("Person"); |
if (!Person_class) { |
NSLog(@"[%s] main: Unable to get Person class", __FILE__); |
exit(EXIT_FAILURE); |
} |
// Create an instance of Person. |
NSLog(@"[%s] main: Instantiating Person_class", __FILE__); |
NSObject<Person,Titling>* person = [[Person_class alloc] init]; |
// Use person. |
[person setName:@"Perrine LeVan"]; |
[person setTitle:@"Ms."]; |
NSLog(@"[%s] main: [person name] = %@", __FILE__, [person name]); |
// Close the library. |
if (dlclose(lib_handle) != 0) { |
NSLog(@"[%s] Unable to close library: %s\n", |
__FILE__, dlerror()); |
exit(EXIT_FAILURE); |
} |
} |
return(EXIT_SUCCESS); |
} |
Получение информации о символе в определенном адресе
Одна из функций динамической совместимости загрузчика (DLC), dladdr(3) OS X Developer Tools Manual Page
, предоставляет информацию об изображении и самом близком символе, соответствующем адресу. Можно использовать эту функцию для получения информации о библиотеке, экспортирующей определенный символ.
Информация dladdr
обеспечивает возвращается через выходной параметр типа Dl_info
. Это имена полей структуры, а также их описаний:
dli_fname
: Путь изображенияdli_fbase
: Базовый адрес изображения в процессеdli_sname
: Имя символа с адресом, который равен или ниже, чем адрес, предоставленный дляdladdr
dli_saddr
: Адрес символа, обозначенногоdli_sname
Перечисление 15 показывает, как изображение может получить информацию о символе:
Перечисление 15 , Получающее информацию о символе
#include <stdio.h> |
#include <dlfcn.h> |
extern char* dependencies(); |
int main(void) { |
// Get information on dependencies(). |
Dl_info info; |
if (dladdr(dependencies, &info)) { |
printf("[%s] Info on dependencies():\n", __FILE__); |
printf("[%s] Pathname: %s\n", __FILE__, info.dli_fname); |
printf("[%s] Base address: %p\n", __FILE__, info.dli_fbase); |
printf("[%s] Nearest symbol: %s\n", __FILE__, info.dli_sname); |
printf("[%s] Symbol address: %p\n", __FILE__, info.dli_saddr); |
} |
else { |
printf("[%s] Unable to find image containing the address %x\n", |
__FILE__, &dependencies); |
} |
} |