Property '' not found on object of type 'id' - objective-c

I'm getting Property 'aVariable' not found on object of type id when trying to read or write aVariable to the array. Shouldn't it be known what class the object is that I added? Also noticed that it works to read the value with NSLog(#" %#",[[anArray objectAtIndex:0] aVariable]);
I'm a beginner at Objective C so it might be some simple thing I'm not getting.
AnObject
#interface AnObject : NSObject
#property (nonatomic,readwrite) int aVariable;
#end
AnotherObject
#interface AnotherObject : NSObject
#end
test.h
#import "test.h"
#implementation AnObject
#synthesize aVariable;
- (id)init
{
self = [super init];
if (self) {
aVariable=0;
}
return self;
}
#end
test.m
#implementation AnotherObject
- (id)init
{
self = [super init];
if (self) { }
return self;
}
- (NSMutableArray*) addToArray
{
NSMutableArray* anArray = [[NSMutableArray alloc] initWithCapacity:0];
AnObject* tempObject = [[AnObject alloc] init];
tempObject.aVariable=10;
[anArray addObject:tempObject];
// Property 'aVariable' not found on object of type 'id'
[anArray objectAtIndex:0].aVariable=[anArray objectAtIndex:0].aVariable + 1;
// Property 'aVariable' not found on object of type 'id'
NSLog(#" %i",[anArray objectAtIndex:0].aVariable);
// This works
NSLog(#" %i",[[anArray objectAtIndex:0] aVariable]);
return anArray;
}
#end

This code:
[anArray objectAtIndex:0].aVariable
Can be broken down into 2 sections:
[anArray objectAtIndex:0]
This returns an id- because you can put any type of object into an array. The compiler doesn't know what type is going to be returned by this method.
.aVariable
This is asking for the property aVariable on the object returned from the array - as stated above, the compiler has no idea what this object is - it certainly won't assume that it is an AnObject, just because that is what you added a line or two earlier. It has to evaluate each statement on its own. The compiler therefore gives you the error.
It is a little more forgiving when using accessor methods:
[[anArray objectAtIndex:0] aVariable];
This will give you a warning (that the object may not respond to the selector) but it will still let you run the code, and luckily enough your object does respond to that selector, so you don't get a crash. However this is not a safe thing to rely on. Compiler warnings are your friends.
If you want to use the dot notation, you need to tell the compiler what type of object is being returned from the array. This is called casting. You can either do this in two steps:
AnObject *returnedObject = [anArray objectAtIndex:0];
int value = returnedObject.aVariable;
Or with a mess of brackets:
int value = ((AnObject*)[anArray objectAtIndex:0]).aVariable;
The extra brackets are required to allow you to use dot notation when casting. If you want to use the accessor methods, you need fewer round brackets but more square brackets:
int value = [(AnObject*)[anArray objectAtIndex:0] aVariable];

-[NSArray objectAtIndex:] returns an id pointer. Since id does not contain information about your protocol the compiler cannot know the object has this property you declared; that is why it complains.
You can solve this by either cast the return value of objectAtIndex: or by using the getter/setter notation, i.e. [anArray objectAtIndex:0] setAVariable:...]. Also make sure you import your protocol definition, otherwise the compiler might also not know about the declared method and issue a warning.

Related

In objective C lazy instantiation, why don't we touch the setter?

In objective C, its common practice to instantiate internal class arrays (and the like) in a lazy manner.
So if you call on the getter, it first checks if the array isn't nil, and allocates memory for it if needed.
But what about the setter?
If you are trying to insert some value into one of the array cells, since we did not allocate memory for it yet - where does it go?
I'm missing something here, clearly. Would be happy for a clarification.
I'm not sure I understand your question, but if you do this:
#property (nonatomic, strong) NSMutableArray* myArray;
...
- (NSMutableArray *) myArray {
if(!_myArray) {
NSLog(#"created");
_myArray = [[NSMutableArray alloc] init];
}
return _myArray;
}
...
[self.myArray addObject:#"test"];
The getter is actually getting called when you call addObject:, so you'll see "created" being logged.
So #property declarations are syntactic sugar for declaring, in the case of objects, pointers to instance variables. The "nonatomic" refers to the type of getter and setter automatically created (in this case "non thread safe.") And the "strong" is an indicator to ARC to increase the retain count of the variable.
So when you declare:
#property (nonatomic, strong) NSMutableArray* myArray;
This is what really gets created in your class - just a pointer to your hidden instance variable.
#implementation MyClass {
NSMutableArray *_myArray;
}
As you can see in the getter, you are initializing the _myArray pointer to point to a new NSMutableArray:
- (NSMutableArray *) myArray {
if(!_myArray) {
NSLog(#"created");
_myArray = [[NSMutableArray alloc] init];
}
return _myArray;
}
However in the setter, you are just updating the pointer to a variable you have already created.
self.myArray = [[NSMutableArray alloc] init];
This sends your class the following message:
- (void) myArray: (NSMutableArray *) myArray {
_myArray = myArray;
}
As you can, see the setter doesn't need any special initialization most of the time. The only time you want to create a custom setter is when you want to validate the incoming object has special properties. A contrived example is checking that the NSMutableArray is no larger than 10 objects:
- (void) myArray: (NSMutableArray *) myArray {
if (myArray.count < 10) {
_myArray = myArray;
}
}
Finally, I would like to point out that you can actually lazy instantiate objects using the short ternary operator and parenthetical return values. For example, the following statement:
- (NSMutableArray *) myArray {
return (_myArray = _myArray ?: #{}.mutableCopy);
}
Is equal to:
- (NSMutableArray *) myArray {
if(!_myArray) {
_myArray = [[NSMutableArray alloc] init];
}
return _myArray;
}
You can even macro this pattern into (WSM is my class prefix):
#define WSM_LAZY(object, assignment) (object = object ?: assignment)
So you can write statements like this:
- (NSMutableArray *) myArray {
return WSM_LAZY(_myArray, #{}.mutableCopy);
}
Or even use compound statement syntax to rewrite the original setter you presented as an example:
- (NSMutableArray *) myArray {
return WSM_LAZY(_myArray, ({
NSLog(#"created");
#{}.mutableCopy;
}));
}

is it allowed to call [obj copy] in ObjC with ARC?

There's one thing I don't understand regarding ARC: how should we now treat local variables that were created using [... copy]? If I make a property with (copy) flag, ARC will handle this automatically, but as far as I know there's no __copy flag for variables.
I've tested this with such code:
#interface Foo : NSString
#end
#implementation Foo
- (void) dealloc {
NSLog(#"%p deallocated", self);
}
- (NSUInteger) length {
return 1;
}
- (unichar) characterAtIndex: (NSUInteger) i {
return 'x';
}
#end
- (void) foo {
Foo *f = [[Foo alloc] init];
NSLog(#"%p", f);
Foo *f2 = [f copy];
NSLog(#"%p", f2);
}
What I get is:
0x102406530
0x102015f10
0x102406530 deallocated
I never get "0x102015f10 deallocated", which would suggest the copied variable doesn't get released. It doesn't even get autoreleased, because when I made another method [Foo foo] that returned an autoreleased object, I did get a "deallocated" message a moment later.
So is there any way I can cause it to be released without converting it to a property?
Ok, my bad - ARC does actually handle copied objects properly. I got wrong results because of using NSString for the test, because I wanted to use a class that already implemented copying instead of implementing it explicitly; when I repeated the test on a class inheriting from NSObject and implementing copyWithZone: by returning [[Foo alloc] init], I got two "deallocated" messages. Thanks to #Paul.s for pointing that out.

Traversing a NSMutableArray?

To start let me tell you I am a total Objective-C beginner. This is my problem:
I have a NSMutableArray that stores objects, (Player) that has the name of the player and his/her score.
I am able to add objects to the array using addObject, but I am having trouble traversing this array. This
is how I do it:
// Get the reference to the array
NSMutableArray *myarray = [delegate getArray];
// Create a numerator
NSEnumerator *e = [myarray objectEnumerator];
id object;
while (object = [e nextObject])
{
[object printPlayer];
}
The method printPlayer belongs to the Player class and it just prints the name and the score.
The problem is when I have three players in the array and I am trying to print the content, it reaches this error inside the printPlayer method:
Thread 1: EXC_BAD_ACCESS(code=1, address=0x0000008)
Strangely if I use NSLog(#"%#", object); instead of [object printPlayer]; it prints a reference to the object and does not reach any error.
Anyone could point me what could be the problem when I try to use [object printPlayer]
Cheers
Update 1:
This is my printPlayer method:
-(void) printPlayer
{
NSLog(#"\n\nName: %#\nScore: %d", playerName, playerScore);
}
Update 2:
Player.h:
#interface PROGPlayer : NSObject
#property (nonatomic, assign) NSString *playerName;
#property (nonatomic, assign) int playerScore;
-(id) init: (NSString *) n;
-(void) printPlayer;
#end
Player.m:
#import "PROGPlayer.h"
#implementation PROGPlayer
#synthesize playerName;
#synthesize playerScore;
/**
* Player's class constructor
* #param n Player's name
* #param s Player's score
*/
-init: (NSString *) n
{
if (!(self = [super init])) return nil;
else
{
playerName = n;
playerScore = 0;
}
return self;
}
-(void) printPlayer
{
NSLog(#"\n\nName: %#\nScore: %d", playerName, playerScore);
}
#end
It seems like your problem is in the way you're defining your properties.
You're using assign rather than strong, or copy.
In a nutshell, it's because strong implies that you want your object to be retained.
Using copy implies that you want to create a new copy of an object or a value and set that as value of your property... As Mario and Jarsen explain, using copy is better practice when working with arrays to prevent the array being mutated (i.e. values changed) while it is being enumerated / traversed. Using copy also retains the new object.
If you're using ARC and your objects are not retained, then they will be released automatically by the compiler.
Using assign means that you assume the new object has been retained elsewhere and that you don't want to retain it again.
I suppose what was happening is that you were assigning your variable to your property, but the variable was being released (and hence resulting in nil) and causing the crash.
Here are a few links:
New to Objective C: Need help understanding strong reference vs assign
Objective-C ARC: strong vs retain and weak vs assign
Clarification on assign, retain, copy, strong?
Your playerName property should best be copied instead of assigned
#property (nonatomic, copy) NSString *playerName;
When trying to access the assigned value, the object most likely is gone causing the bad access.
Also remember to release playerName in dealloc when you set the property to copy.
Cheers
You just want to enumerate the array?
for (CustomClass *object in myArray){
[object printPlayer];
}
Either what Mike Z said or the "crude":
for (int i = 0; i < myArray.count; i++) {
CustomClass* object = [myArray objectAtIndex:i];
[object printPlayer];
}
While there are more elegant schemes, you can clearly understand what this one is doing, and how an NS(Mutable)Array is just a simple analog to a standard C array.

iOS - NSMutableArray shows objects out of bounds on setting property

I have implemented the following code to assign NSMutableArray to a property -
NSMutableArray * anArray = [responseDictionary valueForKeyPath:#"tags"];
NSLog(#"The array length is=%d",[anArray count]);
for (NSString *s in anArray) {
NSLog(#"you are %#", s);
}
[self setActiveTagArray:anArray];
It prints out the string values fine. But in the setter function, if I place a breakpoint I see that it shows there are two objects but they are "Out of Scope". What does this mean? What am I doing wrong? My getter also does not fetch any values. The property functions -
-(void)setActiveTagArray:(NSMutableArray *)tags
{
activeTagArray = [[NSMutableArray alloc] init];
activeTagArray = tags;
//NSLog(#"%#",[activeTagArray count]);
}
-(NSMutableArray *)getActiveTagArray
{
return activeTagArray;
}
Is activeTagArray a class variable as well as a property. Consider using _activeTagArray as the class variable name. And then in the .m file just use #synthesize activeTagArray = _activeTagArray;, and for get the second two methods completely.
Response to comment:
You said "I have implemented the following code to assign NSMutableArray to a property". I took this to mean you have "#property (nonatomic, retain) NSMutableArray *activeTagArray;" in your .h file. If this is the case then you would access it thru otherObject'sNameForYourClassHere.activeTagArray.
#synthesize create accessors & mutators for you.

Property and Synthesize in Cocoa

I am trying to learn Cocoa and wanted to develop a very simple game that I had played in my youth. It was originally created in MS Basic. Please note I'm very new to Cocoa and objective c
I have create and object called Defaults. It stores three integer variables, planets, fighters and players.
On the front end, I have three drop down boxes with various defaulted values.
I wanted to understand KVC as well as how to set values but I get the following warning...
"Incompatible integer to pointer sending 'int' to parameter of type 'int *'
[NEW CODE]
DEFAULTS.H
#import
#import
#interface Defaults : NSObject {
int fighters;
int planets;
int players;
}
#property (assign) int fighters;
#property (assign) int planets;
#property (assign) int players;
#end
Here is an excerpt of the implementation in my controller:
#import "DefaultsController.h"
#import "Defaults.h"
#implementation DefaultsController
- (id)init
{
self = [super init];
if (self) {
// Initialization code here
Defaults *theDefaults = [[Defaults alloc] init];
NSLog(#"Planets: %#",[theDefaults valueForKey:(#"planets")]);
}
return self;
}
-(void)dealloc
{
[super dealloc];
[Defaults release];
}
-(void) awakeFromNib{
}
-(IBAction) addPlanets:(id)sender{
[theDefaults setValue:[planetsButton titleOfSelectedItem] forKey:#"planets"];
[theDefaults setPlanets:[[planetsButton titleOfSelectedItem] intValue]];
theDefaults.planets = [[planetsButton titleOfSelectedItem] intValue];
NSLog(#"Planets: %i", theDefaults.planets);
NSLog(#"%#", [planetsButton titleOfSelectedItem]);
NSLog(#"%i", [[planetsButton titleOfSelectedItem] intValue]);
}
What am I doing wrong?
You've declared your properties to be integer pointers, but then you're attempting to set them to integer values, not integer pointer values. Remove the asterisks in "int *fighters", etc, to change them to integers.
Pointers are one of the most fundamental and challenging concepts of C-based languages (including Objective-C), and if you aren't clear on the distinction I drew above I suggest you look for some careful explanations of how they work.
Update:
Another issue I see here, which may relate to your question below, is that you're doing some funny things with your logging statements. When you put %# in a formatting string, you're telling NSLog that you want it to replace that with an Objective-C object. But an int is not an object; it's just a basic C type. Then, you're using key-value coding to retrieve your property value, which is further confusing the issue, because key-value coding is also intended to work with Objective-C objects. Replace that line with the following:
NSLog(#"Planets: %i", theDefaults.planets);
and I think you'll get something more in line with what you're expecting. %i tells NSLog that you have an integer value to print, and theDefaults.planets (or the equivalent [theDefaults planets]) will return the value of the planets property, whether it's an object or not.