Actually I am from java background and I am learning objective c.I am very confused about strange behaviour of objective C."Please Read 3rd Question its important one."
Questions are provided in sequence so please give answers in sequence as its understandable to me and others.
Question 1
I have two classes derived from NSObject: A and B:
#interface A : NSObject
#end
#interface B : NSobject
-(void)display; // It displays "I am class B"
#end
Now if I do this:
A *a = [[B alloc]init]; // Show warning not error (it must be illegal)
[a display]; // prints "I am class B"
It calls the display method of class B. I don't think that it should happen because:
A doesn't have the method display. By polymorphism.
This could be a security threat as I am creating reference of any class and passing object of any another class and accessing data by it.
There could be design issues as Dog class instance gets an object of Printer class and now i am calling print method on Dog instance.
I have reference of NSArray and passed object of NSMutableArray and now i am calling NSMutableArray method on this instance.
[nsarr addObject:#:abc]; //Looking very odd
Question 2
If I have Foo protocol and if any class is not confirming it. It should not be allowed to get object of that class in protocol reference.
#protocol Foo
#required
-(void)abc;
#end
If i call:
id<Foo> obj= [[B alloc]init]; // Shows warning ignore it for now as it must be illegal also
[obj display]; // It will call display method which should be illegal
It should not happen, as B is not conforming to protocol Foo and obj is taking B object and calling B instance method. I think its very bad because of polymorphism and security
Question 3
If my class has a class method which returns an object of that class which is not autoreleased, the compiler shows warning. If I pass the object returned by that class (not conforming protocol) method to reference of protocol. (IT SHOULD BE AN ERROR).
id<Foo> obj = [Abc aClassMethodReturnsObjectWhichNotAutoreleased]; //show warning
It shows a warning which is good. Abc did not conform to the protocol Foo
BUT
id<Foo> obj = [NSArray arrayWithObjects:#"abc",#"def",nil]; // It does **not** show a warning as it will return autorelease object. NSArray doesn't conform protocol Foo
Why does the above assignment to the NSArray class not show a warning as it is showing in the previous example.
Thanks in advance.
EDIT
*Answer 3rd Question:*As NSArray returns id object which will allow to pass in "id obj" but in "aClassMethodReturnsObjectWhichNotAutoreleased" case the method returns "ABC *" pointer so that is why compiler giving warning in this case.
Question 1:
A *a = [[B alloc]init]; //Show warning not error (it must be illegal)
[a display]; //prints "I am class B"
Here you are using a static type A for the variable named a. You are then assigning a different type of object (B) to the variable.
Unlike java, Objective-C does not enforce the static typing requirement, however it does warn you when it is being compiled since the compiler detected a difference between the declared type and the actual type of the object. It happily stuffs the B object into your variable though, so a is now pointing to the B object that you created. Once the program is compiled and running (at run-time), A *a is treated the same as id a.
Another feature of Objective-C is that you can send any message to any object at any time. This is part of the dynamic nature of Objective-C. Obviously there are cases where sending the wrong message to an object can cause bad things (tm) to happen so you need to ensure that you only send appropriate messages. There are various functions that can test the class of an object at run-time, or even test to see if it is able to handle a particular message before you send it in order to prevent the bad things. If you are using static typing (like in this example) then the compiler will issue warnings to tell you that you may have made a mistake and should review the code.
Question 2:
This is actually very similar to question 1. The compiler is warning you that you are assigning what appears to be an incorrect value to the variable, however at run-time you can send any message to any object, so it will work on the actual object instead of the "expected" object from the type declaration.
Question 3:
Good question. I would have thought that you would get a warning there too. Maybe someone else can help out on that. My first thought is that this is a bug and should be reported as such, but there may be a reason for it that I'm not aware of....
Objective-C and Java have very different type rules, as you have discovered.
Java is strictly statically typed, which means that types must match, and you can never make an assignment that is not allowed by the type conversion rules.
Objective-C is dynamically typed with optional static types. You can break out of the type system at any time. For some cases, the compiler will emit warnings, but it is still allowed.
This is the reason why you are seeing the behavior. Objective-C is not broken, it just have different rules than the ones you know from Java.
Apple has a lot of documentation of the specific rules, perhaps you would want to read Enabling Static Behavior.
Here are some more resources about dynamic vs static typing for you:
Dynamic type languages versus static type languages and What do people find so appealing about dynamic languages?
A *a = [[B alloc]init]; //Show warning not error (it must be illegal)
[a display]; //prints "I am class B"
Because you initialized variable from B class that have display property.
It's correct
Related
Is it possible to check and get the name of the class an object was initialized in?
Example: I have Class A. I created object ABObject using [[ABOBject alloc] init]; via an instance method.
How can I find out the class from which an instance ABObject was created, namely "A" here?
Objects can be created outside the context of a class, so it wouldn't make sense for this to be a built-in language feature.
If you do have to do this, one way to work around it would be to use the objc_setAssociatedObjects() function in objc/runtime.h immediately after any such object was instantiated. Something like:
ABObject *object = [[ABObject alloc] init];
objc_setAssociatedObject(object, #"InstantiatingClassKey", [self class], OBJC_ASSOCIATION_ASSIGN);
Then you could get it with objc_getAssociatedObject(object, #"InstantiatingClassKey").
I think you'd be better off re-assessing your design because this is not going to be particularly maintainable. Even extracting this into a category on NSObject to remove duplicated code you'll still have an extra step to remember and weird relationships between your objects.
Also, as Martin R. points out in the comments, I'm taking a shortcut and passing a string literal as the key argument for the function, in reality you'd want to follow the practice of using the address of some static or global variable.
This is a slight follow on from a previous question:
for(id <stuffieProtocol> eachObject in stuffieArray) {
if([eachObject respondsToSelector:#selector(secretID)]) [eachObject secretID];
}
The above is a line from a fast enumeration loop that takes a number of objects out of an NSArray and checks to see if they respond to the method secretID. The method secretID is not part of the #protocol but rather a method defined on one of the objects in the collection.
The if statement above fails because the compiler says "No known instance method for selector secretID" Which is fine, id does not care and the method is not defined in <stuffieProtocol> I can fix this by casting each object to the type that does define secretID:
if([eachObject respondsToSelector:#selector(secretID)]) [(Ted *)eachObject secretID];
My question is in the original code, how does the compiler know that eachObject responds to the method secretID but yet when you ask it to call that method its says "No known method" I am just a little curious about the mechanics?
The compiler doesn't know anything about whether the object responds to that selector but the runtime does. (i.e. The compiler doesn't execute the code.) The compiler only cares that any NSObject can respond to respondsToSelector:.
I come from a C# background and I am learning Objective-C. I was wondering if I could get some clarification on syntax. Thanks in advance for your help.
What is the difference between the lines of code shown below? Given that I know for sure that the type in the "tempItem" dictionary is an (NSString *) type and newsItem.pictureUrl is also an (NSString *):
Scenario 1:
newsItem.pictureUrl = [tempItem objectForKey:#"picture"];
Scenario 2:
newsItem.pictureUrl = (NSString *)[tempItem objectForKey:#"picture"];
I know what you mean. I also started out preferring to cast, since it clearly shows what you are doing. But after a while, it gets terribly tedious and you learn to omit it.
The fact that there are no generics, and that the containers only store objects, is a bit of a bummer, especially if you come from a language with generics. It means you are constantly and explicitly converting between objects and simple types (e.g. between NSNumber and int) and that there is no way, except to query [object class], to ensure you only get an NSString or an exception you can handle.
But the cast will not make any difference. If the object returned is not an NSString, and you cast it to one, it will make no difference. The cast does no implicit type checking, nor a conversion. It merely reinterprets the return value.
Casting between object types can basically only affect two things:
What warnings the compiler emits (e.g. "the class for this variable doesn't appear to have the method you're trying to call")
What properties the object has and how they work (e.g. the equivalent getter for self.awesome might be [self awesome] or [self isAwesome])
It emphatically does not affect what kind of object you get. The static types at compile time are just hints for the compiler. If you cast an object to a type that it isn't, you're just lying to the compiler.
In that particular case, it doesn't have any effect at all. Some people do write code like that, but AFAIK that's just because they find it comforting to just act like they're using a statically typed language (even though Objective-C isn't).
There's no difference between the two lines of code; it's purely stylistic.
The method objectForKey: here returns an object of type id, which is a generic object pointer. In Objective-C, an id can be implicitly converted to any Objective-C object type without a cast. The following two lines are equivalent:
id someId = ...;
NSString *someString = someId; // #1
NSString *someString = (NSString *)someId; // #2
This is similar to how in C, a pointer of type void* can be implicitly converted to a pointer to any other type without a cast (that is also true of Objective-C, but void* pointers are discouraged in Objective-C; that is not true in C++).
As far as type safety goes, both are equivalently unsafe. If the runtime type of the object is in fact the type you're casting it to (whether the cast is explicit or implicit) or a subclass thereof, then everything will work as intended. If the runtime type is not what you're expecting, then most likely an NSException will be thrown with the common object does not response to selector error, due to calling a function that doesn't exist for that type. It's also possible you might crash with a segmentation fault due to accessing an ivar that doesn't exist or has an unexpected value (since the object really isn't that type).
If you're unsure of that object's runtime type, you should check its runtime type with the -class or -isKindOfClass: methods, and then only take action if it's a particular type. Prefer using-isKindOfClass:`, since that still works with subclasses, as opposed to comparing the class for exact equality with a particular class. For example:
id someId = ...;
if ([someId isKindOfClass:[NSString class])
{
// It's an NSString
NSString *someString = someId;
// Do stuff with someString...
}
The type of an Objective-C instance is really only useful for determining the appropriate amount of memory to allocate for creation of the instance, and for static analysis (code completion, compilation etc). At run time the instances are all represented by id's and the actual type of the object means much less. This dynamic behavior is by design, and allows a great amount of flexibility when designing ObjC applications.
You will see very little typecasting in the typical ObjC program.
Casting is only really necessary when you want to have the compiler understand the type for a call, so that it doesn't give "may not respond" warnings.
Suppose we are writing a class (let's call it Class) in an iPhone program. In all the samples out there, the init methods are typically declared like this:
-(id) initWithFoo: (Foo *) foo
My question is: would it be more logical to do the following? Why or why not?
-(Class *) initWithFoo: (Foo *) foo
From Implementing an Initializer on Mac Dev Center
The reason for this is that id gives an indication that the class is purposefully not considered—that the class is unspecified and subject to change, depending on context of invocation. For example, NSString provides a method initWithFormat:. When sent to an instance of NSMutableString (a subclass of NSString), however, the message returns an instance of NSMutableString, not NSString. (See also, though, the singleton example given in “Combining Allocation and Initialization.”)
I have several dataSources I use for one UIViewController. My view controller uses KeyValue Observing in order to follow the state of certain properties at runtime. When I swap dataSources, I need to stop observing those properties. The problem is, I'm not sure of the class of the dataSource at runtime, therefor something like this is not valid:
if (aDataSource != dataSource) {
// Ensure we stop observing the existing dataSource, otherwise bad stuff can happen.
[dataSource removeObserver:self forKeyPath:#"someKeyPath"]; // not valid, compiler doesn't know what class dataSource is.
[dataSource release];
dataSource = [aDataSource retain];
}
The compiler needs a concrete class in order to know the object's interface. How can I grab the class of dataSource in this particular case, and then typcast the dataSource for the removeObserver:forKeyPath: selector above? I prefer something dynamic/smarter than caching the name of the class in an NSString instance and referring to that whenever I switch. Meaning, I could always do something like:
NSString *lastDataSource = #"MyClass";
Class foo = [NSClassFromString(lastDataSource)];
Thanks.
If you code like this:
id foo = ...;
[foo removeObserver:self forKeyPath:#"someKeyPath"];
The compiler will be fine with it as objects with type id accepts any message (as long the signature is known to the compiler).
Now if you have:
id<NSObject> foo = ...;
[foo removeObserver:self forKeyPath:#"someKeyPath"];
The compiler will give you a warning:
warning: '-removeObserver:forKeyPath:' not found in protocol
This is because you're referring to the protocol NSObject not to the NSObject class where the KVO methods are defined.
But if you have:
NSObject* foo = ...;
[foo removeObserver:self forKeyPath:#"someKeyPath"];
That will compile fine too, as in this case you're using the class NSObject.
Related links:
Objective-C protocol
The id Type and Protocols
What do you mean it is not valid? Do you get a compile error?
Objective-C supports dynamic typing for objects by default. You should be able to call any method on any object in Objective-C, even if the compiler can't guarantee from the static type that that object supports that method.
I think you need to cast them to NSObject *, since that's where the KVO methods are (not in NSObject protocol).
Just let me add that the approach you outline with ...
NSString *lastDataSource = #"MyClass";
Class foo = [NSClassFromString(lastDataSource)];
... will of course not be able to supress your compile-time warnings, since the class "foo" will only get calculated at run-time. So even though you as the programmer can plainly see from the code that "foo" will end up being the class "MyClass", this is not clear to the compiler, and so if "MyClass" has a method "myMethod:" you will still get a compiler warning if you send that message to an object declared as "foo".
I'm guessing you realise this, but it's better to make it clear why that approach won't solve your problem.