How the Obj C object's property name may go through several calls and become a selector name? - objective-c

I have a small piece of code using MapBox framework:
if (tmpItem.itemTyype > 0 && tmpItem.itemTyype <= 12)
marker = [[RMMarker alloc] initWithUIImage:[UIImage imageNamed:[NSString stringWithFormat:#"PoiSmall%d",tmpItem.itemTyype]]];
else
marker = [[RMMarker alloc] initWithUIImage:[UIImage imageNamed:#"PoiSmall"]];
This code leads to an exception:
-[RMQuadTreeNode itemTyype]: unrecognized selector sent to instance 0xe9aaf00
The RMQuadTreeNode class is a MapBox' class and never be used by myself in my code. Note the unrecognized selector name - this is the name of my tmpItem.itemTyype property!
If I replace this code with the following:
if (tmpItem.itemTyype > 0 && tmpItem.itemTyype <= 12)
image = [UIImage imageNamed:[NSString stringWithFormat:#"PoiSmall%d",tmpItem.itemTyype]];
else
image = [UIImage imageNamed:#"PoiSmall"];
marker = [[RMMarker alloc] initWithUIImage:image];
then error is gone. It's not a problem to replace one fragment of code with another one, but I want to know HOW the 'itemTyype' property became a selector name and later was called somethere inside MapBox framework. The property name went thru NSString call, UIImage call, RMMarker call and do-not-know-how-many another calls to reach RMQuadTreeNode. HOW THIS CAN BE?
This knowledge is not necessary for me to solve this particular problem, but it is necessary to know Objective C better.
EDIT. I have to change the question above. Adjusted question is: I want to know HOW the 'itemTyype' property was passed not only to [NSString ...] call but also somethere inside MapBox framework.
In other words, should we see something like
-[UIImage itemTyype]: unrecognized selector sent to instance 0xe9aaf00
or
-[RMMarker itemTyype]: unrecognized selector sent to instance 0xe9aaf00
?
Answer is NO because the [tmpItem itemTyype] is a parameter of
[NSString stringWithFormat:#"PoiSmall%d",tmpItem.itemTyype]
(ok, just rewriting it as)
[NSString stringWithFormat:#"PoiSmall%d",[tmpItem itemTyype]]
but it's not a parameter of [UIImage ...] nor [RMMarker ...] nor something else like [RMQuadTreeNode ...].
So how it was passed thru the chain of all these calls above and reached [RMQuadTreeNode ...] call? How the parameter of [NSString ...] call magically turned into a parameter of [RMQuadTreeNode ...] call?

In Objective-C dot notation for properties is just a 'shortcut' for writing accessor methods. So when you write
object.property
This is equivalent to writing:
[object property]
And
object.property = ?
is equivalent to:
[object setProperty:?]
So, back to your example,
[[RMMarker alloc] initWithUIImage:… ,tmpItem.itemTyype]]];
is the same as:
[[RMMarker alloc] initWithUIImage:[UIImage imageNamed:…,[tmpItem itemTyype]]]];
This is why you're seeing method itemTyype being called.
So object tmpItem is an instance of RMQuadTreeNode, which doesn't declare method itemTyype (or the equivalent property).
These Apple docs give a fuller explanation.

Simple explanation:
If you declare a property setter and getter methods to access this property are generated automatically (different for older Xcode versions) as well as an instance variable for this property. A setter is used to set the value of this variable, a getter is used to get the value, both so KVO can be used as well.
If you declare a property itemType like for example
#property (strong) NSNumber *itemType;
you also get in your class
- (void)setItemType:(NSNumber*)newValue;
- (NSNumber*)itemType;
as well as a variable
NSNumber *_itemType;
When you use the dot notation of Objective-C 2.0 like self.itemType these getter/setter methods are actually called.
This is a short overview, you should definitely invest time and read up on Objective-C like for example this here

Related

Initializing a constant that's value takes a completionBlock argument in its initializer

I have a property, that in Objective-C I created like this:
self.myProperty = [[MyClass alloc] initWithCompletionBlock:^(MyClass *object) {
dispatch_async(dispatch_get_main_queue(), ^{
[self doSomethingAfterInitialization];
});
}];
And it worked great. Initialization of the MyClass object could create an indeterminate amount of time, so I passed a completionHandler in to it. When it finished, doSomethingAfterInitalization: would handle business.
Now in Swift, I'm trying to create the same object and assign it to a property, with problems.
The property never will change, so it makes sense to me to create it as a Swift constant.
So I'm trying it like this:
let myProperty = MyClass(completionBlock:{ (MyClass) -> (Void) in dispatch_async(dispatch_get_main_queue(), doSomethingAfterInitialization())})
To me that seemed like a direct translation... but the Swift compiler tells me that's not correct, via the error
Use of instance member 'doSomethingAfterInitialization' on type 'MyViewController'; did you mean to use a value of type 'MyViewController' instead?
Well that didn't help much. So instead I tried changing the call to the doSomethingAfterInitialization function to self. doSomethingAfterInitialization(), in which case I see
Value of type '(NSObject) -> () -> TodayWidgetTableViewController' has no member 'doSomethingAfterInitialization'
Any idea how I can fix this? Obviously my initializer is a little weird in the first place, so I'm wondering if this is something that doesn't really translate at all to Swift.

Why a pointer to no object can be asked to execute a setter?

I creating a pointer does not create a Class object.
But it can be send method to execute in another instance method.
I think just a pointer to the object can be asked to execute a method.
Why the "t" can use setter in the method like this image?
This happens because Xcode only checks if declared type of t variable has visible readwrite property named a. It doesn't care if t is actually object or something else at this point. For example, it cannot know if t is object or nil, or anything else in such situation
- (void) method:(One*) t{
t.a = #"Some string";
}
You can call it like this
[two method:nil];
Xcode won't complain. You can even do this
One *t = (One *)[[NSObject alloc] init]; //warning here
t.a = #"Some t"; //Xcode doesn't care, that t is actually NSObject and doesn't respond to setA:; But you get a runtime error
nil in ObjC responds to all selectors - you can call any visible method on nil object and you won't get a warning nor error. Just silently passes by

Accessing element in array - Request for member in something not a struture or union

I have created a custom button called ModuleUIButton which inherits from UIButton. This custom button has an mutable array property called cycleList.
I have an mutable array of ModuleUIButton's called moduleList. I need to be able to access an element of the cycleList mutable array, and I use the following call:
//Create ModuleUIButton
ModuleUIButton *newModuleButton = [[ModuleUIButton alloc]
//Customize ModuleUIButton
initWithFrame:CGRectMake(29.0, (200.0+(88*moduleCounter)), 692, 80.0)];
[newModuleButton setTitle:#"Module" forState:UIControlStateNormal];
[newModuleButton setBackgroundColor:[UIColor purpleColor]];
//Add newModuleButton to moduleList array
[moduleList addObject:newModuleButton];
//Access the first element in moduleList which is newModuleButton and add
// a string to it's cycleList property
[[self.moduleList objectAtIndex:0].cycleList addObject:#"new Cycle"];
When attempting to compile however, I get:
Request for member 'cycleList' in something not a structure or a Union
Is the compiler complaining because it doesnt know that moduleList's 0th element will be a ModuleUIButton? If so, how can I access any of the ModuleUIButtons properties by referencing it from the mutable array?
Any insight would be appreciated!
Is the compiler complaining because it doesnt know that moduleList's 0th element will be a ModuleUIButton?
Exactly. The return type of objectAtIndex: is id. The compiler can't know what the actual type is, and can't therefore turn the property access .cycleList into the correct method call as it normally would, because it can't know what the correct method is.
The property access essentially works by rewriting your code. The complier sees foo.bar and goes to foo's class, finds the methods that correspond to the bar property, and turns what you wrote into [foo bar] or [foo setBar:], as appropriate. The trick is that, since the methods associated with a property can have any name (not just the standard bar/setBar) the compiler must be able to determine the type of the object in order to figure out the right method names to use.
When you use the bracket syntax, you're telling the compiler the exact name of the method you want called. It doesn't have to do any lookup, it just turns that into the usual call to objc_msgSend.
If so, how can I access any of the ModuleUIButton's properties by referencing it from the mutable array?
Just use the "standard" syntax:
[[self.moduleList objectAtIndex:0] cycleList]
You can also give the compiler a hint by casting, as Taskinoor suggested:
((ModuleUIButton *)[self.moduleList objectAtIndex:0]).cycleList
This explicitly tells the compiler to treat the object being returned from objectAtIndex: as a ModuleUIButton; it can then figure out what the method name should be.
Try to cast like this:
ModuleUIButton *button = (ModuleUIButton *)[self.moduleList objectAtIndex:0];
[button.cycleList addObject:#"new Cycle"];

incompatible pointer type

I have this class:
#interface G2Matrix : NSObject
...
- (id) initWithArray:(float *)val;
...
#end
This line below give me a warning saying that the first argument to the method initWithArray has an incompatible pointer type:
float m[16];
...
G2Matrix* matrix = [[[G2Matrix alloc] initWithArray:m] autorelease];
If I change the method name to something like initWithArray1 the warning disappears. I know that some objects in foundation classes have a method with the same name, but I am deriving from NSObject, which doesn't have this method. What gives?
Additional info - I call the same initWithArray method from other init methods in the G2Matrix class, but I don't see the warning there.
At a guess, this is a type problem:
Inside the other init methods, you call [self initWithArray:...]. self is typed as a G2Matrix*. In this context the compiler can fully resolve which imp (C function pointer) will eventually handle the method call, and detect its signature (argument and return types) correctly.
Out in regular code, [G2Matrix alloc] returns an id. In this context the compiler can only tell the method selector, which will be bound to an imp at runtime. It has to guess which initWithArray: you mean, and as you can see from the warning it guesses wrong, since a foundation class has an initWithArray: method with a different signature. Your code does still work, the compiler just can't be certain.
Picking a unique name for the initMethod (initWithFloats: maybe?) is the recommended way to shut the warning up. Other ways are: break it into two lines; or cast the alloc return value to the right class:
G2Matrix *matrix = [G2Matrix alloc];
matrix = [[matrix initWithArray:pointerToFloats] autorelease];
// or
G2Matrix* matrix = [[(G2Matrix *)[G2Matrix alloc] initWithArray:m] autorelease];
Looks a little odd, but allows you to turn the treat-warnings-as-errors compiler flag back on.
#tathagata thats because initWithArray is method defined in NSArray class so you cannot use it unless you subclass NSArray class.
see the documentation on NSArray
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/NSArray.html
PS.
by use the method, i meant Override the existing method for your purpose which is not a good idea you can find the Subclassing Notes in the document.

Subclassing and Casting in Objective C

I came across a strange problem today. I created a subclass of UIView and added only 1 method to the template code provided by xcode.
#interface FloatView : UIView {
}
- (void)floatTest:(CGFloat)x;
#end
- (void)floatTest:(CGFloat)x {
NSLog(#"float was %f", x);
}
Then in my appDelegate I had code like this:
UIView *floatView = [[FloatView alloc] init];
[floatView floatTest:10.0f];
Pretty simple, right? What should this print out? I thought it would something like "10.0000", but no, it prints out "0.000000".
I wrestled with this for hours, trying to figure out what I was doing wrong, and then I changed the code in my appDelegate to
FloatView *floatView = [[FloatView alloc] init];
[floatView floatTest:10.0f];
Only then, did it print out the expected "10.0000". Why is this so? I've declared FloatView as a subclass of UIView, shouldn't I be able to assign a FloatView object to a UIView pointer without problems?
Even though floatView was declared a pointer to a UIView, it's really a floatView and it should be able to handle the floatTest message? Am I totally off base here?
Actually, polymorphism is working as expected. If it didn't work, nothing would have been printed (in your example, 0.0000 is being printed). The thing is, while your instance actually responds to testFloat:10.0f message, since the compiler can't statically see the method declaration (as UIView class doesn't declare such a method), it assumes that your method takes ... as argument and returns id.
When CGFloat is passed to a method that expects variable number of arguments (...), it's promoted to double. Thus, the receiving method is passed a double argument and thinks it's a float and it doesn't get printed correctly.
You can verify this behavior by changing NSLog line to:
NSLog(#"%f", *(double*)&x);
When the compiler sends the message to FloatView* rather than a UIView*, it can find the exact signature of the method. It can see it really expects CGFloat and doesn't promote the argument to double. As a result, it works correctly.
Additionally, if UIView* contained the method declaration that took a CGFloat, the compiler would call the method appropriately. To summarize, this is not a polymorphism issue; it's a missing method signature issue.