I'm trying to learn objective-c. I'm trying to parse a binary file doing the following (simplified):
#interface DatFile : NSObject {
NSData* _data;
}
-(id)initWithFilePath:(NSString *)filePath;
-(void) readFile;
-(void) auxiliaryMethod;
#implementation DatFile
- (id) initWithFilePath:(NSString *)filePath {
if ( self = [super init] ) {
_data = [NSData dataWithContentsOfFile:filePath];
}
return self;
}
-(void) readFile {
int header;
[_data getBytes: &header range: NSMakeRange(0, 4)];
NSLog(#"header: %u", header);
short key;
[_data getBytes: &key range: NSMakeRange(4, 2)];
NSLog(#"key: %u", key);
short value;
[_data getBytes: &value range: NSMakeRange(6, 1)];
NSLog(#"value: %u", value);
[self auxiliaryMethod];
}
-(void) auxiliaryMethod {
short value;
[_data getBytes: &value range: NSMakeRange(6, 1)];
NSLog(#"value: %u", value);
}
My problem is that the code inside the auxiliaryMethod does not compute the same value computed by readFile method. Since the _data object is the same, why the method are computing different values? And, as you can see, the logic inside the auxiliaryMethod is just a copy of the other one...
In other languages (java) I usually separate some logic in smaller methods in order to make the code mode readable/maintainable. This is why I'm trying to mimic it with ObjC.
Of course that probably missing something, but after some hours, I gave up. I don't see where is my mistake. Probably I should erase my project and start it again...
TIA,
Bob
%u is for printing a 32 bit unsigned value. A short is 16 bits. Therefore, you are printing the parsed value plus some stack garbage.
Try %hu; the h modifies %u to print a short value.
If that doesn't work:
make sure your data objects are the same between the two methods
(don't see how they can't be... but...)
show the actual values printed
decode into an unsigned long that is explicitly initialized to 0 before decoding
With this kind of bug, it is pretty much guaranteed it is either that you aren't dealing with the input you think you are or you are dealing with some C-ism related to data types and the implied conversions in this kind of code.
I.e. assuming _data is consistent, then it indicates that the code you think is working is not actually working, but only appearing to by coincidence.
The problem is that you're passing a range of size 1 to getBytes:range:, but a short is 2 bytes in size. So getBytes:range: is only setting one of value's bytes, and the other contains random garbage.
If you really only want to get one byte from the data, change the type of value to int8_t. If you want to get two bytes, pass 2 as the second argument of NSMakeRange (and I recommend also changing the type of value to int16_t).
Related
I am quite new to objective-c and I am trying to convert an int into a NSNumber so that I can save it into Core-Data.
I've the following piece of code (index is an NSInteger)
- (void) associateOrNotToARoutine:(NSString*)exerciseName associate:(BOOL)associate index:(NSInteger)index
NSLog(#"number w index %d, %d",[NSNumber numberWithInteger:index],index);
and it returns
number w index 170413600, 2
I need an int of 2 to be translated into a number 2 along with all other numbers to be translated into the correct number... Could anyone tell me why i am getting this convertion? I tried reading on NSNumber manual but i found nothing
Try:
NSLog(#"number w index %#, %d",[NSNumber numberWithInteger:index],index);
^^
The %# format specifier will call the [NSNumber description] method, which should return the value you are after. Your original code will return the address of the NSNumber object, not its content.
Even though this question has already been answered, I thought I'd flesh out a longer answer for future readers in general:
What's happening?
%d is a C format string used to indicate one of the passed parameters is an integer (int) ivar value. Much like %f is used for float values.
[NSNumber numberWithInteger:index] returns a pointer to an NSNumber instance. If you use %d, NSLog thinks you're passing it an integer when, in fact, you're passing a pointer. So the pointer value (a memory address) is printed.
What's %#?
As mentioned by trojanfoe: %# tells NSLog() that you are passing an object. In that case, NSLog asks the object to describe itself using a string… it calls the description method.
Specific answer
For this specific question, there are multiple ways. The two main one being:
NSLog(#"number w index %#, %d", [NSNumber numberWithInteger:index], index);
NSLog(#"number w index %d, %d", [[NSNumber numberWithInteger:index] intValue], index);
Extra goodness
When using %#, the passed object can be anything that responds to description, essentially any descendant of NSObject. Also, if you're creating your own classes, it's a good idea to overload description to return a more meaningful string than the default NSObject implementation.
// Try using it with NSArray or NSDictionary and see how each describe themselves.
NSLog(#"the array description: %#", myArray);
NSLog(#"the dictionary description: %#", myDictionary);
You should use,
[[NSNumber numberWithInteger:index] intValue]
to get the integer value, the NSNumber, is holding
I've tried to search, but I think my google is skill is not good enough, so I'm asking a help from people. I'm scanning through a string and taking each element. I need to find out whether it's number or text. I did my checking in that way:
// Try to convert to double
#try{
double whatever=[myString doubleValue];
// If text is stored in myString, then it should throw an exception
} #catch (NSException *e){
// Do whatever I need, but here I know that it's text, not a number
}
However I found out that Objective-C doesn't throw an exception, it just returns 0. I can't rely on the fact that user won't use zero as an input, what should I do?
Thanks in advance and I'm pretty sure there are treads like this, I just didn't find them...
Try this:
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber *number = [formatter numberFromString:myString];
if (number) {
// it's a number
} else {
// it's not a number
}
Convert to double and back to string. If it matches the source, then it was a double.
Careful with leading and trailing spaces.
You can use the class NSScanner to find out if you got a valid double with this method :
- (BOOL)scanDouble:(double *)doubleValue
once you have initialized your scanner with your string.
Why (double *) ?
Because you want the method to return to you 2 informations, the doubleValue and is it a valid double. The latter is given by the BOOL and for the former only a Pointer can get you a value out of a method call (apart from the return value)
Parameters
doubleValue
Upon return, contains the scanned value. Contains HUGE_VAL or –HUGE_VAL on overflow, or 0.0 on underflow.
Return Value
YES if the receiver finds a valid floating-point representation, otherwise NO.
NSMutableString *stringElement = [NSMutableString stringWithCapacity:[yourString length]];
for (int i=0; i<[yourString length]; i++) {
if (isdigit([stringElement characterAtIndex:i])) {
//is digit
}
}
Example:
NSError* error;
NSString* string=#"10";
NSNumberFormatter* formatter=[NSNumberFormatter new];
formatter.numberStyle= NSNumberFormatterDecimalStyle; // set whatever style you need
NSNumber* number;
if([formatter getObjectValue: &number forString: string errorDescription: &error])
{
// You got the number
NSLog(#"%#",number);
}
else
{
// haven't got the number, the string is invalid
NSLog(#"%#",error);
}
To ensure that a formatted string returned by NSString initWithFormat:arguments: is as expected, I need to determine if there are the same number of format specifiers as arguments. Below is a (slightly contrived and highly edited) example:
- (void)thingsForStuff:(CustomStuff)stuff, ...
{
NSString *format;
switch (stuff)
{
case CustomStuffTwo:
format = #"Two things: %# and %#";
break;
case CustomStuffThree:
format = #"Three things: %#, %#, and %#";
break;
default:
format = #"Just one thing: %#";
break;
}
va_list args;
va_start(args, method);
// Want to check if format has the same number of %#s as there are args, but not sure how
NSString *formattedStuff = [[NSString alloc] initWithFormat:format arguments:args];
va_end(args);
NSLog(#"Things: %#", formattedStuff);
}
Using this method, [self thingsForStuff:CustomStuffTwo, #"Hello", #"World"] would log
"Two things: Hello and World"
...but [self thingsForStuff:CustomStuffTwo, #"Hello"] would log
"Two things: Hello and "
...something that would be preferred to be caught before it happens.
Is there a way to count the format specifiers in a string, preferably something lightweight/inexpensive?
Well, I created my own regex, I have no idea if it's going to catch all of them, and may end finding some false positives, but seems to be working for me:
static NSString *const kStringFormatSpecifiers =
#"%(?:\\d+\\$)?[+-]?(?:[lh]{0,2})(?:[qLztj])?(?:[ 0]|'.{1})?\\d*(?:\\.\\d+)?[#dDiuUxXoOfeEgGcCsSpaAFn]";
You can count the number of arguments using:
NSRegularExpression *regEx = [NSRegularExpression regularExpressionWithPattern: kStringFormatSpecifiers options:0 error:nil];
NSInteger numSpecifiers = [regEx numberOfMatchesInString: yourString options:0 range:NSMakeRange(0, yourString.length)];
Is there a way to count the format specifiers in a string, preferably
something lightweight/inexpensive?
Nope -- really isn't. At least, not if you want it to work across all possible format strings. You would have to duplicate the parser that is used by stringWithFormat:. I.e. don't try to validate everything.
You could count the number of %, but that would not catch things like %% or other special cases. That may be good enough for your purposes.
Because of the way C and Objective-C handle variadic functions/methods like yours, you cannot in general tell how many arguments the user has provided.
Here are two ways to handle your situation.
First, look for another way to do this. The number of arguments you pass to the method is determined at compile-time. So maybe instead of using a variadic method, you should just have three methods:
- (void)doStuff:(CustomStuff)stuff withThing:(Thing *)thing;
- (void)doStuff:(CustomStuff)stuff withThing:(Thing *)thing1 thing:(Thing *)thing2;
- (void)doStuff:(CustomStuff)stuff withThing:(Thing *)thing1 thing:(Thing *)thing2 hatWearer:(Cat *)cat;
And you select the right method to call at compile-time based on how many arguments you want to pass, eliminating the switch statement entirely.
Second, I see that your predefined format strings only use the %# format. Does this mean that you expect the user to only pass objects to your method (aside from the (CustomStuff)stuff argument)?
If the user will only pass objects to your method, and you require those arguments to be non-nil, then you can get the compiler to help you out. Change your method to require the user to pass nil at the end of the argument list. You can tell the compiler that the argument list has to be nil-terminated by declaring the method (in your #interface) like this:
#interface MyObject : NSObject
- (void)thingsForStuff:(CustomStuff)stuff, ... NS_REQUIRES_NIL_TERMINATION
#end
Now the compiler will warn the user “Missing sentinel in method dispatch” if he calls your method without putting a literal nil at the end of the argument list.
So, having changed your API to require some non-nil arguments followed by a nil argument, you can change your method to count up the non-nil arguments like this:
- (void)thingsForStuff:(CustomStuff)stuff, ... {
int argCount = 0;
va_list args;
va_start(args, stuff);
while (va_arg(args, id)) {
++argCount;
}
va_end(args)
int expectedArgCount;
NSString *format;
switch (stuff) {
case CustomStuffTwo:
expectedArgCount = 2;
format = #"Two things: %# and %#";
break;
case CustomStuffThree:
expectedArgCount = 3;
format = #"Three things: %#, %#, and %#";
break;
// etc.
}
NSAssert(argCount == expectedArgCount, #"%# %s called with %d non-nil arguments, but I expected %d", self, (char*)_cmd, argCount, expectedArgCount);
va_start(args, stuff);
NSString *formattedStuff = [[NSString alloc] initWithFormat:format arguments:args];
va_end(args);
NSLog(#"Things: %#", formattedString);
}
You could count the number of format specifiers, but IIRC you will never be able to count the number of arguments passed into a variable-argument method. This is because of the way C pushes arguments on the stack without specifying how many it has pushed.
Most functions overcome this by requiring that the last argument be nil or some kind of terminator (see [NSArray arrayWithObjects:]). There's even a macro that allows the compiler to check this and emit a warning at compile time.
You can use NS_FORMAT_FUNCTION at the end of your function prototype, as in stringWithFormat method of NSString.
So your method's prototype should be like this:
- (void)thingsForStuff:(CustomStuff)stuff, ... NS_FORMAT_FUNCTION(1,2);
long specifierCount = [myFormatString componentsSeparatedByString:#"%"].count;
This will get you close. Its just a simple split. You would have to account for escaped % values.
I'm using a 3rd party library for an iOS project I work on, and I'm down to one warning left in the project, namely on this line of code
[NSNumber numberWithUnsignedLongLong:'oaut']
And the warning is
Multi-character character constant
I suck at C, so I don't know how to fix this, but I'm sure the fix is relatively easy. Help?
EDIT: More context.
#implementation MPOAuthCredentialConcreteStore (KeychainAdditions)
- (void)addToKeychainUsingName:(NSString *)inName andValue:(NSString *)inValue {
NSString *serverName = [self.baseURL host];
NSString *securityDomain = [self.authenticationURL host];
// NSString *itemID = [NSString stringWithFormat:#"%#.oauth.%#", [[NSBundle mainBundle] bundleIdentifier], inName];
NSDictionary *searchDictionary = nil;
NSDictionary *keychainItemAttributeDictionary = [NSDictionary dictionaryWithObjectsAndKeys: (id)kSecClassInternetPassword, kSecClass,
securityDomain, kSecAttrSecurityDomain,
serverName, kSecAttrServer,
inName, kSecAttrAccount,
kSecAttrAuthenticationTypeDefault, kSecAttrAuthenticationType,
[NSNumber numberWithUnsignedLongLong:"oaut"], kSecAttrType,
[inValue dataUsingEncoding:NSUTF8StringEncoding], kSecValueData,
nil];
if ([self findValueFromKeychainUsingName:inName returningItem:&searchDictionary]) {
NSMutableDictionary *updateDictionary = [keychainItemAttributeDictionary mutableCopy];
[updateDictionary removeObjectForKey:(id)kSecClass];
SecItemUpdate((CFDictionaryRef)keychainItemAttributeDictionary, (CFDictionaryRef)updateDictionary);
[updateDictionary release];
} else {
OSStatus success = SecItemAdd( (CFDictionaryRef)keychainItemAttributeDictionary, NULL);
if (success == errSecNotAvailable) {
[NSException raise:#"Keychain Not Available" format:#"Keychain Access Not Currently Available"];
} else if (success == errSecDuplicateItem) {
[NSException raise:#"Keychain duplicate item exception" format:#"Item already exists for %#", keychainItemAttributeDictionary];
}
}
}
EDIT 2: They were attempting to meet the requirements of this by creating that NSNumber:
#constant kSecAttrType Specifies a dictionary key whose value is the item's
type attribute. You use this key to set or get a value of type
CFNumberRef that represents the item's type. This number is the
unsigned integer representation of a four-character code (e.g.,
'aTyp').
In C and Obj-C the single-quote ' is used only for single-character constants. You need to use the double-quote: "
Like so:
[NSNumber numberWithUnsignedLongLong:"oaut"]
That covers the warning, but there's also a semantic issue here. Although a single character constant, such as 'o', can be treated as an integer (and can be promoted to an unsigned long long), a "string" (char * or char []) cannot, which means you can't use "oaut" as an argument to numberWithUnsignedLongLong:
Update:
I guess the four-character code is supposed to be treated as an integer, i.e., the 8 bits of each char put in place as if they together were a 32-bit int:
char code[] = "oaut";
uint32_t code_as_int = code[0] | (code[1] << 8) | (code[2] << 16) | (code[3] << 24);
[NSNumber numberWithUnsignedLongLong:code_as_int]
although I'm not sure which endianness would be expected here, nor why this is calling for an unsigned long long, unless just to be certain there are enough bits.
Rudy's comment, now that I think of it, is correct -- multi-character constants are allowed by some compilers for exactly this purpose (it is "implementation-defined" behavior).
'oaut' (single quotes) is a character, so the compiler tries to interpret it as a multi-byte character but can't make any sense of it. That explains the error message.
I guess that if you gave a proper string, like #"oaut", you'd get another error message, since numberWithUnsignedLongLong: expects an unsigned long long, not a string or a character. Are you trying to pass a variable with the name "oaut"? If so, use
[NSNumber numberWithUnsignedLongLong: oaut];
If not, then please explain what "oaut" is.
Edit
'oaut' may actually be the original value. There are/were multi-character character constants in C. Using a (4 byte) char, used as int and promoted to unsigned long long would then be possible. This must be old code. It seems such code was accepted by CodeWarrior.
Assuming that really a multi-char char const was meant, 'oaut' looks like a "magic number" and this value was chosen because it is the beginning of "oauth". I guess it should either be value 0x6F617574 or 0x7475616F.
#Josh Caswell 's answer is partially right, the simplest and "official" solution is:
[NSNumber numberWithUnsignedInt:'oaut']
unsigned int's length is 32-bit in both 32-bit and 64-bit cpu, there's a practical example from Apple: https://developer.apple.com/library/ios/samplecode/CryptoExercise/Listings/Classes_SecKeyWrapper_m.html
I want to have a GUID in my objective-c model to act as a unique id. My problem is how to save the CFUUIDRef with my NSCoder as its not a an Object type.
I keep playing around with the following lines to encode/decode but I can't seem to find any good examples of how to save struct types in objective-c (all of my NSObject types are encoding/decoding fine).
e.g. for encoding I am trying (which I think looks good?):
CFUUIDBytes bytes = CFUUIDGetUUIDBytes(uuid);
eencoder encodeBytes: &bytes length: sizeof(bytes)];
and for decoding which is where I get more stuck:
NSUInteger blockSize;
const void* bytes = [decoder decodeBytesForKey: kFieldCreatedKey returnedLength:&blockSize];
if(blockSize > 0) {
uuid = CFUUIDCreateFromUUIDBytes(NULL, (CFUUIDBytes)bytes);
}
I gt an error "conversion to a non-scaler type" above - I've tried several incarnations from bits of code I've seen on the web. Can anyone point me in the right direction?
Tim
The easier (but a bit more inefficient) way is to store it as an NSString (CFString) using CFUUIDCreateString, and recover the UUID with CFUUIDCreateFromString.
The problem with your code is the last line of the decoding, "bytes" is a pointer to a CFUUIDBytes struct and you're trying to cast it as if it is the CFUUIDBytes struct itself, which is not correct and is correctly detected by the compiler. Try changing the last line to:
uuid = CFUUIDCreateFromUUIDBytes(NULL, *((CFUUIDBytes*)bytes));
The idea here is that you cast "bytes" to be a pointer to CFUUIDBytes (inner brackets) and then dereference the casted pointer (outer brackets). The outer brackets are not strictly necessary but I use them to make the expression clearer.
Based on the answers given, I tried the casting approach given by Eyal and also the NSData approach suggested by Rob, and I thought that the latter seemed clearer, although I am interested in what others think.
I ended up with the following:
- (void)encodeWithCoder:(NSCoder *)encoder {
// other fields encoded here
CFUUIDBytes bytes = CFUUIDGetUUIDBytes(uuid);
NSData* data = [NSData dataWithBytes: &bytes length: sizeof(bytes)];
[encoder encodeObject: data forKey: kFieldUUIDKey];
}
- (id)initWithCoder:(NSCoder *)decoder {
if (self = [super init]) {
// other fields unencoded here
NSData* data = [decoder decodeObjectForKey: kFieldUUIDKey];
if(data) {
CFUUIDBytes uuidBytes;
[data getBytes: &uuidBytes];
uuid = CFUUIDCreateFromUUIDBytes(NULL,uuidBytes);
} else {
uuid = CFUUIDCreate(NULL);
}
}
}