Cocoa, Swift, Objective-C: Interacting with C APIs (Взаимодействие с C API)

Подразделы:

Swift предоставляет эквивалент C примитивам целого типа — char, int, float, double, но между C int и Swift Int к примеру нет неявного преобразования. Предпочтительней использовать Swift типы

Таблица соответствия

C Type Swift Type
bool CBool
char, signed char CChar
unsigned char CUnsignedChar
short CShort
unsigned short CUnsignedShort
int CInt
unsigned int CUnsignedInt
long CLong
unsigned long CUnsignedLong
long long CLongLong
unsigned long long CUnsignedLongLong
wchar_t CWideChar
char16_t CChar16
char32_t CChar32
float CFloat
double CDouble

Enumerations (перечисления)

Swift импортирует все перечисления записанные в C-стиле с помощью макроса NS_ENUM, при этом из case убираются префиксы

Например такой C код

Импортируется в Swift как

Доступ к case по точке

Option Sets (наборы опций)

Swift так же импортирует перечисления записанные в C-стиле с помощью макроса NS_OPTIONS, при этом из case так же убираются префиксы

Swift импортирует как

В Objective-C набор опций представлен битовой маской целых значений. Для совмещения масок используем ИЛИ (|), для проверки на соответствие маске И (&). Новый набор опций создается из константы или выражения, пустой набор опций = 0

В Swift набор опций — структура реализующая OptionSetType протокол со статическими переменными для каждого значения опции. Набор опций ведут себя как Set.
insert(_:) или unionInPlace(_:) методы для добавления значений опций.
remove(_:) или substractInPlace(_:) методы для удаления значений опций
contains(_:) для проверки значения опции.
Создается новый набор опций с помощью синтаксиса массива []. Доступ к значениям по точке, как в перечислениях.
Пустой набор создается с помощью [] или с помощью инициализатора по умолчанию

Unions (Объединения)

Swift лишь частично поддерживает union тип в C. Когда импортируется агрегатор типа unions, bitfield, такие как NSDecimal тип, — Swift не может получить доступ к неподдерживаемым полям. Однако C и Objective-C API которое принимает аргументы таких типов/возвращающие значения таких типов могут использоваться в swift.

Pointers (Указатели)

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

C Syntax Swift Syntax
const Тип * UnsafePointer<Тип>
Тип * UnsafeMutablePointer<Тип>

Для типов у классов:

C Syntax Swift Syntax
Тип * const * UnsafePointer<Тип>
Тип * __strong * UnsafeMutablePointer<Тип>
Тип ** AutoreleasingUnsafeMutablePointer<Тип>

Constant Pointers (Константные указатели)

Когда функция должна принимать в качестве аргумента UnsafePointer<Тип> — она может принимать одно значение из нижеперечисленных:
nil, которые передается как нулевой указатель
UnsafePointer<Тип>, UnsafeMutablePointer<Тип> или AutoreleasingUnsafeMutablePointer<Тип>, которые в случае необходимости будут сконвертированы в UnsafePointer<Тип>
String (строку), если Тип = Int8 или UInt8. Строка будет автоматически сконвертирована в UTF8 в буфере который проживет до конца вызова.
inout выражение, адрес должен взяться у объекта, у которого тип = Тип
[Тип] значение, которое передается в качестве указателя на начало массива, жизнь которого продлевается до конца вызова

Если мы так определили функцию:

Мы можем вызывать ее следующими способами:

Если же функция должна принимать вместо UnsafePointer<Тип>UnsafePointer, — то она сможет принимать те же операнды как UnsafePointer<Тип>, но для любого типа

Функцию теперь можно взывать такими способами:

Mutable Pointers (Изменяемые указатели)

Когда функция должна принимать в качестве аргумента UnsafeMutablePointer<Тип> — она может принимать одно значение из нижеперечисленных:
nil, которые передается как нулевой указатель
UnsafeMutablePointer<Тип>
inout выражение, адрес должен взяться у объекта, у которого тип = Тип
inout[Тип] значение, которое передается в качестве указателя на начало массива, жизнь которого продлевается до конца вызова

Если мы так определили функцию:

Мы можем вызывать ее следующими способами:

Если же функция должна принимать вместо UnsafeMutablePointer<Тип>UnsafeMutablePointer, — то она сможет принимать те же операнды как UnsafeMutablePointer<Тип>, но для любого типа

