Return type from valueForKeyPath:? - iphone

This is probably pilot error on my part, but I am a little confused why this does not return an int (as thats the type of the property identified by the key path). Does valueForKeyPath: return an object instead, can anyone explain.
// Simple Object
#interface Hopper : NSObject
#property(nonatomic, assign) int mass;
#end
// Test
Hopper *hopper = [[Hopper alloc] init];
[hopper setMass:67];
NSLog(#"HOPPER: %d", [hopper valueForKeyPath:#"mass"]);
.
WARNING: Conversion specifies type 'int' but the argument has type 'id'

Yes, it returns an objc object:
- (id)valueForKeyPath:(NSString *)keyPath;
Details for automatic conversions from non-objc objects to objc objects (e.g. NSNumber and NSValue) is covered in Accessor Search Patterns for Simple Attributes.
Therefore, you would use the objc object format specifier %#:
NSLog(#"HOPPER: %#", [hopper valueForKeyPath:#"mass"]);

valueForKeyPath returns an object. int and char types are not objects. Access the property via the . operator or similar.
NSLog(#"HOPPER: %d", [hopper mass]);
NSLog(#"HOPPER: %d", hopper.mass);
Edit: Didn't fully read example code, updated answer

Related

NSLog pointer syntax

I'm a little bit confused about the syntax of NSLog. For example,
NSString *nameString = #"Name";
NSLog(#"nameString is: %#", nameString);
If my understanding is correct (which it very well may not be), then nameString is defined to be a pointer to a String. I thought then that this would print the memory address that nameString holds, not the value of that address. So, if that is true, then in the NSLog statement, to get the value of the pointer, shouldn't we need to use the asterisk notation to access what nameString points to like this:
NSLog(#"nameString is: %#", *nameString);
?
It has been a little while since programming in C, but since Objective-C is a superset of C I thought they would behave similarly.
An explanation would be greatly appreciated! Thanks!
The command %# is like "shortcut" that calls the method -description on the receiver. For an NSString it simply display the string itself, since is inherited from NSObject you can override it, very usefull if you create for own class. In that case the default behaviur is print the value of the pointer. If you want to print the address of the pointer in the string just replace with :
NSLog(#"nameString is: %p", nameString)
I think that you use an asterisk only to declare a pointer. Then, you only use the name you decided. For example:
NSString *foo = [[NSString alloc] initWithString:#"Hello"];
NSLog(#"%#", foo);
Correct me if I am wrong :)
It's an object and NSLog is a function that uses its format specifiers to determine what to do with the argument. In this case the specifier is %# which tells NSLog to call a method on an object.
Normally this will call the method "description" which returns an NSString but it probably does respondsToMethod first and falls through to some other string methods.

NSCoding - reuse object for writing?

I have a Singleton which has an NSMutableArray containing a Class, the Class contains the proper NSCoding routines to encode/decode the data - it all works fine.
However I'd now like to also save data that is not part of the Class (array), but instead is part of the Singleton and is not specific to each item in the Class/array. So I've added the appropriate code in the Singleton including:
BOOL alarmIsOn;
...
#property(nonatomic,assign) BOOL alarmIsOn;
...
#synthesize alarmIsOn;
...
[encoder encodeBool:alarmIsOn forKey:#"alarmison"];
...
alarmIsOn=[decoder decodeBoolForKey:#"alarmison"];
When I save my data I previously used this which works perfectly:
GlobalData *globDat=[GlobalData getSingleton];
NSData *encodedObject = [NSKeyedArchiver archivedDataWithRootObject:globDat.allMsgs];
[encodedObject writeToFile:plistPath atomically:YES];
Now I'd like to add the following to include the additional data from the Singleton:
encodedObject = [NSKeyedArchiver archivedDataWithRootObject:globDat.alarmIsOn];
[encodedObject writeToFile:plistPath atomically:YES];
However it gives me this error in Xcode:
Automatic Reference Counting Issue: Implicit conversion of 'BOOL' (aka 'signed char') to 'id' is disallowed with ARC
And this warning:
Semantic Issue: Incompatible integer to pointer conversion sending 'BOOL' (aka 'signed char') to parameter of type 'id'
What am I doing wrong, and how can I fix this?
Your error is that
globDat.alarmIsOn
is a bool and
NSKeyedArchiver archivedDataWithRootObject:
wants an
id
which is another word for an opaque pointer to an object. A bool is just a byte. How exactly you want to fix it is up to you. To use that routine requires an object.

How can I see values of Object in NSLog?

Suppose I have an object containing some data.
How can I see that data using NSLog?
If anyone is not clear about my question, then can ask me again.
If you want to see an NSArray and NSDictionary and etc objects then you can directly print like NSLog(#"%#",object);
If it is an user defined object then you need to display by calling with property (attribute).
User defined object with name object and properties like
NSString *property1;
int property2;
NSMutableArray *property3;
Print them in the console as follows:
NSLog(#"%#, %d, %#" object.property1,object.property2,object.property3);
If you implement the -(NSString*)description method in your class then you can use NSLog to output a summary of the data. Of course, you can also directly output any property.
For example:
NSLog (#"%# %d", object, object.integer);
The first part calls the description method and outputs that; the second part gets the value of the integer property of object and outputs that.
Every Objective-c Object (this comes from NSObject) has a property called description. So if you want to print information about your class this is the way to go.
#implementation MyClass
- (NSString*)description
{
return [NSString stringWithFormat:#"MyClass:%#", #"This is my class"];
}
so if you do a call like this.
MyClass *myClass = [[MyClass alloc] init];
NSLog(#"%#", myClass);
NSLog(#"%#", [myClass description]); //Same as the line above
Then it will write "MyClass:This is my class" to the console (in this case it will print it twice).
Implement description of the given class.
-(NSString*)description {
return [NSString
stringWithFormat:#"<%#> name: `%#` size: `%#`",
NSStringFromClass(self), self.name,
NSStringFromCGSize(self.size)];
}
NSLog(#"%#", object); // <Object> name: `Harry` size: `{2, 2}`
extension Object: CustomStringConvertible {
var description: String {
"<\(Self.self)> name: `\(name)` size: `\(size)`"
}
}
print(object) // <Object> name: `Harry` size: `(2.0, 2.0)`
I would suggest these:
Objects:
For objects like Dictionary, Array, Strings do it like:
NSLog(#"%#", object);
For basic data-types like integers
NSLog(#"%i",intVal);
For type encoding you should see http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
Use this class https://github.com/arundevma/ICHObjectPrinter
NSLog(#"Object description is %#",[ICHObjectPrinter descriptionForObject:person]);
NSLog(#"My object data:%#",[myObj someData]);
NSLog(#"My object Other data:%#",[myObj someOtherData]);
Or directly:
NSLog(#"%#",myObj);
NSLog(#"Description:%#",[myObj description]);
Additionally to Satya's answer, if you want to see basic c data types, use the format specifiers. Such as %d for an integer:
NSLog (#"My integer:%d", myObject.myInteger);
The complete list is here:
http://www.cplusplus.com/reference/clibrary/cstdio/printf/

Use of asterisk in variable names

The book "iPhone Programming. The Big Nerd Ranch Guide" cites the following method (page 96)
(void)mapView:(MKMapView *)mv didAddAnnotationViews:(NSArray *) views {
MKAnnotationView *annotationView = [views objectAtIndex:0];
id <MKAnnotation> mp = [annotationView annotation];
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance([mp coordinate], 250, 250);
[mv setRegion:region animated:YES];
}
I'm confused because of the asterisk usage. The line that begins with "MKAnnotationView" and the following one can be represented in an abstract fashion by:
ObjectType variableName = [object message];
Questions:
In the first case an asterisk precedes the variable name, but not in the second. Why?
In the case where the asterisk is used, should not be the pointer the assigned to nil?
Thanks.
I tend to think of it as what variable types require an asterisk, not what variable names require an asterisk. Objective C doesn't allow you to allocate objects on the stack like so:
// Declare an NSObject. Won't work.
NSObject myObject;
Instead, all objects must be dynamically allocated on the heap using pointers like so:
// Declare a pointer to an NSObject. Will work.
NSObject* myObject = [[NSObject alloc] init];
id is a special Objective C keyword that just means "A pointer to some Objective C object". This may or may not inherit from NSObject and is dynamically typed. What's important to note is that, while there is no asterisk, this is still a pointer to an object:
// Same as before. Will work.
id myObject = [[NSObject alloc] init];
The only difference is that the compiler has no information about what myObject is.
As a finishing note, id <MKAnnotation> is exactly the same as a regular id, but with some extra information for the compiler. Read it as "a pointer to some Objective C object that behaves like an MKAnnotation". MKAnnotation, in this case, is the name of a Protocol whose required methods you are declaring that particular id to implement.
id is already defined as a pointer to a struct. If you look at its definition in objc.h, you would that id is defined as,
typedef struct objc_object {
Class isa;
} *id;
Since it is already a pointer to an objc_object, you can create pointers to objects without using the asterisk as,
id myObject;
Also saying that an object is type id gives the compiler absolutely no information about the object except its class which comes from the isa property.
An NSObject on the other hand is defined as,
#interface NSObject <NSObject> {
Class isa;
}
To create a pointer to an object of NSObject or one of its subclass (such as MKAnnotationView), you would declare it as,
NSObject *myObject;
MKAnnotationView *myObject;
We are putting the asterisk here to denote that it is a pointer.
Specifying the protocol(s) next to the type gives the compiler more information for static-type checking.
You should check out this article for a brief introduction to the differences between id and NSObject. For an in-depth understanding, checkout this article on the Objective-C runtime.
ObjectType is normally something like "pointer to a MKAnnotationView", which is represented in Objective-C as it is in C: "MKAnnotationView *". Exceptions include the "id" type, various integer and floating point types (including their typedefs), enums (which are really integer types), and some small structs like CGRect.

Obj-C: Difference between "Fairfield" and #"Fairfield" (with at string)?

I just had a ridonkulous typo in my iPhone app, answered here.
Now I'm wondering about the #"..." notation.
why this works:
NSArray *someArray = [NSArray arrayWithObjects: #"Fairfield", nil];
and this does not (even though it compiles, it will throw an EXC_BAD_ACCESS):
NSArray *someArray = [NSArray arrayWithObjects: "#Fairfield", nil];
Edit:
Ok, so you guys have pointed out that I can't add a C string to an NSArray, because it's obviously not an object.
Now another question: Isn't this somewhat of an oversight? I mean, why does the "...WithObjects:" message specify a list of (id) instead of (NSObject *)?
"#Fairfield" is a normal C string with an '#' character in it. #"Fairfield" is an Objective-C string (NSString on OS X) with no literal '#' in it.
You cannot add C strings to Cocoa collections.
It accepts id rather than NSObject because all initialisers return id. All initialisers return id because subclasses would otherwise override the return type of their ancestors' initialisers.
For example, -[NSMutableString init] can't return NSMutableString * because it subclasses -[NSString init], which can't return NSString * because it overrides -[NSObject init].
Unfortunately, implicit type-casting between const char * and id is perfectly legit, so the compiler won't throw a warning, however a static analyser may be able to pick this sort of mishap up fairly easily.
"Fairfield" is a C string, #"Fairfield" is an Objective-C string.
#"Fairfield" is an object (NSString), so you can send it methods ([#"Fairfield" uppercaseString]) and add it to Objective-C arrays ([NSArray arrayWithObjects:#"Fairfield",nil]). You can only add objects to NSArrays.
On the other hand, "Fairfield" is a C string, and is generally not used in Cocoa. For the most part, you can get by with only using #"Fairfield"
The other reason that a number of things in Cocoa deal with id rather than NSObject* is because, unlike some other languages (say, Java and C#), where all objects in the language must inherit from some global base class, it's entirely possible to have objects that do not descend from NSObject (NSProxy being one example). It's not something you'd do often, but it is possible. The id type means "pointer to any Objective C instance".