Учебник по типу CFNumber

Введение

Объект CFNumber инкапсулирует C числовых типов. Он используется в различных случаях в Core Foundation API. Следовательно, очень полезно знать, как работает этот тип. Мы разработали программу командной строки, которая демонстрирует некоторые случаи использования CFNumber.

Исходный код

Исходный код программы можно найти здесь.

Запуск программы

Если вы скомпилируете и запустите программу, вы получите следующий вывод:

❯ ./CFNumberDemo
Address of temperature 0x7ffee9824d50
Value of pTemperature 0x7face3504dd0
Value of myFuncAddress 0x1063dd5a0
---------------------------
Get Types:...
ui number type is kCFNumberLongType = NO
ui number type is kCFNumberSInt64Type = YES
f number type is kCFNumberFloatType = NO
f number type is kCFNumberFloat32Type = YES
temperatureAddress number type is kCFNumberLongLongType = NO
temperatureAddress number type is kCFNumberSInt64Type = YES
pTemperature number type is kCFNumberLongLongType = NO
pTemperature number type is kCFNumberSInt64Type = YES
myFuncAddress number type is kCFNumberLongLongType = NO
myFuncAddress number type is kCFNumberSInt64Type = YES
---------------------------
Get Values:...
ui value retrieved = 5
f value retrieved = 8.20
temperatureAddress value retrieved = 0x7ffee9824d50
Temperature maximum 40, minimum 32
pTemperature value retrieved = 0x7face3504dd0
Temperature maximum 42, minimum 31
myFuncAddress value retrieved = 0x1063dd5a0
Call function via returned value to prove that it works...
res = 3
---------------------------

Беззнаковое целое

Программа демонстрирует случай unsigned int и то, как вы можете обернуть его как CFNumber.

  • строка 1: переменной ui присваивается значение 5.
  • строки 3–5: мы создаем CFNumber, обертывающее значение переменной ui. Обратите внимание, что мы вызываем функцию CFNumberCreate() и указываем распределитель по умолчанию (kCFAllocatorDefault) и тип kCFNumberLongType в качестве типа числа, которое мы хотим обернуть. Наконец, и это очень важно, мы предоставляем адрес памяти, в которой хранится значение, которое мы хотим обернуть, как CFNumber.
  • строки 10–14: эти строки демонстрируют, что Core Foundation может наконец обернуть число типом, отличным от того, который мы указываем при вызове функции CFNumberCreate(). CFNumberGetType() возвращает kCFNumberSInt64Type для значения 5, которое мы сохранили, даже если мы создали его с типом kCFNumberLongType.
  • строки 22–24: эти строки демонстрируют, как мы можем получить значение за объектом CFNumber. Мы используем функцию CFNumberGetValue(). Мы определили тип, который мы использовали при создании CFNumber, и адрес памяти, где мы хотим сохранить возвращаемое значение. Программа печатает значение 5 в качестве возвращаемого значения. Обратите внимание, что хотя мы изменили значение, хранящееся в исходной переменной (ui = 8), это изменение не отражается на значении, которое мы получаем из CFNumberGetValue(), потому что CFNumber создает копию значения, которое мы передаем в функцию CFNumberCreate().
  • строка 27: Мы не должны забывать освобождать ресурсы, зарезервированные объектом CFNumber. Это делается с помощью функции CFRelease().

Плавать

Мы переносим число float в число CFNumber. Вот часть кода, связанная с этим:

Это не сильно отличается от случая unsigned int.

  • Убедитесь, что функция CFNumberCreate() принимает в качестве последнего аргумента адрес памяти, содержащей значение, которое мы хотим обернуть.
  • Обратите внимание, что в действительности используется тип kCFNumberFloat32Type, а не kCFNumberFloatType, хотя мы указали последний при вызове CFNumberCreate().
  • Исходное значение копируется, когда мы обертываем/создаем файл CFNumber. Следовательно, любое изменение (f = 15.3) не влияет на значение CFNumber, которое возвращает 8.20, когда мы получаем значение с CFNumberGetValue().
  • Не забываем освобождать ресурсы за CFNumber вызовом CFRelease().

Адрес локальной переменной

Как сохранить адрес локальной переменной в объект CFNumber? Вот как мы это делаем в нашей демонстрационной программе:

