Может ли метод класса NSNumber различать signed и unsigned int?

#objective-c

#objective-c

Вопрос:

Есть ли какой-либо способ проверить NSNumber, чтобы увидеть, является ли это int или unsigned int. Я пытался сделать это с objCType, но я не могу определить разницу. Рассмотрим следующий сценарий:

 NSNumber *number1 = [NSNumber numberWithUnsignedInt:100];
NSNumber *number2 = [NSNumber numberWithInt:100];

NSLog(@"%@",[NSString stringWithUTF8String:[number1 objCType]]);
NSLog(@"%@",[NSString stringWithUTF8String:[number2 objCType]]);
  

Вывод:
я
я

Кто-нибудь знает, как решить эту проблему?

Ответ №1:

objCType не гарантирует, что тип, который он возвращает, будет таким же, как тип, который вы ввели.

То, что вы пытаетесь сделать, по сути, игнорирует смысл NSNumber , который заключается в том, чтобы избежать неуклюжести, с которой необходимо обрабатывать числовые значения в C. Заключая числовое значение в NSNumber , вы можете сравнить его с любым другим подобным значением без учета их «фактических» (побитовых) типов, и вы можете получить значение обратно в другом «фактическом» типе, чем вы начали.*

Он не возвращает вам точно такой же тип, потому что в этом нет необходимости — вы вводите все, что хотите, и извлекаете все, что хотите. В любом случае, тип переменной C неизвестен во время выполнения.

Я думаю, вы должны использовать NSNumber , потому что вам нужно вставить числовые значения в коллекцию Cocoa? Возможно, вам придется обернуть NSNumber в свой собственный объект, который также хранит запись начального типа. Для этого потребуется много switch или несколько if / else секунд…


* Единственное предостережение здесь заключается в том, что теперь вы должны снова беспокоиться о размере типа: попытка поместить значение, большее, чем FLT_MAX в float , например, приводит к мусору.

Ответ №2:

Почему это проблема?

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

Тип числа действительно имеет значение только при создании.

Комментарии:

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

Ответ №3:

Нет. Под капотом NSNumber используется CFNumber , и последний не хранит значения без знака — методы NSNumber проверяют переданное значение, и если оно слишком велико, чтобы поместиться в подписанный тип того же размера, он использует следующий более крупный подписанный тип. (И да, если вы храните большое 64-разрядное целое число без знака NSNumber , используется внутреннее 128-разрядное целое число со знаком.)

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

Ответ №4:

Вы можете расширить NSNumber категорию использования.

 @implements NSNumber (Signed)
    - (BOOL)isNumberSigned {
        // Test if number less than zero
        // return the result
    }
  

В этом прелесть Objective-C; расширить класс очень просто.

Ответ №5:

Использование [- doubleValue] , [- stringValue] или [- decimalValue] может выявить, было ли исходное значение отрицательным при создании с использованием метода инициализации со знаком. Я удивлен, что нет простой - wasCreatedSigned функциональности.

 BOOL negTest;
negTest = ([myNSNumber doubleValue] < 0);                 // <0.1 micro sec
negTest = [[myNSNumber stringValue] hasPrefix:@"-"];      // >1.5 micro sec
negTest = ([myNSNumber decimalValue]._isNegative);        // >2.5 micro sec
negTest = ([myNSDecimalNumber decimalValue]._isNegative); // <0.1 micro sec
  

Внутренне NSNumber знает о подписанном состоянии своего значения.

Что касается -objCType я заметил, что он не всегда будет отражать тип инициализации, как указано в документации. Но он будет точно отличать значения с плавающей запятой / double (d) от целых чисел (c, s, i, q, Q …).

Вот некоторый код, с которым можно поиграть, он выявит некоторые странности (протестировано на iOS 4.3):

 void isNeg(NSNumber* num, NSString* initMethod);
void isNeg(NSNumber* num, NSString* initMethod)
{
    printf("r");
    NSLog(@"%@ (class:%@)", initMethod, [num class]);

    double dval = [num doubleValue];
    NSLog(@"Is Negative:%c, objCType:%s", (dval<0)?'Y':'N', [num objCType]);
    NSLog(@"strVal: %@", [num stringValue]);
    NSLog(@"%%f    : %f", dval);
    NSLog(@"%%g    : %g", dval);
    NSLog(@"%%lld  : %lld", [num longLongValue]);
    NSLog(@"%%llu  : %llu", [num unsignedLongLongValue]);
}

// main...
double testDouble = 10001e-9;
Class nc = [NSNumber class];

isNeg([nc numberWithChar:    -1], @"Char");
isNeg([nc numberWithChar:    -2], @"Char");
isNeg([nc numberWithChar:    -3], @"Char");
isNeg([nc numberWithChar:     1], @"Char");
isNeg([nc numberWithChar:    12], @"Char");
isNeg([nc numberWithChar:    13], @"Char");
isNeg([nc numberWithUnsignedChar: 12], @"UChar");
isNeg([nc numberWithUnsignedChar: 13], @"UChar");

isNeg([nc numberWithLongLong:        -LONG_LONG_MAX], @"LongLong");
isNeg([nc numberWithLongLong:         LONG_LONG_MAX], @"LongLong");
isNeg([nc numberWithUnsignedLongLong: ULONG_LONG_MAX], @"ULongLong");

isNeg([nc numberWithDouble:-LONG_LONG_MAX], @"Double");
isNeg([nc numberWithDouble: ULONG_LONG_MAX], @"Double");

isNeg([nc numberWithDouble:-testDouble], @"Double");
isNeg([nc numberWithDouble: testDouble], @"Double");

nc = [NSDecimalNumber class];
isNeg([nc numberWithDouble:-testDouble], @"Double");
isNeg([nc numberWithDouble: testDouble], @"Double");