Why do we create instances of classes in for loops? - objective-c

So I am busy reading an objective-c book by Big Nerd Ranch. I'm on chapter 17 at the moment and managed to complete the required challenge at the end of the chapter. However, I just have two question that I would like to understand.
In the following bit of code - StockHolding is a custom class that has instance variables and the stocks (an array) points to three instances of stockholding with values setting its stock value and cost in dollars.
At first I tried to access the array to get the data from the objects it pointed to - but it seems that was not going to work as the array doesn't know what data its objects contain - just where they are in memory, right?
What I want to know is why was it necessary to create a new instance of stockholding (holdings) in this for loop to access those variables?
How does the new instance of stockholding know what the values of my stocks are?
for (StockHolding *holdings in stocks){
NSLog (# "%# has %d shares. Cost: $%.2f. Stock value: $%.2f", [holdings stockName],[holdings numberOfShares], [holdings costInDollars], [holdings valueInDollars]);
}
I'm going to try have a guess here to see if maybe I understand it a little better?
We create an instance of our class in the for loop so that we have access to its instance methods and variables - then we use the stocks array to get the variables from those objects in the array?
I may be completely off.. :(
Any advice?

stocks is an array having the objects of type StockHolding
So in order to access all values in the array and print the values.You need to get all the StockHolding instance inside the array we use for ...in method
note Here new instance is not created just new reference is made to the memory that is in the array so that you can access it and use it

Absolutely no new instances are created in the for loop at all. Since Objective-C objects are always represented as pointers, one variable != one instance. The holdings local variable inside the loop is assigned the pointer to the element of the array which is currently being enumerated upon each iteration. It's just a "reference" to an already existing object.

You're not creating new instances. You're iterating through existing instances.
Presumably in [CODE] you have created the objects and added them to the NSArray. The for loop just gives them to you one at a time. You name it holdings, do something with it, then grab the next.
That's all.

In Objective-C objects are typeless. Any message can be sent to any object. Code like [holdings stockName] means "send the message 'stockName' to the object 'holdings'". So the Objective-C runtime will inspect the object to see whether it implements that message. If so then it'll pass execution into the implementation.
The type of your object makes no difference to how processing will occur at runtime.
An NSArray stores anything that conforms to the NSObject protocol. So it can hold any old mix of objects. The same goes for the other collections.
Although you could write all your code without mentioning a single object type, you usually don't because if you say which type of objects you're dealing with then the compiler can perform some sanity checks. That makes you less likely to write broken code.
So the code:
for (StockHolding *holdings in stocks)
just means "let me do something to every object in the collection stocks and don't give me any compiler warnings when I treat them like instances of StockHolding". They may actually be other classes. If they're other classes that implement stockName, numberOfShares and the rest then your code will work perfectly.
So, for example:
NSMutableArray *arrayOfStrings = [NSMutableArray array];
[arrayOfStrings addObject:#"34.3"];
[arrayOfStrings addObject:#"19.8"];
float total;
for(NSNumber *number in arrayOfStrings)
{
total += [number floatValue];
}
Will compile and work perfectly — not because the strings are actually converted to numbers but because both classes implement floatValue to return a float. So each NSNumber *number is actually an NSString, and if you tried to call, say, isEqualToNumber: on any of them you'd raise an exception because that isn't implemented by strings. But telling the compiler you're going to act as if they're numbers means you don't get a warning for using floatValue and when the runtime spots that the object implements floatValue execution continues as usual.

The for..in loop is used for fast enumeration.
This
for (StockHolding *holdings in stocks)
{
}
won't create any new object, it takes one object from array and cast it to the specified type and assign it to the specified variable.
Means:
Takes the object from the array . Equivalent to [stocks objectAtIndex:index];
Assign it to the specified object. Equivalent to StockHolding *holdings = [stocks objectAtIndex:index];
Note that Only the reference is used (assignment) there is no object is allocated.

Related

Checking if object exist in object array without looping

I would like to check if my NSMutableArray contains my custom object. But if I understand correct contains functions searches for the same object in array (placed at the same memory point)
if(![objectArray containsObject:objToCheck])
{
[objectArray addObject:objToCheck];
}
I know that objectArray has identical object with identical variable values compared to objToCheck, yet such if always returns false. Is there a way to check this without writing custom loop and comparing objects by their parameters?
Override the [NSObject isEqual:] method (actually it's part of the NSObject protocol) of your custom object and check whatever instance variables make sense to you for an object to be considered equal.
Here's an Apple Cocoa Competency article on the subject.
You might try creating a temporary NSSet from your array and testing against that for membership.

Creating a NSMutableArray to hold pointers

I am trying to create a mutable array in objetive c to hold references to objects. The objects in the array are regularly updated through user interaction and i want the array to automatically reflect changes made to the objects as they occur. Does anyone know if there is a way to do this? Perhaps store pointers to the objects instead of the objects themselves in the array? Any help would be much appreciated
Thanks in advance
Edit: I should mention that the objects are not exactly being updated in the strict sense of the word. They are being reinitialized. For ex if i had a controller:
MyController = [MyController alloc] initWith.....]]
the above call is made again with different init parameters.
The array always stores the pointers.... It holds a strong reference to it or sends it a retain message (if using non ARC).
So
[myMutableArray addObject: anObject];
adds the pointer to it.
If you now change anObject's properties and access it later through the array, it will
give you the pointer to just that object with the changes to its properties.
Edit:
No, if you alloc/init, you are creating a new object instance and allocate new memory for it on the heap (ie, it's another pointer to a new memory address).
What exactly are you trying to accomplish? There sure is a way, if you provide a little more detail.
If you alloc/init the object with the same class, why not just create a method to change the object's properties:
Instead of
myObject = [[MyClass alloc] initWithParameter1: one parameter2: two];
You could create a method that changes these properties:
[myObject updateParameter1: anotherOne parameterTwo: anotherTwo];
And, of course, the advantage of a mutable array is, that you can change its contents, so like #Eli Gregory pointed out, you can replace an object with another one (or rather the pointers to it).
Because you want to point to a newly allocated and initialized object, you can't 'update' the pointer, what you can do is 'replace' the pointer with a new one at a certain index.
A method you could use to do this is:
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject
so it would look something like:
NewViewController *new = [[NewViewController alloc] init..];
[myArray replaceObjectAtIndex:x withObject:new];

Are pointers to arguments in Objective C methods are const by default?

There are methods in Cocoa classes that accept an address of a pointer. Most commonly the argument is address of future NSError * object in CoreData validation methods (among others). This way it is possible to put custom or owned object into the place of the address that given argument points to.
My question is: why can't we do that with simple pointer arguments? E.g. let's say I have a method:
- (void)addObject:(id)someObject toArray:(NSMutableArray *)array;
I can easily pass the mutable array as second argument, call addObject: on it and after the method call the array will be modified. Why is this not done with NSError * objects? Are pointers passed to methods are defined as const by default? Is this to avoid accessing NULL?
Why is this not done with NSError * objects?
Because there's no such thing as an NSMutableError. Your example works because you can modify the contents of the array without modifying the pointer to the array itself. However, since NSError instances are immutable, you cannot modify an NSError. As such, you must necessarily create a new error object. And if you want to return that error object along with some other piece of data, at least one of those must be done via an out-parameter (such as an NSError **).
Are pointers passed to methods are defined as const by default?
Nope. Inside that method you're welcome to do this:
- (void)addObject:(id)someObject toArray:(NSMutableArray *)array {
someObject = somethingElse;
[array addObject:someObject];
}
What's important to remember here is that you're only changing a pointer in a slot in memory. The memory slot corresponding to someObject is just a space on the stack, and you're changing the contents of that space to have a value that points to a different allocated object than the one you were given.
Is this to avoid accessing NULL?
Any NULL protection you need must be done yourself.
It's because the NSError class does not define any way to modify instances after creation. The pointer itself is mutable, but an NSError is not.
They are all plain C pointers. They are not const unless you make them const. Const pointers are not a good thing to use in most situations in objective-C, or even often plain C. Const pointers are a subtle concept, and the complexities of the meaning and syntax don't mesh well with the Objective-C style of programming. Forgetting they exist is likely a good first approximation.
Example: NSArray and NSMutableArray - we would not need an NSArray class if const worked 'correctly' - but it can't due to the design of C.
** - For NSError, etc., the idea is to create an NSError, not alter the one you have passed in. In other words, you need a pointer to a pointer to be able to create an instance (i.e. change the actual object).

When and when to not allocate memory to objects

NSArray *array = [dictionary objectForKey:#"field"];
and
NSArray *array = [[NSArray alloc] initWithArray:[dictionary objectForKey:#"field"]];
I see both kind of approaches very frequently in objective C code.
When tried to understand, I found both of them used in similar situation too, which makes contradiction. I am not clear on when I should use 1st approach and when 2nd one?
Any idea?
Detailed explanation and useful references are moms welcome.
First off, those two examples are doing slightly different things. One is retrieving something from an existing dictionary and one is creating a new array by retrieving something from an existing dictionary (the value of that key is an array).
But, if you're asking the difference between getting objects by alloc vs. convenience methods. ([NSString alloc] init vs [NSString stringWith ...), by convention, you own anything that you call alloc, new copy or mutableCopy on. Anything that you call that is not those, is autoreleased.
See the memory guide here. Specifically, look at the rules.
Getting an autoreleased object means it will go away at some point in the near future. If you don't need to hold onto outside the scope of that function, then you can call autorelease on it or use one of the convenience methods that's not alloc, etc...
For example:
// my object doesn't need that formatted string - create the autoreleased version of it.
- (NSString) description {
return [NSString stringWithFormat:#"%# : %d", _title, _id];
}
// my object stuffed it away in an iVar - I need the retained version of it. release in dealloc
- (void) prepare {
_myVal = [[NSString alloc] initWithFormat:"string I need for %d", _id];
}
In the first example, I created a convenience methods for others to call, my class doesn't need that object beyond the scope of that method so I create the autoreleased version of it and return it. If the caller needs it beyond the scope of his calling method, he can retain it. If not he can use it and let it go away. Very little code.
In the second example, I'm formatting a string and assigning it to an iVar variable that I need to hold onto for the lifetime of my class so I call alloc which will retain it. I own it and releasing it eventually. Now, I could have used the first version here and just called retain on it as well.
You have a fundamental misunderstanding of allocations versus instance methods.
The first example, NSDictionary's -objectForKey method, returns id, not an instance of NSDictionary, therefore it does not allocate or initialize the variable.
The second, however is the classic retain part of the retain-release cycle.
The two methods are fundamentally equal (if we are to assume that array is alloc'd but empty in the first, and nil in the second), and both get ownership of the array object. I would go with the second, as it guarantees a reference, and it's shorter.
What I think you're confusing this with are new and convenience methods. Convenience methods (like NSNumber's +numberWithInt:, NSString's +stringWithFormat:, and NSMutableArray's +array), return an autorelease instance of the class (usually). New takes the place of alloc and init in just one word.

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.