#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");