Code:
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
BOOL initial = YES;
[invocation setArgument:&initial atIndex:2];
Question:
Would it be possible to pass YES to setArgument:atIndex: without creating the temporary variable?
I was thinking that maybe there's a language construct I'm not aware of and/or constant in the runtime that is always YES that I can point to.
Thanks!
No, not in any clean, reliable way.
NSInvocation will dereference whatever pointer you send it and copy data of length specified by the method signature out of it. You need to have that information somewhere so you can get an address to it, and having the local variable as you have is the best way to do so.
The answer is no. A pointer must point to an address in memory. So first you must allocate that memory and then send the address of that allocated memory into the method. In the case of a primitive the memory allocated will be on the stack and with an object the memory allocated for the object will be on the heap and the address of that object will be stored on the stack. As for the error you are getting the void* parameter of setArgument:atIndex: seems to want an object and not a primivtive. Have you tried using a NSNumber to represent a bool. NSNumber comes with a numberWithBool: method.
A pointer must point to something(including garbage) or nothing(means the pointer being initialized to NULL). A pointer is an indirect reference to an object. If you don't have such an object for your pointer to point to, you may not need a pointer. You can simply call setArgument:NULL atIndex:2.
The case to use a pointer like that in your code is to pass an output parameter, whose value will be set in the function you call, and in this case, you probably don't need to initialize the parameter before passing it to the function, the function is supposed to take care of assigning correct value to it.
So in your case, if you didn't mean to use a output parameter, you only need to pass the primitive BOOL to the function, no pointer needed.
EDIT
I just took a look at the doc for NSInvocation. The answer is the same as others', NO.
You have to pass a pointer, which must point to an existing object for NSInvocation to work correctly.
Related
I'm trying to teach myself objective-c using the big nerd ranch book, it is a really great book but certain aspects confuse me.
The current chapter is talking about using setValue:forKey function which I understand is a method defined in NSObject. The book says that you can use this on a c primitive like int or float and gives this example
I have a custom class called Appliance and in it is an integer instance variable called voltage that stores the voltage of the current appliance
I initialize a new appliance called a
appliance *a = [[appliance alloc]init];
[a setValue:[NSNumber numberWithInt:240] forKey:#"voltage"];
he then sets up a custom setter for voltage and logs the voltage when its called to prove it works
-(void)setVoltage:int(x) {
NSLog(#"setting voltage to %d",x);
voltage =x;
}
whats confusing me is that NSNumber numberWithInt returns a pointer to an NSNumber object thats stored on the heap correct? so then how does he log the integer stored in NSNumber using the %d token. I understand that would log an integer but isn't an object being passed in? furthermore I thought that since voltage was defined as an integer and not a pointer to something it couldn't hold the address to an object in its memory? or is NSNumber kind of forcing it to hold its memory address without actually having voltage being declared as a pointer?
sorry for the confusion this chapter basically kicked my butt.
The conversion between objects and scalar types is handled automatically by the Key-Value Coding methods. From the documentation:
The default implementations of valueForKey: and setValue:forKey:
provide support for automatic object wrapping of the non-object data
types, both scalars and structs.
Once valueForKey: has determined the specific accessor method or
instance variable that is used to supply the value for the specified
key, it examines the return type or the data type. If the value to be
returned is not an object, an NSNumber or NSValue object is created
for that value and returned in its place.
Similarly, setValue:forKey: determines the data type required by the
appropriate accessor or instance variable for the specified key. If
the data type is not an object, then the value is extracted from the
passed object using the appropriate -<type>Value method.
So in your case, intValue is applied automatically to the passed NSNumber
object, and the resulting integer is passed to setVoltage:.
You are correct in that you are creating an NSNumber instance and passing that. But, you're passing it to setValue:forKey: and it's doing some work for you. It's finding the appropriate setter method for voltage (setVoltage:), checking the data type and unboxing the number into an int before calling the setter.
There are methods in Cocoa classes that accept an address of a pointer. Most commonly the argument is address of future NSError * object in CoreData validation methods (among others). This way it is possible to put custom or owned object into the place of the address that given argument points to.
My question is: why can't we do that with simple pointer arguments? E.g. let's say I have a method:
- (void)addObject:(id)someObject toArray:(NSMutableArray *)array;
I can easily pass the mutable array as second argument, call addObject: on it and after the method call the array will be modified. Why is this not done with NSError * objects? Are pointers passed to methods are defined as const by default? Is this to avoid accessing NULL?
Why is this not done with NSError * objects?
Because there's no such thing as an NSMutableError. Your example works because you can modify the contents of the array without modifying the pointer to the array itself. However, since NSError instances are immutable, you cannot modify an NSError. As such, you must necessarily create a new error object. And if you want to return that error object along with some other piece of data, at least one of those must be done via an out-parameter (such as an NSError **).
Are pointers passed to methods are defined as const by default?
Nope. Inside that method you're welcome to do this:
- (void)addObject:(id)someObject toArray:(NSMutableArray *)array {
someObject = somethingElse;
[array addObject:someObject];
}
What's important to remember here is that you're only changing a pointer in a slot in memory. The memory slot corresponding to someObject is just a space on the stack, and you're changing the contents of that space to have a value that points to a different allocated object than the one you were given.
Is this to avoid accessing NULL?
Any NULL protection you need must be done yourself.
It's because the NSError class does not define any way to modify instances after creation. The pointer itself is mutable, but an NSError is not.
They are all plain C pointers. They are not const unless you make them const. Const pointers are not a good thing to use in most situations in objective-C, or even often plain C. Const pointers are a subtle concept, and the complexities of the meaning and syntax don't mesh well with the Objective-C style of programming. Forgetting they exist is likely a good first approximation.
Example: NSArray and NSMutableArray - we would not need an NSArray class if const worked 'correctly' - but it can't due to the design of C.
** - For NSError, etc., the idea is to create an NSError, not alter the one you have passed in. In other words, you need a pointer to a pointer to be able to create an instance (i.e. change the actual object).
What is the difference between these two methods that I believe do the same thing (cast to a BOOL):
BOOL boolOne = (BOOL) [dictionary objectForKey:#"boolValue"];
BOOL boolTwo = [[dictionary objectForKey:#"boolValue"] boolValue];
When should either be used over the other?
They are quite different.
The first gets an object pointer from the dictionary, then interprets the pointer as a BOOL. This means that any non-nil pointer will be interpreted as YES, and nil as NO. In the concrete example, as dictionaries cannot contain nil pointers, you will only ever get YES from this line of code.
The second one takes the same object from the dictionary, then sends the message boolValueto that object. Presumably, and if the object recognizes the message, that will result in a BOOL version of the object.
As a concrete example, if the dictionary contains an NSNumber associated with the key #"boolValue", the NSNumber will receive the message boolValue, and if it is non-zero return YES, otherwise NO.
So to answer your question, you should use the second form. Casting a pointer to a BOOL rarely makes any sense.
No, they are not the same. The difference is that 2nd one is correct one, 1st one is not.
In your 1st line you simply cast pointer to BOOL which is roughly equivalent to checking if pointer is nil or not and has nothing to do with the value actually stored in the object.
[[dictionary objectForKey:#"boolValue"] boolValue];
is not a cast, but calls a method on NSNumber, that returns a bool. Inside a cast might be involved — but the implementation details aren't public.
Is this pointer assignment correct?
customclass.somearray = &*otherarray;
where somearray and otherarray are NSArray objects.
If not, how do I solve my problem:
I want to share this otherarray object with customclass.somearray. And I want all changes
made to customclass.somearray to be made to the original otherarray too.
Doing it this way, it works. I just want to ask, is it correct?
Your two variables are pointers of the same type, so just assign one to the other:
customclass.somearray = otherarray;
The way you have written this is unnecessary. Using the dereference operator * essentially gives you the "contents" of the pointer. The address-of operator & correspondingly gives you the address of whatever you apply it to. Your pointer otherarray contains an address. If you dereference that address and then take the address of that, you end up right back where you started.
Be aware that the left side of this assignment is a property access (assuming that customclass is also an object and not just a struct). This means that the compiler will change your expression into:
[customclass setSomearray:&*otherarray];
// And my version will be changed into:
[customclass setSomearray:otherarray];
That is, it becomes a method call rather than a simple assignment. This does not affect the syntax you should use, however.
When working in Objective-C, you never deal with objects directly, but always refer to them via pointers. Always. In C++, you can declare an actual object on the stack, for example, but you never do that in Objective-C. So, if you have:
NSArray *otherArray = [NSArray arrayWithObjects:#"foo", #"bar", nil];
then otherArray is a pointer to an instance of NSArray. Likewise, your somearray property will be of type NSArray*, so the types will match and you can just assign one to the other:
customclass.somearray = otherarray;
Hope that helps.
How would I dealloc a boolean value?
Deallocing it this way below gives me a warning: Incompatible pointer to integer conversion assigning to 'BOOL' (aka 'signed char') from 'void *'
- (void)dealloc {
self.booleanVar = nil;
[super dealloc];
}
Perhaps I should clarify, this is from a simple class inherited from NSObject.
I'm using the self.var = nil pattern that you see in Cocoa Touch classes. Let's say if it was an NSString* instead should I use self.var = nil or [var release] in the deallocmethod? I'm a little confused here.
You don't need to do it. It is not an object. This also explains the warning, as you're trying to assign a nil pointer (that's a NULL for objects basically) to a non-object.
Regarding your second question, yes. You can think of primitive variables as being part of the object, so when it's deallocated the vars will not exist anymore.
But when you have a NSString * in an object, it's just a pointer to another object. If you dealloc the former, the pointer will be deleted, not the NSString. No one might point to it, it's kind of lost in the air, occupying memory. So, before deleting the pointer, if you won't need the object anymore, you send it a release message. That's done in the dealloc method, since it's called to "delete" and object and thus is a good place to delete also every other object that has no use anymore.
You dont need to dealloc a BOOL, since BOOLs are really just a byte, which is a primitive data type. You only need to dealloc objects which have been allocated to memory.
First of all, if booleanVar is just a plain BOOL value, as in it is declared like so:
BOOL booleanVar;
then you do not need to free up any memory associated with it, since that memory is allocated and freed when the class that holds it is allocated and deallocated. So no code for booleanVar in dealloc will be fine.
However, if you are talking about a pointer for a BOOL, defined like so:
BOOL *booleanVar;
and what you want is to set this variable to a non-value, you should set it equal to NULL instead of nil, since NULL is for value pointers and nil is for object pointers (see: NULL vs nil in Objective-C).
However, if what you want is to free up the memory that the BOOL pointer points to, allocated with malloc or realloc, etc, then try the free() C function (see: http://www.cplusplus.com/reference/clibrary/cstdlib/free/).
What would really clear all this up is if you showed us the property declaration for booleanVar in the class interface, which would tell us exactly what you want to do and you would get an answer with complete certitude.