Если вы так объявите функцию:

Вы сможете вызвать ее следующим образом:

Autoreleasing Pointers (автоматически освобождаемые указатели)

Когда функция должна принимать в качестве аргумента AutoreleasingUnsafeMutablePointer<Тип> — она может принимать одно значение из нижеперечисленных:
nil, которые передается как нулевой указатель
AutoreleasingUnsafeMutablePointer<Тип>
inout выражение, для операнда которого выполняется примитивное (неглубокое) копирование во временный nonowning буфер. Адрес этого буфера передается вызываемому коду и после возвращения управления — значение в буфере загружается, ему делается retain и переназначается в операнд.
Отметим что массивы недопустимы.

Если мы так определили функцию:

Вы сможете вызвать ее следующим образом:

Типы на которые указывают указатели — не взаимозаменяемы (not bridged).
Например NSString ** приходит в Swift как AutoreleasingUnsafeMutablePointer, а не AutoreleasingUnsafeMutablePointer.

Function Pointers (Указатели на функции)

В swift 1.2 указатели на функции импортировали в Swift в виде CFunctionPointer<Тип>, где Тип — тип функции Swift.
Например int(*)(void) в C импортировался как CFunctionPointer<() -> Int32>

С приходом swift 2.0 CFunctionPointer убрали напрочь. Теперь C указатели на функции импортируются в Swift как замыкания, но помеченные атрибутом @convention(c).
Например тот же указатель int (*)(void) будет импортирован как @convention(c) () -> Int32.

Когда вызывается функция, которая принимает в качестве аргумента указатель на функцию — можно передать функцию верхнего уровня Swift, или замыкание определенное на месте или nil. Только Swift типы функций с атрибутом @convention(c) могут быть использованы для приема аргументов — указатель на функцию. Например возьмем функцию из Core Foundation’s CFArrayCreateMutable(_:_:_:). Она принимает структуру CFArrayCallBacks, которая инициализируется с помощью callback указателя на функцию:

Замечу, что если бы замыкание было присвоено в переменную/константу — это бы не подошло, нужны только замыкания определенные на месте

В этом примере инициализатор CFArrayCallBacks использует nil значения в качестве аргументов для retain и release параметров, функцию customCopyDescription в качестве аргумента для параметра customCopyDescription, и определенное на месте замыкание в качестве аргумента для параметра сравнения.

Global Constants (глобальные константы)

Глобальные константы определенные в C и Objective-C файлах с исходным кодом автоматически импортируются swift компилятором как глобальные константы Swift‘а

Preprocessor Directives (Директивы препроцессора)

Swift компилятор не использует препроцессор, так что директивы препроцессора не импортируются.

Макросы

Simple Macros (Простые макросы)

Обычно, когда используется директива #define чтобы определить константу в C и Objective-C — в Swift мы взамен используем глобальную константу.
Вместо

Лучше использовать

Поэтому компилятор импортирует простые макросы для определения констант в глобальные переменные в Swift.

Complex Macros (Сложные макросы)

Сложные макросы в C и Objective-C не имею аналогов в swift и не импортируются. В swift взамен лучше использовать функции и дженерики

Build Configurations (конфигурации билдов)

Swift код и Objective-C условно компилируются разными способами. Swift код может условно компилироваться основываясь на вычислении конфигураций билда. Конфигурации билда включают true и false значения, флаги командной строки и функции на определение текущей платформы. Задать флаги в командной строке можно с помощью -D <#флаг#>

Функция Валидный аргумент
os() OSX, iOS, watchOS
arch() x86_64, arm, arm64, i386

Стоит отметить, что arch(arm) не вернет true для устройств ARM 64. arch(i386) вернет true если код компилируется для 32–битного iOS симулятора.

Простейшие условия компиляции могут выглядеть так:

Утверждения — состоят из 0 или более корректных Swift утверждений, которые могут включать выражения, утверждения и проверки условий. Можно добавлять дополнительные проверки с помощью &&, || операторов, обращать значение проверки с помощью !, добавлять блоки проверки с помощью #elseif:

В отличии от условных утверждений в C препроцессоре, — условные утверждения в Swift должны полностью окружать блоки кода, который должен быть корректным. Т.к. swift проверяет синтаксис даже кода который не компилирует

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *