I want to convert NSValue to NSNumber. What is wrong with this code?
char value[4];
[myNSValue getValue:(void*)value];
NSNumber *number = [[NSNumber alloc] initWithBytes:(void*)value objCType:[myNSValue objCType]];
NSTimeInterval duration = [number intValue];
It causes a crash on the last line. What could happen here?
As #ale0xB diagnosed in the comments above, NSNumber doesn't actually provide its own implementation of the -initWithBytes:objCType: method; so, when you invoke that selector, you're actually getting the implementation that comes from NSNumber's base class NSValue. There is no1 difference between [[NSNumber alloc] initWithBytes:foo objCType:bar] and [[NSValue alloc] initWithBytes:foo objCType:bar] — they both call the same method implementation, which releases the self it was given and returns a new object which is generally of type NSConcreteValue (an undocumented subclass of NSValue but not of NSNumber).
To be even clearer:
#import <assert.h>
#import <Foundation/Foundation.h>
int main()
{
id x;
x = [NSNumber numberWithInt:42];
assert( [x isKindOfClass:[NSNumber class]] );
assert( [x respondsToSelector:#selector(intValue)] );
int fortytwo = 42;
x = [[NSNumber alloc] initWithBytes:&fortytwo objCType:#encode(int)];
assert( [x isKindOfClass:[NSValue class]] );
assert( ! [x isKindOfClass:[NSNumber class]] ); // yikes!
assert( ! [x respondsToSelector:#selector(intValue)] ); // your particular problem
}
However, categories to the rescue! Martin Häcker has written a category NSNumber(CreatingFromArbitraryTypes) which can be adapted to do what you want. See the public-domain source code here.2 The basic idea is to special-case every possible type-encoding:
if you're trying to encode an "i", then dispatch to -numberWithInt:;
if you're trying to encode an "f", then dispatch to -numberWithFloat:;
and so on. This is tedious; but once you've written the code once, you can use the category from then on and simply call [NSNumber numberWithValue:myNSValue].
(1 – More or less. There's no difference significant in this context, I'd say.)
(2 – That code has a couple of bugs, especially in later revisions. See my patch here.)
Please read my comment above for further explanation.
To achieve what you want, simply init an NSString with your (char *) and then invoke NSString's intValue. - Assuming you know the type, if not look at the comments below -
Cheers
Related
I have a function That takes by reference any kind of object
-(BOOL)RemoteCall:(id**)DataClass;
in the implementation i use [*DataClass isMemberOfClass:[NSMutableArray class] to find out the type of the object. The problem is it does not work with NSMUtableArrays Does anybody have a solution to this problem ? Here is the relevant code:
Implementation:
-(BOOL)RemoteCall:(id**)DataClass
{
if([*DataClass isMemberOfClass:[NSMutableArray class] ] == YES)
{
NSMutableArray * SW =(NSMutableArray *)*DataClass;
//do something with SW
DataClass= (id**)SW;
return TRUE;
}
}
Any help and I mean anything at all will be appreciated, I'm stuck.
Method Call:
NSMutableArray * channelArray = [[NSMutableArray alloc]init]
Services * serv = [[Services alloc] init];
return [serv RemoteCall:&channelArray];
Pass by reference in Objective-C is almost never the right way.
There are a number of problems with that code.
(id**) is a pointer to a pointer to a pointer to an object. Probably not at all what you want.
YES and NO are BOOL return types; not TRUE
there is no reason in that code to be returning something by reference.
method names start with lower case letters. Arguments do, too.
There will never be an instance of NSMutableArray in an application; just subclasses
You can't tell the difference between a mutable and immutable array in the first place; check for isKindOfClass: or isMemberOfClass: for an NSMutableArray won't do you much good (it is useful, but misleading).
This is better:
-(BOOL)remoteCall: (id) dataThing
{
if([dataThing isKindOfClass:[NSMutableArray class]] == YES)
{
NSMutableArray *swArray = dataThing; // not strictly necessary, but good defensive practice
//do something with swArray
return YES;
}
return NO;
}
To be called like:
NSMutableArray * channelArray = [[NSMutableArray alloc]init]; // you'll need to release this somewhere
Services * serv = [[Services alloc] init];
return [serv remoteCall:channelArray];
Since you don't return a different array in remoteCall:, channelArray's contents will be manipulated by the method and the YES/NO return value.
If there is some reason why the above seemingly won't work for you, please explain why.
Note: The code obviously requires an NSMutableArray if you are going to muck with the contents. The isKindOfClass: could be checking for NSMutableArray or NSArray and it wouldn't matter either way. When using arrays in your code and requiring a mutable array, it is up to you to make sure the data flow is correct such that you don't end up w/an immutable array where you need a mutable array.
If you don't need to reassign your variable, then don't use this. id or NSObject * is just fine and works by reference anyway. id * or NSObject ** would be references. id ** doesn't make sense at all here.
Also, learn naming conventions (like upper/lowercase).
NSArray is a class cluster. That means that every NSArray instance is actually an instance of some subclass. Only isKindOfClass: is useful for class-membership testing with class clusters.
Also... thats horrible code - please accept this:
-(BOOL)remoteCall:(id)dataClass {
if([dataClass isKindOfClass:[NSMutableArray class]]) {
NSMutableArray *sw =(NSMutableArray *)dataClass;
return YES;
}
}
that should work.
Constructive critisism of coding: You need to adhere to coding conventions. Although your code will work... its not brilliant to read and theres a lot of unnecessary *s and such.
Function names should be camel coded with a preceeding lower-case letter as should variable names. Passing (id) into a function doesn't require *s at all. Objects you pass into a function only available throughout the scope of the method anyway and that method doesn't own it, I'm not sure what you're trying to do with all the extra *s, but just treat objects you pass into the method as if you don't own them. :)
As Eiko said before, i'd use just id and not double pointers to ID.
I'm also pretty sure that isMemberOfClass is your Problem. isMember does not check for inheritance, so you're only asking for Top level Classes. isKindOfClass is probably the better choice, as there is no guarantee that Apple doesn't use an internal subclass of NSMutableArray internally. Check the Apple Docs.
i'd write it as such:
-(BOOL)RemoteCall:(id)dataClass
{
if([dataClass isKindOfClass:[NSMutableArray class] ] == YES)
{
NSMutableArray * SW =(NSMutableArray *)dataClass;
//do something with SW
return TRUE;
}
}
for a day now I stare at the following routine and can't get my grips around it. I have a class such as:
MyClass.h
...
#interface MyClass : NSObject {
NSNumber *myNumber1;
NSNumber *myNumber2;
}
#property (nonatomic, retain) NSNumber *myNumber1;
#property (nonatomic, retain) NSNumber *myNumber2;
#end
and the m-file
#import "MyClass.h"
#synthesize myNumber1, myNumber2;
#implementation MyClass
-(id) init {
self = [super init];
NSLog(#"Retain Counts myNumber1: %d, myNumber2: %d", [myNumber1 retainCount], [myNumber2 retainCount]);
myNumber1 = [NSNumber inbitWithint:10];
myNumber2 = [NSNumber inbitWithint:2];
NSLog(#"Retain Counts myNumber1: %d, myNumber2: %d", [myNumber1 retainCount], [myNumber2 retainCount]);
return self;
}
...
I use this class as a globals container and instantiate it from every other class in my app using a
MyClass *myGlobals = [[MyClass alloc] init];
===>>> the weirdo >>>
Running the routine I have the following facinating console output:
Retain Counts (before) - myNumber1: 0, myNumber2: 0
Retain Counts (after) - myNumber1: 1, myNumber2: 26
How can that be?
Do not call `retainCount`
The return value of retainCount can only be considered accurate if you are calling it on an instance of a class you wrote and you have never ever not even once passed said instance to any API provided by the system.
The absolute retain count is an implementation detail for which you might not have any control.
Assuming, for the moment, that your code was typed and, thus, the errors are not really in your original code...
NSNumber caches a subset of common values. That is, for some numeric values, there is a single instance of NSNumber that is returned for all requests to retrieve an NSNumber instance wrapping that number.
If this is your actual code, it shouldn't work at all, it should simply crash. If not, you should cut and paste your actual code.
However, a couple of things.
First, if you use an NSNumber as an ivar, like anything else, you must take ownership of it. If you plan to use a convenience constructor, you must either assign it using the property on self, or send it an explicit retain message.
Second, you typed something like initWithInt: here. If you are actually using that message, then you have never actually allocated the number in the first place. You must replace this with one of the following:
myNumber1 = [[NSNumber alloc] initWithInt:10]; // explicitly create the number
myNumber1 = [[NSNumber numberWithInt:10] retain]; // take ownership through retain
[self setMyNumber1:[NSNumber numberWithInt:10]]; // use the property accessors to deal with ownership and the convenience constructor to create the number
However you choose to do it, you must at some point take ownership of the object.
You should never even look at retainCount as it carries little significance anyway. The reason you see the count of 26 is that probably somewhere in the frameworks (or even in your own app) other instances of NSNumber exist that are created with the same int 2. An NSNumber is immutable, and so as an optimization NSNumber probably just gives you back an instance it has created earlier. As long as you don't look at retainCount it doesn't matter to you.
OK, This is a method from my program that keeps giving the EXC_BAD_ACCESS error and crashing. I indicated the line below. questionsShown is a readwrite property and points to an NSMutableArray that I initialize with a capacity of 99 at an earlier point in the program. When I debug everything appears normal in terms of the property being allocated. I assumed there must be some issue with memory management but I am having serious trouble finding the problem. Thanks in advance for any help.
#synthesize questionList;
#synthesize questionLabel;
#synthesize questionsShown;
-(IBAction)next{
int numElements = [questionList count];
int r;
if (myCount == numElements){
[questionLabel setText:#"You have seen all the questions, click next again to continue anyways."];
[questionsShown release];
questionsShown = [[NSMutableArray alloc] initWithCapacity:99];
myCount = 0;
}
else {
do {
r = rand() % numElements;
} while ([questionsShown indexOfObjectIdenticalTo:r] != NSNotFound);
NSString *myString = [questionList objectAtIndex:(NSUInteger)r];
[questionLabel setText:myString];
myCount++;
[questionsShown addObject:r]; //results in crash with message EXC_BAD_ACCESS
myCount++;
}
}
The EXC_BAD_ACCESS is coming from dereferencing r, which is just an integer. Your compiler should be giving you a warning (make pointer from integer without a cast) on that line.
If questionsShown is supposed to be some kind of index set for you (which it appears to be), you might want to either use that class, or you will have to box your integer in an NSNumber object. So:
[questionsShown addObject:[NSNumber numberWithInt:r]];
and when you read it:
[questionsShown indexOfObjectIdenticalTo:[NSNumber numberWithInt:r]]
I recommend, however, that you take a look at the NSIndexSet documentation.
With a mutable index set, you could do:
[questionsShownIndexSet containsIndex:r]
and
[questionsShownIndexSet addIndex:r]
I'm having some trouble passing a number as an argument for a method:
- (void)meth2:(int)next_int;
And to call that method I need this:
int next_int = 1;
[self performSelectorOnMainThread:#selector(meth2:) withObject:next_int waitUntilDone:NO];
//update next_int and call meth2 again
at this point, I get a "pointer from integer without a cast" error and would happen the same with a NSInteger. An NSNumber is not useful because it's immutable and I need to change the value constantly.
Any idea how can I do this?
Thanks.
If you're just trying to call the method, you could use the standard syntax:
[self meth2:next_int];
If you really need to use the performSelectorOnMainThread: you could wrap the number in an NSNumber for the call. You say you can't do this because you need to change the number, but you can just pull an int out and change that:
[self performSelectorOnMainThread:#selector(meth2:) withObject:[NSNumber numberWithInt:next_int] waitUntilDone:NO];
// ... later ...
- (void)meth2:(NSNumber *)number {
int myInt = [number intValue];
// do stuff with myInt
}
But maybe you mean that you want to get the value of the number as an output from your call to meth2. If that's what you mean, then you could pass in a double pointer so you can receive a new object back:
- (void)meth2:(NSNumber **)number {
int myInt = [*number intValue];
// do stuff with myInt
*number = [NSNumber numberWithInt:myInt];
}
// The caller can now operate like this:
NSNumber *number = [NSNumber numberWithInt:next_int];
[self performSelectorOnMainThread:#selector(meth2:) withObject:&number waitUntilDone:YES];
int returnInt = [*number intValue];
Of course, that's not really thread-safe, so if you're doing stuff with multiple threads, I would advise using the #synchronized keyword for access to multi-thread-accessed variables, or setting up atomic properties (i.e. properties not declared as nonatomic).
Also, meth is bad for you!! haha
Wrap the integer in an NSNumber before passing it:
int next_int = 1
NSNumber *theNumber = [NSNumber numberWithInt:next_int];
[self performSelectorOnMainThread:#selector(meth2:) withObject:theNumber waitUntilDone:NO];
Then your -meth2: method could look something like this:
- (void)meth2:(NSNumber*)theNumber
{
int next_int = [theNumber intValue];
// do whatever
}
It's a bit of a hack, but this works under ARC:
int next_int = 1;
[self performSelectorOnMainThread:#selector(meth2:)
withObject:(__bridge id)(void*)next_int
waitUntilDone:NO];
The __bridge keyword will tell the compiler to ignore reference counting under ARC, but it requires a pointer, so you first have to cast the int to a C style void pointer. When your method receives the message it will treat that object pointer as if it's an integer.
Note: If you can change the method to take an NSNumber instead of an integer, then that would be the "proper" fix. Unfortunately that's not always possible.
You can't use next_int as the withObject: because it's not an Object.
Change your call to:
[self performSelectorOnMainThread:#selector(meth2:)
withObject:[NSNumber numberWithInt:next_int] waitUntilDone:NO];
EDIT:
And change meth2 to expect an NSNumber instead of an int.
I asked a similar question, but I couldn't get it working exactly. I'm building an iPhone app, and there is a method that I want called from different files. I figured the easiest way would simply be to make a method in another file, and call the method from the other files.
Here are some problems. I need to return multiple values from the method, after passing it multiple values. For example, I'm passing it: (int, int, int, string, string). And it needs to return all of those values, after they have been changed. Someone showed me this code:
- (NSDictionary *)EndOfTurn:(int)varTurns withFatness:(int)varFatness
{
varTurns--;
if (varTurns <= 0) {
varFatness = varFatness - 5;
}
else {
varFatness += 2;
}
return [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:varFatness], #"FATNESS", [NSNumber numberWithInt:varTurns], #"TURNS", nil];
}
However, this code doesn't work, and I need some more information to really understand it. Let's assuming I'm passing it these values:
int varMoney;
int varNumSheep;
int varNumShepherds;
NSString *test1;
NSString *test2;
So I need to get all of these values back from the method.
How do I declare this in the header file? This should be in an Objective-C file, but could you give me the code for the entire file so I can see where it would go with the #implementation and #end, whatnot. Also, how would I call this method?
What about passing in the values as pointers?
For example:
- (void) getValuesForInt:(int *)int1 anotherInt:(int *)int2 aBool:(BOOL *)bool1 anotherBool:(BOOL *)bool2 {
if (*int1 == 42 && *int2 == 0) {
*int1 = 0;
*int2 = 42;
}
if (*bool1 == NO) {
*bool2 = YES;
}
}
Then you can invoke it like:
int int1 = 42;
int int2 = 0;
BOOL bool1 = NO;
BOOL bool2 = NO;
[self getValuesForInt:&int1 anotherInt:&int2 aBool:&bool1 anotherBool:&bool2];
NSLog(#"int1: %d int2: %d bool1: %d bool2: %d", int1, int2, bool1, bool2);
//prints "int1: 0 int2: 42 bool1: 0 bool2: 1"
Edit:
This works equally well with objects. You'll often see this used when dealing with NSError objects:
NSError *error = nil;
[anObject doSomething:foo error:&error];
Can be implemented as:
- (void) doSomething:(id)terrible error:(NSError **)error {
if ([terrible isEqual:reallyBad]) {
if (error != nil) { *error = [NSError errorWithDomain:#"domain" code:42 userInfo:nil]; }
}
}
You can use a block closure to pass back multiple values from a method like this. -rrh
[self heyFunctionGiveMeBackTwoValuesFromThisFruitArray:#[#"apple", #"orange", #"banana", #"apple"] findThisFruit:#"apple" closureFunction:^(int fruitCount, NSString* fruitString)
{
NSLog(#"Two values returned, int-fruitCount:%d, NSString-fruiteString:%#", fruitCount, fruitString);
}];
- (void)heyFunctionGiveMeBackTwoValuesFromThisFruitArray:(NSArray*)fruitsArray findThisFruit:(NSString*)findThisFruit closureFunction:(void (^)(int fruitCount, NSString *fruitString))passBackResultsUsingThisClosure
{
NSInteger fruitsFound = 0;
NSString* fruitsMessage = [NSString stringWithFormat:#"No %# Found", findThisFruit];
for (NSString* string in fruitsArray)
{
if ([string compare:findThisFruit] == NSOrderedSame)
{
fruitsFound++;
}
}
if (fruitsFound > 0)
{
fruitsMessage = [NSString stringWithFormat:#"You have %# on your list this many times:%d", findThisFruit, fruitsFound];
}
passBackResultsUsingThisClosure(fruitsFound, fruitsMessage);
}
Results:
Two values returned, int-fruitCount:2, NSString-fruiteString:You have apple on your list this many times:2
If you have that many different things that need to be returned from a method, either encapsulate it into an NSDictionary as others have suggested or consider just defining a class. You can declare the instance variables and properties to encapsulate the data, as needed.
Defining a class to encapsulate such information proves to be quite efficient and maximizes flexibility. If you need to refactor your app such that the collection of data gains new fields, needs to be saved for later, or might need to gain functionality, a class will ease these changes.
Since you can only return a single value from any method in C and C-derived languages, you simply need to return a single value that represents all of your other values. This is what your sample code is doing with an NSDictionary.
The sample code is correct, even if it's a bit contrary to common Objective-C style.
What you declare in the header file is simply the declaration of the method, that is:
#interface MyClass : NSObject
- (NSDictionary *)EndOfTurn:(int)varTurns withFatness:(int)varFatness;
#end
In the source file, then:
#implementation MyClass
// code, as given above
#end
If you only need to return primitive values, then returning a struct may be the optimal solution. You get compile-time error checking (e.g. as opposed to an NSDictionary where you could attempt to read an invalid key), while not requiring all the code/files involved in creating a class.
typedef struct myStruct {
int varMoney;
int varNumSheep;
int varNumShepherds;
} myStruct;
Apple uses structs in many of their methods too (e.g. CGPoint, CGRect).
The reason this won't work with objects is because ARC forbids this.
One slight improvement to the last point in some designs is to use a struct holding enum members. This gives you the compile-time checking already mentioned, something that looks like an object in the return value, and the benefit of clear cases if you need to check the values in the return.
The struct:
typedef struct _SIXRecorderStateChange {
SIXRecorderState oldState;
SIXRecorderState newState;
} SIXRecorderStateChange;
The client code:
SIXRecorderStateChange stateChange = [recorderState stop];
if (stateChange.newState == SIXRecorderStopped) {
...
...