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.
Related
I have a class "ABC" and its method which returns non autoreleases object of that class.
#interface ABC:NSObject
+(ABC *)aClassMethodReturnsObjectWhichNotAutoreleased;
#end
#implementation ABC
+(ABC *)aClassMethodReturnsObjectWhichNotAutoreleased{
ABC *a = [[ABC alloc]init];
return a;
}
#end
If I have a protocol Foo.
#Protocol Foo
#required
-(void)abc;
#end
My ABC class is "not" confirming Foo protocols.
1st call
id<Foo> obj = [ABC aClassMethodReturnsObjectWhichNotAutoreleased]; //show warning
It shows warning "Non Compatible pointers.." thats good.Abc did not confirm protocol Foo
BUT
2nd call
id<Foo> obj = [NSArray arrayWithObjects:#"abc",#"def",nil]; // It will "not" show warning as it will return autorelease object.NSArray don't confirm protocol Foo
In first call compiler gives warning and in second call compiler is not giving any warning.I think that is because i am not returning autorelease object.
Why is compiler not giving warning in 2nd call as NSArray is also not confirming FOO
Thanks in advance
In your first example, the return value is a specific type so the compiler can verify the assignment.
In the second example, the NSArray arrayWithObjects: method has a return type of id. You can assign an object of type id to a variable of any type. The compiler has no way to verify that what you are doing is truly correct or not.
This issue has nothing to do with autoreleased objects. It's all about the data types. id is a kind of catch-all type that can be anything.
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
What is the normal behavior in Objective-C if you call a method on an object (pointer) that is nil (maybe because someone forgot to initialize it)? Shouldn't it generate some kind of an error (segmentation fault, null pointer exception...)?
If this is normal behavior, is there a way of changing this behavior (by configuring the compiler) so that the program raises some kind of error / exception at runtime?
To make it more clear what I am talking about, here's an example.
Having this class:
#interface Person : NSObject {
NSString *name;
}
#property (nonatomic, retain) NSString *name;
- (void)sayHi;
#end
with this implementation:
#implementation Person
#synthesize name;
- (void)dealloc {
[name release];
[super dealloc];
}
- (void)sayHi {
NSLog(#"Hello");
NSLog(#"My name is %#.", name);
}
#end
Somewhere in the program I do this:
Person *person = nil;
//person = [[Person alloc] init]; // let's say I comment this line
person.name = #"Mike"; // shouldn't I get an error here?
[person sayHi]; // and here
[person release]; // and here
A message sent to a nil object is perfectly acceptable in Objective-C, it's treated as a no-op. There is no way to flag it as an error because it's not an error, in fact it can be a very useful feature of the language.
From the docs:
Sending Messages to nil
In Objective-C, it is valid to send a
message to nil—it simply has no effect
at runtime. There are several patterns
in Cocoa that take advantage of this
fact. The value returned from a
message to nil may also be valid:
If the method returns an object, then a message sent to nil returns
0 (nil), for example:
Person *motherInLaw = [[aPerson spouse] mother];
If aPerson’s spouse is nil,
then mother is sent to nil and the
method returns nil.
If the method returns any pointer type, any integer scalar of size less
than or equal to sizeof(void*), a
float, a double, a long double,
or a long long, then a message sent
to nil returns 0.
If the method returns a struct, as defined by the Mac OS X ABI
Function Call Guide to be returned in
registers, then a message sent to
nil returns 0.0 for every field in
the data structure. Other struct
data types will not be filled with
zeros.
If the method returns anything other than the aforementioned value
types the return value of a message
sent to nil is undefined.
From Greg Parker's site:
If running LLVM Compiler 3.0 (Xcode 4.2) or later
Messages to nil with return type | return
Integers up to 64 bits | 0
Floating-point up to long double | 0.0
Pointers | nil
Structs | {0}
Any _Complex type | {0, 0}
One thing you should be clear on is that in Objective-C, you don't call a method on an object, you send a message to an object. The runtime will find the method and call it.
Since the first versions of Objective-C, a message to nil has always been a safe no-op that returns nil. There's a lot of code that depends on this behavior.
Nothing happened. When you send a message to nil it means that there is no receiver
and the message fly away. objc_msgSend method is used from Runtime library. Message dispatch mechanism is used that is why Objective-C is different and there are a lot of advantages are based on this principle. It will not throws any errors as Java or Swift do
Are there any gotchas for Toll free bridging between NS and CF types?
I'm not sure if I'm doing it wrong but I can't seem to use CF opaque types like ABAddressID inside of an NS Array.
There are not too many 'gotchas'. But this is a C based language, so not every item descends from a CFType. For instance an ABRecordID is really just a 32 bit integer. So its not a CFType. To add ABRecordIDs to an array you would do something like this:
NSMutableArray* newArray = [NSMutableArray array];
ABRecordID someID = 24875247; // you get this somewhere from some call
[newArray addObject:[NSNumber numberWithInt:someID]]; // adds an ABRecordID to the array by putting the int into an NSNumber
Then later when you want the number back:
ABRecordID thatID = [[newArray objectAtIndex:0] intValue]; // retrieve the number, then ask for its int value.
If you read the documentation on a CFType, it will always say whether it is toll free bridged with some NS* counterpart.
Quote from the docs:
"CFNumber is “toll-free bridged” with its Cocoa Foundation counterpart, NSNumber. This means that the Core Foundation type is interchangeable in function or method calls with the bridged Foundation object. Therefore, in a method where you see an NSNumber * parameter, you can pass in a CFNumberRef, and in a function where you see a CFNumberRef parameter, you can pass in an NSNumber instance. This fact also applies to concrete subclasses of NSNumber. See Integrating Carbon and Cocoa in Your Application for more information on toll-free bridging."
But an int in C is most definitely NOT a CFNumber.
Hope that helps,
--Tom
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".