Printable type string from parameter - objective-c

Using clang and objective-c I'm wondering if I can get a printable string describing the type of a potentially null parameter (i.e. compile time only type information.)
For example something like:
- (void)myFunc:(NSString *)aNilString {
NSLog(#"%s", __typeof__(aNilString));
}
Obviously this doesn't work because __typeof__ gets me the actual type, not a string. In C++ we have typeid which returns a std::type_info, but that name is mangled, e.g. "P12NSString*" and not "NSString*".
Ideally I'd like something that can be passed into functions like objc_getClass(). Is there a way to get what I want?
Edit: I'd like not to have to compile as C++, so this solution is out:
abi::__cxa_demangle(typeid(*aNilString).name(), 0, 0, 0));

To get a string that can be passed into functions like objc_getClass() just use the description of the class object.
In this example, I changed the type of your argument from NSString * to id since if you already knew that the argument was an NSString * then this question would be kind of moot. Note that I call the class method of the object being passed in and then the description method of the class in order to get an NSString * which describes the class and can be used with the methods that you reference.
Note also that if the object is nil then there is no class and calling description will simply return nil and the NSLog will print (null). You can't determine the type of pointer at runtime because it is simply a pointer to a class object (and that doesn't exist for nil).
- (void)myFunc:(id)aClassObject {
// Get the description of aClassObject's class:
NSString *classString = [[aClassObject class] description];
NSLog(#"%#", classString); // Prints __NSCFConstantString
// Bonus: Get a new object of the same class
if ([classString length] > 0) {
id theClass = objc_getClass([classString UTF8String]);
id aNewObjectOfTheSameType = [[theClass alloc] init];
NSLog(#"%#", [[aNewObjectOfTheSameType class] description]); // Prints __NSCFConstantString
}
}

Related

id pointer in Objective-C

I am new to Objective-C. I am coming from a Java background. I am trying to create a generic function in objective-c as follows:
- (id<BaseEntity>*)retreive:(NSString *)idValue unpackedWith:(id<ITransferObject>*) transferObject{
NSString * url = [[[self baseUurl] stringByAppendingString: [self getPath]]stringByAppendingString: #"/retreive/"];
url = [url stringByAppendingString:idValue];
NSMutableURLRequest *request = [self createGET:url];
NSString *responseString = [self processRequest:request];
BaseEntity* responseEntity = [transferObject unpack:responseString];
return responseEntity;
}
What I am trying to accomplish is create a function that can take a pointer to any object that implements the ITransferObject protocol. Use a function on this object as defined in the ITransferObject protocol. Then return a pointer to an object that implements the BaseEntity protocol. I feel like I took a Java generics approach to this and it is not working.
I get the following error:
Bad receiver type `__autoreleasing id<ITransferObject> *'
from this line:
BaseEntity* responseEntity = [transferObject unpack:responseString];
And I get the error:
Implicit conversion of an Objective-C pointer to `__autoreleasing id<BaseEntity> *' is disallowed with ARC
from this line:
return responseEntity;
Any ideas?
id is a pointer type in itself—you don’t need to append a * to it to represent a pointer as you do with a class.
- (id<BaseEntity>)retrieve:(NSString *)idValue unpackedWith:(id<ITransferObject>)transferObject
id is a special type - it is a by-reference type that does not require an asterisk: it is already a pointer. Here is an example that illustrates this point:
-(void)someMethod:(id)data; // id does not need an asterisk
...
NSString *str = #"Hello"; // NSString needs an asterisk
[self someMethod:str]; // This works
Adding an asterisk after an id makes the type a pointer to an id - i.e. a double pointer. There are legitimate situations when you need a pointer to an id, but it is not what you need in your situation.
You have two ways of fixing this issue:
You can change id for NSObject (similar to passing Object in Java), or
Remove the asterisk after the id.
The second approach is more common:
- (id<BaseEntity>)retreive:(NSString *)idValue unpackedWith:(id<ITransferObject>) transferObject;
First of all, it looks like BaseEntity is a class, not a protocol, so you can't use it as a protocol.
Second, the id type is inherently a pointer, so you normally don't want to declare a pointer-to-id.
Declare your method like this instead:
- (BaseEntity *)retreive:(NSString *)idValue unpackedWith:(id<ITransferObject>) transferObject{
(Note that I have changed the transferObject type by removing the *.)
Alternatively, use NSObject instead of id and leave the star, this:
- (BaseEntity*)retreive:(NSString *)idValue unpackedWith:(NSObject<ITransferObject>*) transferObject{
Also, the correct spelling is retrieve.

Printing a struct using description

I would like to know if it is possible to use the description function in the Cocoa framework to log the contents of a struct. For example:
typedef struct {float a,b,c;}list;
list testlist = {1.0,2.5,3.9};
NSLog(#"%#",testlist); //--> 1.0,2.5,3.9
No. The description message is a method found in the NSObject protocol, so by definition, must be an object. There is, however, a more convenient way of log debugging, using a LOG_EXPR() macro. This will take objects and structs:
LOG_EXPR(testlist);
Which would output:
testlist = {1.0, 2.5, 3.9};
This code can be found here.
description is a method and as such can only be called on an object. In turn, the %# format specifier only works for objects which respond to description.
You can write your own function to make a pretty NSString with the contents of your struct:
NSString * pretty_string_from_list( list l ){
return [NSString stringWithFormat:#"<list: [%f, %f, %f]>", l.a, l.b, l.c];
}
Then call that function when you log the struct:
NSLog(#"%#", pretty_string_from_list(testlist));

Problem with an Array, a Property and NSLog

So i have this block of code it adds players to an NSMutableArray in my ViewController playerList. For some reason i cannot print all the playernames to the log. Am I doing something wrong? I keep getting an error that says member refrence struc objc_object is a pointer. Can anyone see what im doing wrong?
p1,p2,p3,p4 are all NSString Objects that just have the players names.
the addPlayer method creates a new player object with a property named playerName.
- (IBAction)addPlayerButton:(id)sender {
[self.playerList addObject:[self addPlayer:p1]];
[self.playerList addObject:[self addPlayer:p2]];
[self.playerList addObject:[self addPlayer:p3]];
[self.playerList addObject:[self addPlayer:p4]];
for (id element in playerList) {
NSLog(element.playerName);
}
}
for (id element in playerList) {
NSLog(element.playerName);
}
The compiler warning/error is because element is of type id and you can't use the dot syntax with object references of type id (a specific design choice when creating that feature, btw).
Fixed code:
for (Player *element in playerList) {
NSLog(#"%#", element.playerName);
}
Two (unrelated) problems fixed:
explicitly type element to be a reference to your player class (I assumed the name). This'll allow the dot syntax to work.
Use a format string with NSLog. If a player's name were ever to contain a formatting sequence -- %#, for example -- then NSLog() would try to expand the next (non-existent) argument to NSLog and your app would crash or print garbage (say, if the player's name were "Bob %f %f %f").
doesnt look like they are getting
added to the array properly
Make sure you allocate an array and assign it to playerList somewhere:
self.playerList = [NSMutableArray array];
Use this instead:
NSLog(#"%#", element.playerName);
NSLog is sort of like printf() and friends, but not exactly. You must provide a first argument that is a string literal with the format you want to use, followed by any variables represented in the format. In Objective-C, the special format %# means "use the obeject's description method to fill in a value (if there is one)." Sometimes you get a debugger-like output for an object that does not have that method, e.g. or some such, which isn't too useful of course.
In your case, assuming playerName is an NSString, you'll see it's name output if you use the format %# in NSLog's first argument.
EDIT:
You should be able to use a for statement like this:
for(Player *p in playerList) {
NSLog(#"%#", p.playerName);
}
Just because you use addObject: to add the objects doesn't mean you have to give up using the objects' type when you look at them from the array.
If in fact the objects in playerList are just NSStrings, then your loop can simply be
for(NSString *name in playerList) {
NSLog(#"%#", name);
}

Problem declaring and calling internal metthods

How do I declare and use small helper functions inside my normal methods ?
In on of my objective-c methods I need a function to find an item within a string
-(void) Onlookjson:(id) sender{
NSString * res = [[sender gstring] copy];
persInfoBirth.text = getKeyValue(res, #"Birth");
}
I came up with a normal C type declaration for helper function getKeyvalue like this
NSString * getKeyvalue(NSString * s, NSString * key){
NSString *trm = [[s substringFromIndex:2] substringToIndex:[s length]-3];
NSArray *list = [trm componentsSeparatedByString:#";"];
//....
NSString res;
res = [list objectAtIndex:1];
//...
return res;
}
Example input string in s:
s=#"{ Birth = "1910"; Death = "1936"; }";
Anyway I get an exception "unrecognized selector sent to instance" for any of the two first lines in the helper function
How do I declare helper functions that are just to be used internally and how to call them safely ?
regards
Martin
Is this the real code? Do you get zero errors and warnings from the compiler? You must not ignore compiler warnings and you should turn on the Static Analyser in addition to the standard warnings.
There are many things wrong with the above code, most of which are nothing todo with declaring and calling methods. There is no way the above code could compile so maybe it pasted incorrectly or something..
Anyway.. declaring and using methods. Why are using a c function? Unless you have a good reason why not use Objective-c ? If you do have a good reason to use a C function the your definition should be:-
NSString *getKeyvalue( NSString *s, NSString *key ){
...
}
note the arguments. As NSString instances reside in the heap (not on the stack) you always want to pass pointers to them.
You then need to put the declaration in the header file:-
NSString *getKeyvalue( NSString *s, NSString *key )
EDIT:
In Objective-c there is no distinction between normal methods and helper methods, there is only one kind, and you have aleray written one
- (void)onLookJson:(id)sender { .. }
Taking it apart..
All methods begin with + or –, indicating Class method or Instance method. As you are familiar with C++ i guess you know what this means.
(void) is the return type. ie this method doesn't return a value. If it did it might look like (float) or (NSString *) or (id).
onLookJson: is the method name and the method takes 1 argument. Notice that the ':' is actually part of the name. This method is never is any circumstance just 'onLookJson'. An argument must always follow the :, so a method that doesn't take any arguments must not have one.
Ex
- (NSString *)fullName { .. }
This is an instance method, for example of a Person Class, you would call it like:-
NSString *theName = [aPerson fullName];
So
a method name that takes no
arguments is like 'speak'
a method
name that takes 1 argument is like
'speakTo:'
a method name that takes 2
arguments is like 'speakTo: language:'
a method name that takes 3
arguments is like 'speakTo: language: volume:'
etc.
All that is left is to put in the argument types and names.
Your function definition:
NSString *getKeyvalue( NSString *s, NSString *key ){
would become..
- (NSString *)getValue:(NSString *)s key:(NSString *)key { .. }
again, you need to declare it in the header or you will get a compiler warning.
- (NSString *)getValue:(NSString *)s key:(NSString *)key;

How do I pass arrays of values to SudzC-generated webservice classes?

I have a sudzc service class generated from a WSDL that accepts an ArrayOfInt and ArrayOfString objects as parameters. The service method signature is this:
- (SoapRequest*) Search: (id <SoapDelegate>) handler filters: (NSMutableArray*) displayedAttributes: (NSMutableArray*) displayedAttributes;
My question is, how do I pass values into the parameters that expect NSMutableArrays?
In the above method signature, the "displayedAttributes" parameter is expecting an ArrayOfInt object (which should be populated with several integers in an int tag, e.g., <int>1</int><int>2</int><int>3</int> etc).
However none of these things which I've tried have worked:
Directly passing an NSArray/NSMutableArray of (int) objects
Directly passing an NSArray/NSMutableArray of NSNumber objects
Passing an array of strings containing #"1", #"2", #"3" etc
Passing an array of strings that already contain #"<int>1</int>", #"<int>2</int>", etc
Constructing a CXMLDocument out of a string based on the integers
I'm sure this is somehow explained in the accompanying documentation in the download -- it's just not clear to me at the moment.
#Jon Limjap: Lucky you are!!! it asks you for a type which you have dealt before, I have custom class type that SudzC generated for me (!)... It initializes only when passed CXMLNode, (which need CXMLDocument / CXMLElement).. I have no idea how to deal with such type...
an instance is: filter is a class, I have a class of the filter, but there is no way to initialize it, (except alloc-init and then setting its properties, but its properties are another such custom type.. !!!!)...If you know any "trick" to tell/configure sudzc to allow us to pass objects or fetch objects of cocoa type, do tell me....
I had similar situation of passing array of objects to SOAP request. I managed to get it working by doing following changes.
SOAP array case in not added in
+(NSString *) serialize: (id) object()
{
//look if it is array not implemented
}
so I managed to change in the following method
+ (NSString*) serialize: (id) object withName: (NSString*) nodeName {
if([object respondsToSelector:#selector(serialize:)]) {
if([object isKindOfClass:[SoapArray class]])
return [object serialize:object];
return [object serialize: nodeName];
}
NSString *temp =[NSString stringWithFormat:#"<%#>%#</%#>", nodeName, [Soap serialize: object], nodeName];
NSLog(#"serialise = %#", temp);
return temp;
}
at the time SOAP request,
NSMutableArray arr = [[MYTable_STUB_ARR alloc] init]
MYTABLE_OBJ *obj = [[MYTABLE_OBJ alloc] init];
[arr addObject:obj];
[obj release];
Pass element arr object your SOAP Request ??
This is a bit old, but I hope it will help someone. I implemented the serialize: method on SoapArray this way:
- (NSMutableString *)serialize:(NSString *)name
{
NSMutableString *str = [NSMutableString string];
//[str appendFormat:#"<%#>", name];
for (id content in self)
{
//[str appendString:[Soap serialize:content]];
[str appendString:[Soap serialize:content withName:name]];
}
//[str appendFormat:#"</%#>", name];
return str;
}
As you can see, there are some commented lines. If you uncomment them and comment the currently used one inside the for, you will get a tag named name which will contain objects tagged with the content class name.