Динамические инструкции по использованию библиотеки

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

Динамические библиотеки помогают распределить функциональность приложения в отличные модули, которые могут быть загружены, поскольку они необходимы. Могут быть загружены динамические библиотеки или когда приложение запускается или когда оно работает. Библиотеки, загружающиеся во время запуска, вызывают зависимыми библиотеками. Библиотеки, загружающиеся во время выполнения, вызывают динамично загруженными библиотеками. Вы указываете, от каких динамических библиотек Ваше приложение зависит путем соединения приложения с ними. Однако более эффективно пользоваться динамическими библиотеками как динамично загруженными библиотеками вместо зависимых библиотек. Т.е. необходимо открыть библиотеки, когда Вы собираетесь использовать символы, они экспортируют и закрывают их, когда Вы сделаны. В некоторых случаях система разгружает динамично загруженные библиотеки, когда она решает, что они не используются.

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

Открытие динамических библиотек

Когда изображение открыто, динамический загрузчик загружает зависимые библиотеки изображения; т.е. когда приложение загружается или когда открыта динамическая библиотека. Динамический загрузчик связывает ссылки на символы, экспортируемые зависимыми библиотеками лениво. Ленивый обязательный означает, что ссылки символа связываются только, когда изображение фактически использует символы. Как мера по отладке, можно указать, что все ссылки на экспортируемые символы библиотеки связываются, когда динамический загрузчик открывает библиотеку. Вы используете компилятор -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 перечисляет переменные.

Табличные 1  Переменные окружения, определяющие пути поиска динамического загрузчика

Переменная окружения

Значение по умолчанию

LD_LIBRARY_PATH

Никакое значение по умолчанию

DYLD_LIBRARY_PATH

Никакое значение по умолчанию

DYLD_FALLBACK_LIBRARY_PATH

$HOME/lib;/usr/local/lib;/usr/lib

Когда имя библиотеки является именем файла (т.е. когда оно не включает имена каталогов), динамический загрузчик ищет библиотеку в нескольких расположениях, пока оно не находит его в следующем порядке:

  1. $LD_LIBRARY_PATH

  2. $DYLD_LIBRARY_PATH

  3. Рабочий каталог процесса

  4. $DYLD_FALLBACK_LIBRARY_PATH

Когда имя библиотеки содержит по крайней мере одно имя каталога, т.е. когда имя является путем (относительный или полностью определенный), динамический загрузчик ищет библиотеку в следующем порядке:

  1. $DYLD_LIBRARY_PATH использование имени файла

  2. Данный путь

  3. $DYLD_FALLBACK_LIBRARY_PATH использование имени файла

Например, скажите установку переменных окружения, представленных ранее как показано в следующей таблице.

Переменная окружения

Значение

LD_LIBRARY_PATH

./lib

DYLD_LIBRARY_PATH

/usr/local/dylibs

DYLD_FALLBACK_LIBRARY_PATH

/usr/local/lib

Принятие Ваших вызовов приложения dlopen с именем файла libCelsus.dylib, динамический загрузчик попытался бы открыть библиотеку с помощью следующих путей в порядке:

Путь

Описание

./lib/libCelsus.dylib

LD_LIBRARY_PATH переменная окружения

/usr/local/dylibs/libCelsus.dylib

DYLD_LIBRARY_PATH переменная окружения

libCelsus.dylib

Текущий рабочий каталог

/usr/local/lib/libCelsus.dylib

DYLD_FALLBACK_LIBRARY_PATH переменная окружения

Если приложение вызывает dlopen с путем /libs/libCelsus.dylib, динамический загрузчик пытается найти библиотеку с помощью этих путей в порядке:

Путь

Описание

/usr/local/dylibs/libCelsus.dylib

DYLD_LIBRARY_PATH переменная окружения

/libs/libCelsus.dylib

Путь, как дали

/usr/local/lib/libCelsus.dylib

DYLD_FALLBACK_LIBRARY_PATH переменная окружения

Указание объема и привязка поведения экспортируемых символов

Второй параметр 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 переменная.

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

Для иллюстрирования понятий, представленных в этом разделе, возьмите приложение, изображенное в рисунке 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 функция.

  Приложение рисунка 1 с иерархией зависимой библиотеки

Изображение приложения может получить доступ к экспортируемым символам в 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. Это имена полей структуры, а также их описаний:

Перечисление 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);
    }
}