Dot syntax with 'id' properties - objective-c

Recently i asked question about accessing properties of id object assigned to custom class. Solution is casting or using setter/getter methods.
But, i can't understand one thing, why i can't use dot syntax?
Compare that two lines of code:
myClass.var
[myClass var]
Result is identical, and in fact, it similar. But, if u do:
id obj = myClass;
obj.var; // Error - trying to get value
obj.var = 5; // Error - trying to set value
Its not real error message, i just want to show you that warnings are appear and it crash a build.
But, if u do:
id obj = myClass;
[myClass setVar:5]; // No error - setter
[myClass var]; // No error - getter
No warning, app work fine.
How this could happen? Yes, i can use casting or getting/setting values like described above, but why dot syntax not work?

id is "any kind of object", which means it doesn't have any property named var so you can't access it using dot notation, because var it's not a member of class id.
Now, if you use the 'messaging' pattern [myClass var] you are sending a message and since message dispatching in objective-c is a runtime functionality and dynamic typed, it won't complain at compile time, because the compiler doesn't know whether myClass responds to the selector var (in this case 'has a property named var') or not until it actually tries to dispatch the message to the object.
Hope it helps.

Related

Whats the difference between setting a property in an object through dot notation vs setter?

Looks like there is some subtle difference between setting a property through dot notation vs setter.
In my objective-C code, I have a property that can be set to either an instance of class A or class B. So I declare it as #property id delegate
Both class A and class B have same properties and methods. Just the implementation (functionality) is different if you call methodM1 on one vs the other.
I see that following lines don't work (using property directly through dot notation)
self.delegate = [[Class A alloc] init];
self.delegate.property1 = #"ABCD" ; //Does not work, get an error that property1 not defined on id
But following line works
[self.delegate setPropert1:#"ABCD"] ; //works
What is the difference? Is it that in the setter case, it knows that there is at one class that responds to setProperty1 method so compiler allows it? if yes, it looks like the answer to Q below (which was also my original understanding) is incorrect. I.e dot notation does not get replaced by setter.
Difference between setting a property directly and using its setter?
The problem is that self.delegate is typed as id. You can send any message to an id, so it is legal to say
[self.delegate setProperty1:#"ABCD"];
The compiler just lets go of all control and lets you crash the program if this message doesn't work out, because this is an id.
But for properties there is no such loosening of control. To use a property, you must have an actual type for which that property is defined. Thus you could say
((ClassA*)self.delegate).property1 = // ...
Or
((ClassB*)self.delegate).property1 = // ...
But you can't say
self.delegate.property1 = // no, because self.delegate is an `id`
However, this is just a matter of notation. Even if you could say self.delegate.property1 this would be just another way of saying [self.delegate setProperty1:], so nothing is lost just because you can't use that notation.

Xcode doesn't recognize properties after assigning to id

I have an NSMutableArray of two different objects inside. I am trying to recognize the first object of the array and assign it properly. Here is the example code with my idea of
// I should declare a variable here, firstly I thought about "id someObject;"
// and assigning to it in if statement.
id someObject;
if ([[someArray objectAtIndex:0] isKindOfClass:[firstOpponent class]]) {
someObject = (firstOpponent*)[someArray objectAtIndex:0];
} else {
someObject = (secondOpponent*)[someArray objectAtIndex:0];
}
[someObject method]; // this is OK
someObject.position; // property 'position' not found on object of type '__strong id'
With the idea of declaring "id someObject;" before if statement there is some problem with properties. I read that it isn't possible, thats why I'm asking for other solutions.
Your if statement achieves nothing in respect of typing.
In Objective-C a cast on a reference type, such as (firstOpponent *), does nothing at runtime; it simply allows the compiler to produce better error messages.
In your code you cast, which tells the compiler the type of the reference, and then you immediately assign to a variable of type id - which is the most general/least specific object reference type - and the compiler now knows nothing about the contents of someObject other than it contains a reference to some object.
So your code is equivalent to:
id someObject;
someObject = someArray[0];
[someObject method]; // this is OK
someObject.position; // compile time error
When calling a method on a reference typed as id, in your case someObject, the compiler does no checking and simply allows the method call. At runtime a check is done to verify the actual object references supports the method, and if not a runtime error will occur and the application will be aborted.
However the compiler will only call a property on a object whose type it knows. This is because it needs to know the type to determine what method call to translate the property access into. In general, but not always, the property access:
object.property // read a value
object.property = value // write a value
translate into the method calls:
[object property]
[object setProperty:value]
So you can access the property by doing the translation yourself and writing one of the second pair. If at runtime the reference object does not support the property then you will get an error and your application will abort.
Another option is to define a protocol, say OpponentProtocol, which declares the methods and properties all opponent classes should implement, and then have both your opponent classes implement it. You may then declare:
id<OpponentProtocol> someObject;
where the type means "any object reference as long as it implements the protocol OpponentProtocol". With such a type the compiler knows how to translate a property access into the appropriate method call, so you can access OpponentProtocol properties on someObject.
HTH
You should create a protocol that your two classes can confirm to and use id < MY_PROTOCOL > instead of just id so the compiler knows what the class is capable of responding to.

How to access variable in other class [duplicate]

This question already has answers here:
Property not found in object type
(3 answers)
Closed 9 years ago.
I'm trying to access a variable in another class, but it's giving me the error
Property 'itemType' not found on object of type '__strong id'
Basically, I init the class with this
GameMsgs *warningMsg = [[GameMsgs alloc]initWithItem:#"remove_village-object-warning" andCallingMethod:self];
and in GameMsgs...
- (id)initWithItem:(NSString*)itemTypeP andCallingMethod:(id)callingMethod
{
if ((self = [super init]))
{
sharedInstance = [SKGame sharedInstance];
myCallingMethod = callingMethod;
...
But when I try to access a variable in myCallingMethod, I get the above error. This is how I'm trying to access it...
Text *valueT = [[Text alloc] initWithText:[[myCallingMethod.itemType objectForKey:#"templateKingdomObject"] objectForKey:#"removeCost"] withX:70 withY:60 withSize:14 withFieldWidth:100 withFieldHeight:30 withColour:0xffffff withFont:#"MarkerFelt-Thin"];
With the error just at the start of itemType.
myCallingMethod is a type of id.
I presume this is something obvious, but I'm new to Obj-c still.
The problem is myCallingMethod is a type of id. What this means is that myCallingMethod could be any type of object. That means that the compiler doesn't know what it is so it doesn't know that your dot notation is correct.
You can either use the traditional method notation (then the compiler will just trust you and throw an exception at runtime if you're wrong). Or, change your definition of myCallingMethod to use an actual Class name (one which defines the property itemType).
Your variable callingMethod is of type "id". In the land of Obj-C "id" doesn't mean anything other than an address to basically anything. The compiler doesn't know the real type of the callingMethod object and thus assumes it doesn't have any methods. You can fix this in two ways:
Changing your method declaration to incorporate an actual class for your "callingMethod" variable
- (id)initWithItem:(NSString *)itemTypeP andCallingMethod:(YourClass *)callingMethod
Or by casting to your own type where you need it.
Text *valueT = [[Text alloc] initWithText:[[((YourClass *)myCallingMethod).itemType objectForKey:#"templateKingdomObject"] objectForKey:#"removeCost"] withX:70 withY:60 withSize:14 withFieldWidth:100 withFieldHeight:30 withColour:0xffffff withFont:#"MarkerFelt-Thin"];
But that's ugly.
This is all assuming that your variable "callingMethod" is of one type, otherwise look into protocols.

Why does calling a property on NSObject pointer gives build errors?

I have an NSMutableArray which returns me some object.
The object which I added had properties name,age.
now when I use these properties on the Object returned (obj.name or obj.age ),
Compiler says, no such member, use (->) instead of (.)
I understand that NSObject wont have these members and hence it wont understand the property.
But If i use setters, and getters as method ([obj name] or [obj age]) syntax instead of this properties, I dont get any errors.
But using property means calling a setter or getter only ?
ad Objective C is suppose to be dynamic language, right ?
Do you cast the returned object to your object type (MyObject)?
You should do something like:
((MyObject*)[mutableArray objectAtIndex:0]).age = 20;
The reason you're not getting any errors when using [[mutableArray objectAtIndex:0] name] syntax is that you're calling a method on the returned object (which is of type id), and id s tend to not choke in the compile-time if you call a (yet) non-existant method on them. At the run-time, [mutableArray objectAtIndex:0] might resolve to type MyObject an in that case, the message [obj name] has a proper implementation (IMP). If it doesn't resolve to MyObject, your app will crash.
And note that the reason you're not even getting a compile-time warning is that Xcode knows that there is at least 1 class in your codebase that implements the method name, and it trusts you with calling this method only on instances of that class. if you do something like ((MyObject*)[mutableArray objectAtIndex:0]).ageeeeee = 20;, it'll give you a warning as there's a very good chance that it'll crash (no class in your app implements the method ageeeeee statically).
The type id does not have a property name, and that's why you can't use dot syntax.
Actually, this incident shows perfectly why ObjC is called a dynamic language!
That's right - dot syntax is not supported in such case.
You need to cast a pointer to the actual class:
((MyObject*)[array objectAtIndex: 0]).name = #"Bill";

Type cast in cocoa for fix warnings

I have next situation:
Method:
-(void) myMethod:(id)inValue
{
long a = [inValue longValue];
}
Compiler shows me a warning that -longValue - is multiplied:
multiple methods named '-longValue' found
What can I do to resolve this warning without change method name?
Thank!
Strongly type your method's argument to tell the compiler which variant of the -longValue message you want to use, e.g.:
-(void) myMethod:(NSNumber *)inValue
{
long a = [inValue longValue];
}
If you want to accept multiple types that respond to -longValue (say, NSNumber and NSString) then you'll have to go back to using id and you'll see the warning. You see, something in your (yes, your, not Apple's) class hierarchy has bunged up and used a different signature for -longValue, so the compiler has no way of knowing which one you want to use. id tells it "this is an object" but it provides no explicit information that the compiler can use to resolve its conundrum.
Is there a particular reason why you're passing an id instead of a strongly-typed object?
I'm understood where from this warning - it completely right. If I were compiler developer - I were make possible something like this:
id a = [[[inValue class] alloc]] init];
for type cast; and after this may be:
a = inValue
[a longValue];
for resolve this warning.
But I'm not.)
And I'm only study programming for Mac. So i just asking - it possible or not.