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

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"];

Related

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

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

Understanding Objective-C method value passing

Lets say I have in viewDidLoad:
NSMutableArray *entries = [NSMutableArray array];
[self doSomethingWithArray:entries];
NSLog(#"%#", entries);
Then in method I have:
- (void)doSomethingWithArray:(NSMutableArray *)entries
{
// create some custom data here, lets say - Something *something...
[entries addObject:something];
}
How is it possible that entries (one at the top) now (after method is finished) contain object something, since object "something" is not added to property or instance variable, and nslog will log class "Something" ? And doSomethingWithArray doesn't return anything since its "void".
I have encountered this for first time and dunno if there is any name of this appearance ?
I have seen this for second time in some examples and really dunno how its done.
If anyone could explain this a bit whats happening here I would be very very grateful.
Thank you a lot.
Because Objective-C instances are passed by reference (as you can tell by the * pointer syntax). You basically pass the address of the array to the doSomethingWithArray: method. In that method you add something to the array referenced by that address. And of course once the method returns, your array will contain that new object.
When you are adding the something object to the array, the array always retains it i.e it maintains copy of the Something object.
So NSLog prints the something.
Hope that helps.

Correct way of setting a BOOL property

I have a BOOL property that I want to set in my class initializer.
#property (assign, nonatomic) BOOL isEditMode;
- (id)init
{
. . .
[self setValue:NO forKey:isEditMode];
return self;
}
The compiler gives me an "Incompatible integer to pointer conversion" warning. What am i doing wrong here?
The Key-Value Coding method setValue:forKey: only accepts objects as arguments. To set a BOOL, you need to wrap the number in a value object with [NSNumber numberWithBool:NO]. But there's little reason to do that. Key-Value Coding is a roundabout way to accomplish this. Either do self.isEditMode = NO or just isEditMode = NO. The latter is preferable in an init method (because setters can run arbitrary code that might not be desirable before an object is fully set up).
But to elaborate on the first point: The reason Key-Value Coding works this way is because the type system can't represent an argument that's sometimes an object and at other times a primitive value. So KVC always deals with objects and just autoboxes primitive values as necessary. Similarly, if you do [yourObject valueForKey:#"isEditMode"], you'll get back an NSNumber object wrapping the real value.
The correct syntax to set a property is just
self.isEditMode = NO;
If you want to use -setValue:forKey: you'd have to write it as
[self setValue:[NSNumber numberWithBOOL:NO] forKey:#"isEditMode"];
However, there's absolutely no reason to do this in your situation.
That said, since you're in an init method, I would strongly recommend avoiding any property access whatsoever and instead using the ivar directly, as in
isEditMode = NO;
This avoids the possibility of an overridden setter being called (either in this class or a subclass) that makes the assumption that the object has already completed initialization. For this same reason you also want to avoid property access inside of -dealloc.
You can just assign the value directly:
isEditMode = NO;
I think you mean:
self.isEditMode = NO;
If your code does indeed compile (I'm pretty new to Objective-C so I don't know) setValue probably takes a pointer to a string (#"isEditMode", e.g.) and not some other type (isEditMode, e.g.).

Help me understand why: [self propertyName] works while propertyName doesn't

So I had a class defined with a property - we'll call it propertyName for the sake of this example. I had the property setup with #synthesize in my implementation.
I have a method called objectToNSDictionary which basically dumps that property into a dictionary:
NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:
[self propertyName], #"propertyName", nil];
I return that dict to the caller I use a JSonWriter to convert it to a string and pass it off to some service...
Suffice it to say that the above works. However, my original implementation didn't use [self propertyName] but instead just used propertyName. When I did that, I always had an error saying unrecognized selector sent to instance when I tried to use the object in the caller.
What's the difference in syntax really saying and why does one work and not the other?
When you use [self propertyName] you are referencing the property you defined for your class, via the synthesized getter method. When you use propertyName directly you are bypassing the property and using the class ivar directly. This will work as long as your ivar really is called propertyName, which is not required and might not be the case. Generally its a bad idea to access your ivar directly because doing so circumvents the memory management scaffolding that the compiler generates for you.
You need to post relevant code from your calling class to be able to tell why you are getting a 'selector not recognized' message.

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.