Приведенная выше программа сохраняет адрес переменной temperature в файле CFNumber.

  • строки 2–7: Сначала мы объявляем другую переменную типа unsigned long и сохраняем в ней адрес переменной temperature. Мы делаем это, потому что функция CFNumberCreate() требует, чтобы мы передали значение, которое мы хотим поместить в CFNumber, передав адрес переменной, которая содержит фактическое значение. Поэтому для этой цели используется переменная temperatureAddress. Обратите также внимание, что мы указываем тип CFNumber как kCFNumberLongLongType.
  • строки 13–17: они доказывают, что на самом деле используется тип kCFNumberSInt64Type, а не kCFNumberLongLongType.
  • строки 24–29: они демонстрируют, как можно вернуть значение, стоящее за temperatureAddressNumberRef, и преобразовать его в адрес, указывающий на структуру Temperature. Пользовательская функция PrintTemperature() используется для печати содержимого, результат которой доказывает, что все работает как положено.
  • строка 34: Не забываем освобождать ресурсы за CFNumber с помощью функции CFRelease().

Значение указателя в куче памяти

Мы знаем, что переменная типа указателя содержит значение, указывающее на адрес в памяти. Следовательно, перенос его значения в CFNumber мало чем отличается от предыдущего случая.

  • строка 6: мы хотим обернуть значение, хранящееся внутри pTemperature. Следовательно, достаточно передать адрес этой переменной ((const void *)&pTemperature) в качестве последнего аргумента в CFNumberCreate(). Следовательно, pTemperatureNumberRef будет содержать адрес памяти, на который также указывает pTemperature.
  • Остальная часть программы доказывает, что все работает так, как ожидалось. Мы получаем значение позади pTemperatureNumberRef, используя CFNumberGetValue(), а затем обрабатываем его как адрес структуры Temperature, хранящейся в куче.
  • Мы не должны забывать освобождать как ресурсы за pTemperatureNumberRef, с вызовом CFRelease(), так и память, выделенную в куче для хранения структуры Temperature.

Адрес глобальной функции

Последний пример, над которым мы работаем в этом посте, — это упаковка адреса функции в виде CFNumber. Это не сильно отличается от двух предыдущих случаев, потому что этот случай, как и предыдущие, включает преобразование адреса памяти в CFNumber.

  • строки 4–6: мы объявляем и определяем функцию с именем myFunc и прототипом int ()(int). Он принимает в качестве входных данных целое число и возвращает целочисленное значение, увеличенное на 1.
  • строки 9–15: так мы получаем адрес функции и заключаем его в CFNumber типа kCFNumberLongLongType. Как видите, мы не можем просто передать myFunc в качестве последнего аргумента функции CFNumberCreate(). Это связано с тем, что CFNumberCreate() ожидает, что последним аргументом будет адрес переменной, которая содержит значение, которое мы хотим обернуть. Следовательно, мы сначала определяем переменную myFuncAddress, чтобы иметь в качестве значения адрес функции myFunc, а затем мы передаем адрес этой переменной в качестве последнего аргумента функции CFNumberCreate() ((const void *)&myFuncAddress).
  • строки 21–25: Как и в предыдущих двух случаях, фактический тип за созданным CFNumber — это kCFNumberSInt64Type, а не kCFNumberLongLongType.
  • строки 32–36: Возвращаем значение с помощью функции CFNumberGetValue(). Мы сохраняем значение в новую локальную переменную с именем myFuncAddressValue. Команды printf используются для печати значения и подтверждения того, что мы получаем ожидаемые результаты.
  • строка 41: Эта строка доказывает, что значение, хранящееся внутри myFuncAddressValue, действительно является адресом функции, которая принимает в качестве входных данных целое число и возвращает обратно целое число, увеличенное на 1. Посмотрите, как мы приводим myFuncAddressValue к int (*)(int) и вызываем функцию с использованием (2). Результат, напечатанный в следующей строке, — 3.

Заключительное примечание

В этом посте показаны различные случаи переноса чисел C в числа Core Foundation, также известные как CFNumber. Это оказывается весьма полезным, когда вам нужно использовать Core Foundation API, который использует числа в качестве входных или выходных аргументов.

Ваши отзывы всегда более чем приветствуются. Я всегда учусь у вас, даже больше, чем вы могли бы учиться у меня.