Подтвердить что ты не робот

Решение проблемы NSInteger ↔ NSNumber

Я написал большое приложение для iPhone в социальной сети, и одной из самых больших проблем, с которыми я сталкиваюсь, является тот факт, что NSInteger (и все другие типы NS-не-объектов) не являются гражданами первого класса. Эта проблема связана с тем, что, очевидно, у них нет представления для значения nil.

Это создает две основные проблемы:

  • Тонны накладных расходов и непрозрачность для преобразования в NSNumber и из него при хранении/извлечении из коллекции.
  • Невозможно представить nil. Зачастую я хочу иметь возможность представлять "неустановленное" значение.

Один из способов решить это - использовать NSNumber все время, но это очень запутанно. В объекте модели пользователя у меня было бы около 20 различных NSNumbers, и не было бы простого способа определить, является ли каждый из них float, integer, bool и т.д.

Итак, вот мои мысли о потенциальных решениях и плюсах/минусах. Я не продаюсь никому из них, поэтому я подумал, что попрошу обратной связи и/или альтернативных решений этой проблемы.

  • Продолжайте использовать типы NSInteger и просто используйте NSIntegerMax для представления nil.
    PRO - Меньше памяти наверх
    PRO - Четкая печать
    CON - NSIntegerMax на самом деле ничто. Если программисты не внимательны или не знают этого соглашения, недопустимые значения могут просачиваться в уровень отображения.
    CON - Невозможно сохранить их в коллекции без конверсий.

  • Использовать NSNumber и обозначать типы с использованием венгерской нотации (например, NSNumber fHeight, NSNumber iAge)
    PRO - первоклассные граждане
    Проблема PRO-Nil решена
    CON - Увеличение объема памяти
    CON - Lose проверка типа компилятора
    CON - венгерская нотация спорна

  • Напиши свои собственные первообразные типы примитивных объектов (подумайте о Java http://developer.android.com/reference/java/lang/Integer.html)
    PRO - первоклассные граждане
    Проблема PRO-Nil решена
    PRO - Сохраняет проверку типа компилятора
    PRO - Объекты будут проще NSNumber. Внутреннее хранилище будет специфичным для типа данных.
    CON - Увеличение объема памяти
    CON - жертвует немного переносимости кода и совместимости

Ищете убедительный аргумент в пользу одного из этих методов, или того, о котором я не думал, если у вас его есть.


UPDATE

Я пошел вперед и начал проект с открытым исходным кодом (Apache 2.0), в который я буду тянуть ряд наших внутренних классов, поскольку у меня есть время. В настоящее время он включает обертки объектов для некоторых наиболее распространенных типов данных (BOOL, CGFloat, NSInteger, NSUInteger). Мы решили сделать это, потому что он обновляет эти типы данных до первоклассных граждан со строгой типизацией. Может быть, вы не согласны с этим подходом, но это сработало для нас, поэтому не стесняйтесь использовать его, если хотите.

Я добавляю другие классы, которые мы использовали для использования, включая кеш LRU с поддержкой диска, объект "Pair", пул с низкой памятью и т.д.

Enjoy github - Zoosk/ZSFoundation

4b9b3361

Ответ 1

Наиболее распространенным соглашением для представления идеи nil как NSInteger является использование значения NSNotFound. Это, по сути, равно NSIntegerMax, хотя для читателя, как правило, более очевидно, что это значение дозорного, представляющее отсутствие числа. Существует много случаев, когда это используется во всем Cocoa. Один общий случай - это поле местоположения NSRange как возвращаемое значение от -rangeOfString: и др.

Ответ 2

Вы можете попробовать это?

#define numberWithFloat(float f) \
  [NSNumber numberWithFloat:f]
#define floatFromNumber(NSNumber *n) \
  [n floatValue]

(см. мой первоначальный ответ ниже)

Здесь другая вещь с NSNumber, вам не нужно извлекать то, что вы установили.

Например

NSNumber *myInt = [NSNumber numberWithInteger:100];
float myFloat = [myInt floatValue];

отлично. Сила NSNumber заключается в том, что она позволяет вам "слабые типы" ваших примитивов, используйте compare:, используйте isEqualTo: и stringValue для удобства отображения.


[EDIT]

Пользователь @Dave DeLong говорит, что подкласс Class NSNumber является Bad Idea без большой работы. Поскольку это кластер классов (что означает, что NSNumber является абстрактным суперклассом для множества подклассов), вы должны будете объявить свое собственное хранилище, класс его. Не рекомендуется, и спасибо Дэйву за указание на это.

Ответ 3

В качестве альтернативы существует также NSDecimal struct для представления числовых типов. NSDecimal (и его версия класса Objective-C NSDecimalNumber) позволяет представлять истинные десятичные знаки и избегать ошибок с плавающей запятой, поэтому она обычно рекомендуется для работы с такими вещами, как валюта.

NSDecimal struct может представлять числа, а также не состояние числа (потенциальная замена нуля). Вы можете запросить, является ли NSDecimal не числом с помощью NSDecimalIsNotANumber(), и сгенерируйте значение Not a Number с помощью NSDecimalNumber.

NSDecimals быстрее работают, чем NSDecimalNumbers, и структуры не приносят того же типа проблем управления памятью, что и объекты.

Однако нет простого способа получить значения в форме NSDecimal без использования временного NSDecimalNumber. Кроме того, многие математические функции (например, операции тригонометрии), доступные для простых чисел с плавающей запятой, еще недоступны для NSDecimal. Я хотел бы написать свои собственные функции, которые добавляют некоторые из этих возможностей, но они потребуют доступа к внутренним полям структуры. Apple отмечает это как личное (с использованием подчеркивания), но они присутствуют в заголовках, и я предполагаю, что они вряд ли изменятся в будущем.

Обновление: Дэйв ДеЛонг написал ряд функций для выполнения тригонометрии NSNecimal, корней и других операций. Я изменил их, чтобы обеспечить до 34 цифр десятичной точности, а код для этих функций можно загрузить здесь. Как предупреждение, эта точность имеет цену с точки зрения производительности, но эти функции должны быть хорошими для довольно редких вычислений.

Ответ 4

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

Если вы не можете указать, какой тип подходит для имени переменной, вам нужно выбрать лучшие имена. например

numberOfChildren = [NSNumber numberWithFloat: 2.5];

просто глупо.

И для валютных значений я определенно рекомендую использовать NSDecimalNumber (который является подклассом NSNumber), но определяет арифметические операции базы 10, чтобы вы не нужно беспокоиться о округлении поплавка.