Objective-C, accessing object variables within an NSArray - objective-c

Is it possible to directly access an object variable with an array of objects like so..
NSArray *myObjectsInArray = [NSArray arrayWithObjects:(id) myClass,myOtherClass, nil];
NSLog(#"%d",[myObjectsInArray[0] returnSize]); //works perfect
NSLog(#"%d",[myObjectsInArray[0].size]); // error
I'm not sure if it's a syntax problem or if it cannot be accessed without a method.
thanks for any help.

Accessing an array like so myArray[0] is equivelant to calling [myArray objectAtIndex:0]. The return value of objectAtIndex: is id. Any method call may be sent to an id reference. However, the compiler will not allow the use of the dot syntax shortcut to call methods on it.
If you really want to use the dot syntax, you need to cast it.
NSLog(#"%d",[((MyClass*)myObjectsInArray[0]).size]);

Related

How to properly use makeObjectsPerformSelector: Getting error unrecognized selector

Let me start off by saying I am new to Objective C.
I am getting the error
atusMenuApp[24288:303] -[__NSCFConstantString createListItem]: unrecognized selector sent to instance 0x100002450
Here is my code:
selector = [NSMutableArray arrayWithObjects: #"nvda", #"aapl", #"goog", nil];
[selector makeObjectsPerformSelector:#selector(createListItem:) withObject:self];
- (void)createListItem:(NSString *)title {
//do some stuff
}
Now I have done plenty of looking around and it seems like the biggest reason for this issue is the addition of or lack of the :however I do believe I properly have that in place. Maybe I do not understand the use of makeObjectsPerformSelector very well as after look up the doc on it I found:
Sends to each object in the array the message identified by a given selector, starting with the first object and continuing through the array to the last object.
Any help would be great, Thanks!
[Only if you read the documentation (or thought a bit about why a method is named this way and not that), or even made the effort trying to understand the error message...]
The makeObjectsPerformSelector:withObject: method of NSArray does what it suggests it does: it makes the objects of the array perform the selector, that can have an optional argument. So
[selector makeObjectsPerformSelector:#selector(createListItem:) withObject:self];
will send the createListItem: message to every single NSString object in the selector array and pass in self as its argument. It won't perform the selector on self passing in the object. I. e., what you have is equivalent to
for (NSString *obj in selector) {
[obj createListItem:self];
}
Obviously, instead of this, you want the following:
for (NSString *obj in selector) {
[self createListItem:obj];
}
You don't even need that nasty method for this. A nice fast enumeration for loop will do it.
First you make an array of NSStrings. Then, you send them all the message createListItem. That's all fine and dandy, but NSString doesn't have any method called createListItem; just because you've defined an instance method called createListItem doesn't mean every instance of every class can use it. Only the class who's implementation file has the definition will be able to handle the message. For instance, I can't make a list of Car instances, then define the method fly in another class called Helicopter's implementation and expect to be able to call fly on an instance of Car; only Helicopter can use it. I recommend you read a good book on Objective-C and further familiarize yourself with classes, instances and instance methods.
You misunderstood the method.
It will call the method createListItem: with argument self over every object of the NSArray.
So the resulting call would be something like:
[#"nvda" createListItem:self];
...
Clearly that method doesn't exist for a NSString and there goes your exception.
If you need to apply a method of self to every object inside your array, simply loop through it.

Re-initialize NSMutableArray as NSMutableArray

I was having a problem with my app throwing an exception when calling removeObjectAtIndex on an NSMutableArray, saying that myLocationsArray was declared immutable. All other manipulation on that array was fine, it was most definitely declared correctly etc etc but somewhere in my app it was getting set as immutable. After investigating for a while trying to find where it was getting set immutable, I decided screw it and just redeclared the variable as such:
myLocationsArray = [[NSMutableArray alloc] initWithArray:[defaults
objectForKey:MYLOCATIONSARRAY_KEY]];
right before the removeObjectAtIndex call.
However I know this has got to be badwrong, I'm calling alloc/init twice on the same variable. However it's the only thing that has worked. Is there any way to remind this variable that it is an NSMutableArray without introducing memory leaks like I am?
NSUserDefaults returns immutable copy of your array. It doesn't matter whether you put NSArray or NSMutableArray in it, it always give you immutable copy back.
So, do this to get a mutable copy that you can work with
myLocationsArray = [[NSMutableArray alloc] initWithArray:[[[defaults objectForKey:MYLOCATIONSARRAY_KEY] mutableCopy] autorelease]];
or just this
myLocationsArray = [[defaults objectForKey:MYLOCATIONSARRAY_KEY] mutableCopy];
I would suggest to set a breakpoint on the line where your program is throwing an exception (the one containing removeObjectAtIndex) and inspect with the debugger the real type of the array. If you go with you mouse over the array name, a popup menu will display giving you all the information you need about the pointed object.
What I expect is that you find out this way that the object is an NSArray (vs. NSMutableArray) and then trace back to the point where you initialized it in the first place.
It looks like you're working with NSUserDefaults. All objects you get out of NSUserDefaults are always immutable, regardless of what you stored into it. NSUserDefaults doesn't keep a reference to the specific object you set into it, it keeps the data. It's effectively making a copy. When you get something out of NSUserDefaults, it makes a new (immutable) object from the data it has stored and gives that to you.
Unsurprisingly, you can't change what's stored in NSUserDefaults by mutating what you (think you) stored in it. You can only change what's stored by replacing what you previously stored by storing something anew.
The declaration should not matter; your error is a run-time error. It sounds like your myLocationsArray variable has been assigned an immutable array (NSArray) though whether it is being re-assigned somewhere or was always immutable is impossible to say from your code fragment.

Instance pointers in an array

I'm trying to save pointers of class instances into a mutable array. I'm able to do this but getting them back into use seems to be a problem. The next is how I inserted the pointers into the array:
Class *class = [Class new];
/* Do something with the instance */
[instanceArray addObject:class];
I am able to retrieve the wanted index from the array but when I try to access the instance variables etc. I only get compiler shouting at me or sometimes I get every variable as zero.
How am I supposed to get the instance back to use from the array? I know they are pointers but playing with them didn't seem to work.
Using addObject: the element is inserted at the end of the array. To retrieve it you can use -[NSArray objectAtIndex:]
Pointer arithmetic works in C since then your array is just a pointer to the first index, and array[i] is the same as *(array + i). In Objective-C this may still be done, however you're using an NSArray object. Now you don't have a pointer to the first object anymore, but to the instance of the class. To retrieve the objects stored in the array, you'll have to call the corresponding methods.
EDIT
So you are able to retrieve it from the array and then your pointer is not nil, so you do have access to the object. You know in Objective-C, all instance variables are private so you can't access them from outside. You'd have to declare them as properties first (please refer to the documentation). Also, when you declare a variable to be of type id, accessing a property with dot-syntax doesn't work, it will cause a compiler error.

Objective-c return method returning NSMutableArray instead of declared NSArray return type

If I want to return an immutable array like this + (NSArray *)ids but inside this method I'm declaring a NSMutableArray because I want to sort it using -sortUsingSelector:.
Returning this method works perfect.
But is it "okay" to write code that declares that the return method should be one type and the actually type is another?
Does this work because NSMutableArray is a subclass of NSArray?
Is the actual return value an NSArray or an NSMutableArray?
(…) is it "okay" to write code that declares that the return method should be one type and the actualy type is another?
Yes, it is provided the types are compatible; see the next paragraph. In fact, there are some cases in Cocoa (notably class clusters) that do that as well.
Does this work because NSMutableArray is a subclass of NSArray?
Exactly. Since NSMutableArray is a subclass of NSArray, it inherits all its methods and declared properties, so it publicly behaves like NSArray. This is called the Liskov substitution principle.
Is the actual return value a NSArray or a NSMutableArray?
The return value is whatever you’re returning. Since you’re returning an NSMutableArray, it’s an NSMutableArray. From the caller perspective, it is an object that can be used like an NSArray.
Whether it's "okay" or not is a difficult question. If it's a small project and only you use the code, it might be allright. However, if you rely on nothing being able to change the "NSArray" returned, it's not ok. (See the last point)
Yes.
It's an NSMutableArray. A simple cast will let you change the values in it.
If you actually want to a return an immutable object, do the following:
NSArray* immutableArray = [[mutableArray copy] autorelease];
return immutableArray;

How to store non-objects in a dictionary?

I tried storing a selector(SEL) in a NSMutableDictionary and it caused a crash, probably because the dictionary tries to dereference it as an object pointer. What is the standard recipe for storing non-objects in a dictionary?
You can convert selectors to NSString using NSStringFromSelector() and you can go back the other way with NSSelectorFromString().
SEL aSel = #selector(takeThis:andThat:);
[myDict setObject:NSStringFromSelector(aSel) forKey:someKey];
SEL original = NSSelectorFromString([myDict objectForKey:someKey]);
Try using a NSMapTable with NSObjectMapKeyCallBacks and NSNonOwnedPointerMapValueCallBacks. This works like a NSMutableDictionary but allows any pointers as values, not just objects.
You also could store the selector in a NSInvocation object and use that with a regular dictionary. If you need to store more than the Selector (target, parameters and so on) this is probably the better solution.
Wrap them into objects.