Determine if (void *) pointer from NSInvocation getArgument is object or primitive - ios

Struggling with this one. Hoping it's possible and I don't sound silly.
I'm hacking forwardInvocation in a class I'm writing. What I want to do is forward the invocation to one selector or another depending on if it is an object or primitive type. The end goal is I want to "box" the primitives so they can be added to arrays/dictionaries. For simplicity, the two types of values that typically come through here are NSStrings and enums.
In short, given a pointer, is there a way to tell if it is an object?
__unsafe_unretained id argument;
[anInvocation getArgument:&argument atIndex:2];
// EXC_BAD_ACCESS if primitive (i.e. NSInteger value of 2 ($1 = 0x00000002) )
if (![argument isKindOfClass:[NSObject class]]) {
// Box the value
...
}
Is there a test I can run? Right now my code is hackishly doing this nasty trick:
// All my enums have at most 10 elements. I'm so bad at code.
if ((NSInteger)argument < 10) {
// Box the value
...
}
Thanks in advance.

You can get the type from the method signature:
NSMethodSignature *signature = [invocation methodSignature];
const char* argType = [signature getArgumentTypeAtIndex:2];
The types are listed under Type Encodings in the Objective-C Runtime Guide
You should also make sure you know the type before calling getArgument:atIndex::
This method copies the argument stored at index into the storage pointed to by buffer. The size of buffer must be large enough to accommodate the argument value.
__unsafe_unretained id argument;
[anInvocation getArgument:&argument atIndex:2];
This will write past argument on the stack if the size of the actual argument is greater than sizeof(id)

Pointers in C are just values that represent addresses. The thing at the address pointed to by a void pointer is explicitly untyped. If the goal is to have a method that can take any type — object pointer, scalar or composite — that just isn't going to work. And besides the impossibility of recovering a type from a void pointer, if you're literally trying to pass in a scalar directly instead of passing in its address, that is doubly impossible because the compiler needs to know right type of the value in order to emit the correct code, and most types cannot be converted to a pointer with any fidelity. Either way, Objective-C's type system is just not powerful enough to do what you want.

Related

Is it possible to silence the warning on "performSelector" and receive the return value or object?

I am familiar with the solutions to this ARC warning (performSelector may cause a leak because its selector is unknown) and have implemented them in most cases, but I can't seem to find a way to properly get the return value for a selector without just suppressing a warning.
It seems that maybe it can't or shouldn't be done, but a rewrite of code logic (developed by others) is too time consuming.
Code example:
NSString *message = [callback performSelector:validatorSel withObject:textCell.textField.text];
If validatorSel is known to not to begin with allocor new, or to have copy (or Copy) in its name, and you know there are no memory-management overrides involved (which are rare), then the default memory management will be correct here, and you can suppress the warning with an appropriate #pragma. If you cannot prove those things, then this may crash, which is why there's a warning.
If you cannot prove the above requirements, then there is no way to make this safe under ARC. You will either have to build it without ARC or rewrite it.
From your code sample it looks like you are expecting a selector for a method which takes an NSText * and return an NSString *. So from your linked answer you can determine that the implementation of this method has the function type:
NSString *(*)(ID, SEL, NSText *)
Here ID may be replaced by the type of callback, and NSText * can be replaced by the actual type of textCell.textField.text if our guess is wring.
Again from your linked answer, you can obtain the implementation and call it using:
NSString *(*implementation)(ID, SEL, NSText *)
= (void *)[callback methodForSelector: performSelector:validatorSel];
NSString *message = implementation(callback, validatorSel, textCell.textField.text);
As #RobNapier correctly points out this is only safe under ARC if the selector does not return a retained value, i.e. for normal[*] selectors if it is a member of the init, copy, or new method families. Now you are very unlikely to be passed an init family method for validatorSel as that would require callback to be a reference to an alloc'ed but not init'ed object, so we can ignore that one for now[#]. To test for the other two families you can use code along the lines of:
NSString *message; // for the return value of the selector
NSString *selName = NSStringFromSelector(validatorSel); // get string name of selector
if ([selName hasPrefix:#"new"] // starts with new,
|| [selName hasPrefix:#"copy"] // or copy,
|| [selName rangeOfString:#"Copy"].location != NSNotFound) // or contains "Copy"
{
// need to handle returning a retained object
...
}
else
{
// normal case
NSString *(*implementation)(ID, SEL, NSText *)
= (void *)[callback methodForSelector: performSelector:validatorSel];
message = implementation(callback, validatorSel, textCell.textField.text);
}
Which just leaves how to handle the return value correctly under ARC for copy and new family methods...
Handling copy and new family methods
ARC knows that a method, or function, returns a retained object by an attribute being placed on the method/function type. The naming convention is just the language's way of inferring the attribute if it is not present, it can be manually specified using the NS_RETURNS_RETAINED macro on a method/function declaration. So the missing code above is just:
{
// need to handle returning a retained object
NSString *(*implementation)(ID, SEL, NSText *) NS_RETURNS_RETAINED
= (void *)[callback methodForSelector: performSelector:validatorSel];
message = implementation(callback, validatorSel, textCell.textField.text);
}
The modified type for implementation tells ARC that it will return a retained object and ARC will handle the call the same way it does for known copy or new family methods.
HTH
Note: Handling init family methods
We skipped the init family not just because it is highly unlikely but also because it behaves differently - init family methods consume the object reference they are called on, that is they expect to be passed an owned object which they take ownership of, and will release it if needed. Unsurprisingly consuming an argument is also indicated by an attribute, just as for returning a retained object. The curious reader might wish to determine the code required, even though needing it is highly unlikely.
[*] A "normal" selector is one for a method which follows the standard naming conventions of Objective-C and does not use attributes to alter the memory ownership behaviour in ways contrary to the standard conventions. Only supporting standard conventions is not a big restriction, the whole point of the conventions is that code relies on them!
[#] You are of course very unlikely to be passed a new family selector as well, callback would usually have to be a reference to a class object, but handling it is the same as for the copy family so we've included it.

use parameter type-of id in block iOS

Here is the code
[EvoScrollBarTagView initWithScrollView:self.listTableView
withTagView:[TagView new]
didScroll:
^(id scrollBarTagView, TagView *tagView, CGFloat offset) {
[scrollBarTagView showTagViewAnimation];
........
And my confusion is why the scrollBarTagView(type-of id) can call the method or properties in my EvoScrollBarTagView.h . the parameter scrollBarTagViews type is id, not declared as the EvoScrollBarTagViews instance object, can someone tell me why , Thank you very much...
As mentioned in Objective-C is a dynamic language:
The id type defines a generic object pointer. It’s possible to use id
when declaring a variable, but you lose compile-time information about
the object.
So it doesn't mean that scrollBarTagView can call any method, all it means that it will compile successfully. If the referenced method is not implemented, the app will crash during runtime.
That's part of the point of id -- dynamic typing. You can try to send any message (that the compiler knows about at this point in the code) to an expression of type id without any compiler error or warning.

Cannot pass object with interface type 'NSString' by value to variadic function; expected type from format string was 'id'

I wrote the following code in X-Code,
NSString *myString = #"testString";
NSLog(#"*myString = %#", *myString);
and I got the following error,
Cannot pass object with interface type 'NSString' by value to variadic function; expected type from format string was 'id'
can someone tell me what does it mean? What does "*myString" stand for?
As we know, if we write the code like below, there is no error,
char testchar = 'f';
char *char1 = &testchar;
NSLog(#"*char1 = %c",*char1);
Why NSString cannot write like this case?
#Avi,Thanks for your explanation, originally, what I want to ask is I saw the below declaration and definition:
NSString const *BNRNextItemValuePrefsKey1 = #"NextItemValue";
From my understanding BNRNextItemValuePrefsKey1 a pointer to a constant #"NextItemValue"
but when I do the following, there is no error,
BNRNextItemValuePrefsKey1 = #"jijijij";
why this happen,can you explain it?
The syntax *myString is dereferencing the pointer myString. It tells the compiler to pass the value contained at the memory location pointed to by the pointer, instead of passing the value of the pointer itself. The reason that example gives you an error is because Objective-C does not allow passing of objects by value. You are only allowed to pass pointers to objects. The reference to id in the error message is pointing out that the %# format string is used to print objects. Exactly what gets printed for any object depends on the implementation of - description for that object's type.
Your second example with the pointer to a char works because Objective-C has no issues with passing the value of a char.
question edit
In that statement the const attribute refers to the value being pointed to, not the value of the pointer. In terms of implementation, it doesn't really mean much, as that string will not be changeable anyway. I believe it allows for compiler optimizations.

NSLog giving error when trying to print a single unformatted integer

Hi this is third day of mine using Objective-C today I was writing few simple programs and i ecncountered the following warning
main.m:19:5: warning: passing argument 1 of 'NSLog' makes pointer from integer without a cast [enabled by default]
NSLog(res);
which finally resulted in the Segmentation fault.. Here is my program..
#import <Foundation/Foundation.h>
#interface SomeClass: NSObject
{
int x;
}
#property int x;
#end
#implementation SomeClass
#synthesize x;
#end
int main(){
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc]init];
SomeClass * obj = [[SomeClass alloc]init];
obj.x =20;
int res = obj.x;
NSLog(res); //error
/* But the error was not seen when I replaced the above statement with
NSLog(#"The value is : %d",res);
*/
[pool drain];
return 0;
}
The error message that I got was :
Compiling the source code....
$gcc `gnustep-config --objc-flags` -L/usr/GNUstep/System/Library/Libraries -lgnustep-base main.m -o demo -lm -pthread -lgmpxx -lreadline 2>&1
main.m: In function 'main':
main.m:19:5: warning: passing argument 1 of 'NSLog' makes pointer from integer without a cast [enabled by default]
NSLog(res);
^
In file included from /usr/GNUstep/System/Library/Headers/Foundation/NSObject.h:30:0,
from /usr/GNUstep/System/Library/Headers/Foundation/FoundationErrors.h:29,
from /usr/GNUstep/System/Library/Headers/Foundation/Foundation.h:33,
from main.m:1:
/usr/GNUstep/System/Library/Headers/Foundation/NSObjCRuntime.h:146:16: note: expected 'struct NSString *' but argument is of type 'int'
GS_EXPORT void NSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);
^
Executing the program....
$demo
Segmentation fault (core dumped)
Please help by making me understand why NSLog behaves like this ? Where did I go wrong?
Thank you...
You can't use the NSLog like this, You need to specify the control string and use like:
NSLog(#"%d",res);
The format should be specified for NSLog(), please check the NSLog and NSLogv reference for more information
NSLog
Logs an error message to the Apple System Log facility.
void NSLog ( NSString *format, ... );
Discussion
Simply calls NSLogv, passing it a variable number of arguments.
Availability
NSLogv
Logs an error message to the Apple System Log facility.
void NSLogv ( NSString *format, va_list args );
Discussion
Logs an error message to the Apple System Log facility (see man 3
asl). If the STDERR_FILENO file descriptor has been redirected away
from the default or is going to a tty, it will also be written there.
If you want to direct output elsewhere, you need to use a custom
logging facility.
The message consists of a timestamp and the process ID prefixed to the
string you pass in. You compose this string with a format string,
format, and one or more arguments to be inserted into the string. The
format specification allowed by these functions is that which is
understood by NSString’s formatting capabilities (which is not
necessarily the set of format escapes and flags understood by printf).
The supported format specifiers are described in “String Format
Specifiers”. A final hard return is added to the error message if one
is not present in the format.
In general, you should use the NSLog function instead of calling this
function directly. If you do use this function directly, you must have
prepared the variable argument list in the args argument by calling
the standard C macro va_start. Upon completion, you must similarly
call the standard C macro va_end for this list.
Output from NSLogv is serialized, in that only one thread in a process
can be doing the writing/logging described above at a time. All
attempts at writing/logging a message complete before the next thread
can begin its attempts.
The effects of NSLogv are not serialized with subsystems other than
those discussed above (such as the standard I/O package) and do not
produce side effects on those subsystems (such as causing buffered
output to be flushed, which may be undesirable). Availability
You cant declare the NSLog like that see the tutorial for NSLog its may be useful for you.
Objective-C has a number of built-in data types:
int – integer constant
float – real numbers with fractional component
double – double precision floating point number
char – a single character
short – short integer (2 bytes)
long – double short
long long – double long
BOOL – boolean
The utility function NSLog() can be used to print to the debug console in Xcode. NSLog() uses the NSString formatting services. The easiest way to create a NSString is to use the #”" notation. Inside a format string a % is a placeholder for a value. The character after the % determines the value expected, be it an int or a float and so on. If we declare an integer “int i = 5″ and want to print the value of i with NSLog() we can do it with NSLog(#”The value of i is %d”, i);
You can use %d to print the value of an int, %f for a float and double, %c for a char. For a full listing of all format specifiers supported by NSString formatting methods read through the documentation.
For More Reference Click Here
keep learning..:)
The 1st argument to NSLog is an NSString that should be a string with 0 or more format specifiers.
Use:
NSLog(#"res = %d", res);
or just:
NSLog(#"%d", res);
Have a look at the reference docs for NSLog for details.
You should specify the compiler that what kind of data type you want to print in Log,
NSLog(#"%d",res);
You should not never use NSLog(variable) directly. Instead, use NSLog(#"%d", variable) for integers or NSLog(#"%#", object) for objects.

Pointer passing Objective C or C syntax

I have a method , which takes the following argument.
void callAMethood(const void *iVar){}
now here i am not clear on how should i pointer
the following seems to be quite simple,where i just pass the address instead of value.
NSString *a=#"Hi";
callAMethood(&a);
But, the following seems to work well, but understanding the syntax is difficult.
char const * const a="HI";
callAMethood(&a);
or even this works callAMethood(a);
As xlc points out, both are wrong.
NSString *a=#"Hi";
callAMethood(&a); // This passes a NSString **
char const * const a="HI";
callAMethood(&a); // This passes a char const * const *
Both work, because a pointer to a pointer can stil be cast to a void *.
It is incredibly unlikely that you intended either, though.
Take advantage of C being type-checked at compile time and be more specific with your types.
void awesome_function(NSString *amazingString);
NSString *yay = #"oh my god yes";
awesome_function(yay); // ok
awesome_function(&yay); // compiler error
In short: unless you have a very good reason for using a void * (which I would consider unlikely for a Cocoa(Touch) app), don't.
If you don't want compile-time type checking, use something like Python. If you want to be able to handle different Objective C object types, that's what id is for.
You ask what you should pass to the function (it is not the declaration of a method):
void callAMethood(const void *iVar) {...}
The answer is nobody can tell you!
Your function takes a value of type void * - this type is typically used in C to mean a "pointer to anything". It is not a type that would be used for a function that took a pointer to any object, that would be id.
But that doesn't get us very far, as we've no idea what callAMethood is expecting we've no idea what to pass it - we cannot tell you the answer.
Now you give the example:
NSString *a = #"Hi";
callAMethood(&a);
That is a valid call, it passes a pointer to the variable a. However:
callAMethood(a);
is also a valid call, it passes the pointer which is stored in the variable a. Without any knowledge of what callAMethood is expecting we cannot tell you which is the correct call. The same applies to your second example.
HTH
Every pointer can be converted to void * implicitly.
EDIT:
As Kevin said, only data pointer.
As CRD said,
The answer is nobody can tell you!
without the information of the function of callAMethood. All of those invoking are valid grammatically(can be compiled), howerver, invalid semantically usually unless callAMethood really doesn't care about which type iVar(e.g. it just prints the pointer).
What people said above is correct but you can also use UTF8String to get a const char * from a NSString
const char * myStr = [a UTF8string];
callMehtod(myStr);
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/Reference/NSString.html

Resources