Objective C creating custom arrays - objective-c

In other languages I could create a class then use this to create an array of objects in that class eg class price which is used in a performance class to define a price[] prices;
in objective C i cant get this to work, how do you do it?
The price class is an inherit of NSObject, can I use NSMutableArray?

If you have a class Price, and it inherits from NSObject, then you can have an array of them stored in an NSArray or NSMutableArray. You could also store them in a C array, or an STL vector, although the memorymanagement sematics may be difficult in those cases.
In the case of an NSArray/NSMutableArray, the array takes an ownership reference on the object, so you can release it after adding it to the array, and it will remain in memory until it is removed from the array (and all other locations).
Code might look like:
NSMutableArray* a = [NSMutableArray array];
[a addObject:[Price price]];
// and/or
[a addObject:[[[Price alloc] init] autorelease];
// and/or
Price* p = [[Price alloc] init];
[a addObject:p];
[p release];
NSLog( #"The array is %#", a );
// Remember at this point you do not "own" a, retain it if you want to keep it, or use [NSMutableArray new] above
When a is released, all the Price objects it contains will be released (and potentially deallocated).

Yes, NSMutableArray is what you would want to use.

To answer your last question first: if you are storing instances of Objective-C object (which, as you say, inherit from NSObject) then yes, use NSMutableArray. However, this won't work for primitive types (like int, BOOL, char, char*, etc.).
Objective-C uses the C way of declaring arrays with one caveat: you can't allocate memory on the stack (which is what that does in C), only on the heap. That means you have to use malloc (prefer NSAllocateCollectable() on Leopard or later) and free to manage the memory. Also, you declare the array as a pointer to an array or pointers. Something like this: (Note: pulled from thin air, untested.)
Price **prices = malloc(numberOfEntries * sizeof(Price*));
...
free(prices);
Using an NSMutableArray is generally much easier than managing your own arrays. Just be aware of the memory management rules: collections call -retain on an object when it is inserted, and -release on it when it is removed (or on all objects if the collection is deallocated).

Related

Memory allocation for an array of Objects - Is my understanding valid?

I have a question regarding memory allocation for Objects in an array. I am looking to create an array of Objects, but at compile time, I have no way of knowing how many objects I will need, and thus don't want to reserve more memory than needed.
What I would like to do is allocate the memory as needed. The way I would like to do this is when the user clicks an "Add" button, the array is increased by one additional object and the needed memory for the new object is allocated.
In my novice understanding of Objective C (I was a professional programmer about 20 years ago, and have only recently begun to write code again) I have come up with the following code segments:
First, I declared my object:
NSObject *myObject[1000]; // a maximum number of objects.
Then, when the user clicks an Add button it runs a method with the allocation code: (note: the variable i starts out at a value of 1 and is increased each time the Add button is clicked)
++i
myObject[i] = [[NSObject alloc] init];
Thus, I'm hoping to only allocate the memory for the objects actually needed, rather than all 1000 array objects immediately.
Am I interpreting this correctly? In other words, am I correct in my interpretation that the number of arrayed elements stated in the declaration is the MAXIMUM possible number of array elements, not how much memory is allocated at that moment? It this is correct, then theoretically, the declaration:
NSObject *myObject[10000];
Wouldn't pull any more memory than the declaration:
NSObject *myObject[5];
Can someone confirm that I'm understanding this process correctly, enlighten me if I've got this mixed up in my mind. :)
Thanks!
Why not use NSMutableArray? You can initWithCapacity or simply allocate with [NSMutableArray array]. It will grow and shrink as you add and remove objects. For example:
NSMutableArray *array = [NSMutableArray array];
NSObject *object = [[NSObject alloc] init];
[array addObject:object]; // array has one object
[array removeObjectAtIndex:0]; // array is back to 0 objects
// remember to relinquish ownership of your objects if you alloc them
// the NSMutable array was autoreleased but the NSObject was not
[object release];
Your understanding is mostly correct. When you do:
NSObject *myObject[1000];
You immediately allocate storage for 1000 pointers to NSObject instances. The NSObject instances themselves are not allocated until you do [[NSObject alloc] init].
However, doing NSObject *myObject[10000] will consume more space than doing NSObject *myObject[5], because 10,000 pointers certainly require more memory to represent than 5 pointers.
Remember that both things consume space, the pointer to the NSObject, and the NSObject instance itself, though in practice the space consumed by an NSObject instance will be significantly larger than the 4 bytes consumed by the pointer that refers to it.
Anyhow, perhaps more importantly, there is a better way to manage dynamic object allocation in Cocoa. Use the built-in NSMutableArray class. Like:
NSMutableArray* objects = [[NSMutableArray alloc] init];
[objects addObject: [[[NSObject alloc] init] autorelease]];

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.

When does -copy return a mutable object?

I read in Cocoa and Objective C: Up and Running that -copy will always return an immutable object and -mutableCopy will always return a mutable object:
It’s important to know that calling -copy on a mutable object returns an immutable
version. If you want to copy a mutable object and maintain mutability in the new version,
you must call -mutableCopy on the original. This is useful, though, because if you want
to “freeze” a mutable object, you can just call -copy on it.
So I have something like this:
NSMutableURLRequest *req = [[NSMutableURLRequest alloc] init];
NSLog( #"%#", [req className] ); // NSMutableURLRequest
NSLog( #"%#", [[req copy] className] ); // NSMutableURLRequest
NSLog( #"%#", [[req mutableCopy] className] ); // NSMutableURLRequest
According to this previous answer:
You cannot depend on the result of copy to be mutable! Copying an NSMutableArray may
return an NSMutableArray, since that's the original class, but copying any arbitrary
NSArray instance would not.
This seems to be somewhat isolated to NSURLRequest, since NSArray acts as intended:
NSArray *arr = [[NSMutableArray alloc] init];
NSLog( #"%#", [arr className] ); // __NSArrayM
NSLog( #"%#", [[arr copy] className] ); // __NSAraryI
NSLog( #"%#", [[array mutableCopy] className] ); // __NSArrayM
So...
When does -copy return an immutable object (as expected) and when does it return a mutable object?
How do I achieve the intended effect of getting a "frozen" copy of a mutable object that refuses to be "frozen"?
I think you've uncovered a great rift between documentation and reality.
The NSCopying protocol documentation claims:
The copy returned is immutable if the consideration “immutable vs. mutable” applies to the receiving object; otherwise the exact nature of the copy is determined by the class.
But this is clearly wrong in some cases, as you've shown in your examples (and I've sent feedback to them about this via that documentation page).
But(#2) in my opinion, it doesn't actually matter and you shouldn't care.
The point of -copy is that it will return an object you can use with the guarantee that it will behave independently of the original. This means if you have a mutable object, -copy it, and change the original object, the copy will not see the effect. (In some cases, I think this means that -copy can be optimized to do nothing, because if the object is immutable it can't be changed in the first place. I may be wrong about this. (I'm now wondering what the implications are for dictionary keys because of this, but that's a separate topic...))
As you've seen, in some cases the new object may actually be of a mutable class (even if the documentation tells us it won't). But as long as you don't rely on it being mutable (why would you?), it doesn't matter.
What should you do? Always treat the result of -copy as immutable, simple as that.
1) When does -copy return an immutable object (as expected) and when does it return a mutable object?
you should always treat it as the immutable variant. the mutable interface of the returned type should not be used. apart from optimizations, the answer should not matter and should be considered an implementation detail unless documented.
the obvious case: for a number of reasons, objc class clusters and class designs can be complex. returning a mutable copy could simply be for convenience.
2) How do I achieve the intended effect of getting a "frozen" copy of a mutable object that refuses to be "frozen"?
using the copy constructor of the immutable class is a good way (similar to St3fan's answer). like copy, it's not a guarantee.
the only reason i can think of as to why you would want to enforce this behaviour is for performance or to enforce a restricted interface (unless it's academic). if you want performance or a restricted interface, then you can simply encapsulate an instance of the type which copies on creation and exposes only the immutable interface. then you implement copy via retain (if that's your intent).
alternatively, you can write your own subclass and implement your own variant of copy.
final resort: many of the cocoa mutable/immutable classes are purely interface - you could write your own subclass if you need to ensure a particular behaviour -- but that's quite unusual.
perhaps a better description of why this should be enforced would be good - the existing implementations work just fine for the vast majority of developers/uses.
Bear in mind that there is not one copy implementation -- each class implements its own. And, as we all know, the implementation of the Objective C runtime is a little "loosey goosey" in spots. So I think we can say that mostly copy returns an immutable version, but some exceptions exist.
(BTW, what does this do:
NSArray *arr = [[NSMutable array] init];
?)
The best way to turn an object into an mutable one is to use the mutable 'constructor'. Like for example:
NSArray* array = ...;
NSMutableArray* mutableArray = [NSMutableArray arrayWithArray: array];
Copy is used to make a copy of an object. Not to change it's mutability.

Grow a NSMutableArray with NSNumber objects

Sorry for the newbie question, but I need a NSMutableArray with some NSNumber inside, created dynamically in a for cycle. My code looks like this:
for (...){
NSNumber *temp_number = [[NSNumber alloc] initWithInteger:someNSInteger];
[target_array addObject:[temp_number copy]];
[temp_number release];
}
Is this a correct way to do it? Does it leak?
Thanks! Miguel
Yep, that leaks. You want:
NSNumber *temp_number = [[NSNumber alloc] initWithInteger:someNSInteger];
[target_array addObject:temp_number];
[temp_number release];
So, no copy. The logic is that because you use alloc, you end up owning temp_number. You then add it to the array and the array does whatever it needs to. You've used temp_number for its intended purpose, so you no longer want to own it and release it.
If you were to take a copy, that would create another instance of NSNumber, which you also own, and therefore which you should also release when you're finished with.
In practice, the array (if it's allocated and exists, rather than being nil), will retain the object for itself, but that's an implementation detail specific to that class and not something you should depend upon or even be particularly interested in beyond the contract that says that the objects you add can later be found in the array.

Using malloc to allocate an array of NSStrings?

Since NSSstring is not defined in length like integer or double, do I run the risk of problems allocating an array of NSStrings for it using malloc?
thanks
ie:
NSString ***nssName;
nssName = (NSString***) malloc(iN * sizeof(NSString*));
the end result with for_loops for the rows is a 2D array, so it is a little easier to work then NSArray(less code).
No problems should arise, allocating an array of NSStrings is like making an array of the pointers to string objects. Pointers are a constant length. I would recommend just using NSArray but it is still fine to use a C array of NSStrings. Note that this may have changed with ARC.
Here is completely acceptable code demonstarting this:
NSString** array = malloc(sizeof(NSString*) * 10); // Array of 10 strings
array[0] = #"Hello World"; // Put on at index 0
NSLog(#"%#", array[0]); // Log string at index 0
Since NSString is an object (and to be more precise: an object cluster) you cannot know its final size in memory, only Objective-C does. So you need to use the Objective-C allocation methods (like [[NSString alloc] init]), you cannot use malloc.
The problem is further that NSString is an object cluster which means you do not get an instance of NSString but a subclass (that you might not even know and should not care about). For example, very often the real class is NSCFString but once you call some of the methods that treat the string like a path you get an instance of NSPathStore2 or whatever). Think of the NSString init methods as being factories (as in Factory Pattern).
After question edit:
What you really want is:
NSString **nssName;
nssName = (NSString**) malloc(iN * sizeof(NSString*));
And then something like:
nssName[0] = #"My string";
nssName[1] = [[NSString alloc] init];
...
This is perfectly fine since you have an array of pointers and the size of pointer is of course known.
But beware of memory management: first, you should make sure the array is filled with NULLs, e.g. with bzero or using calloc:
bzero(nssName, iN * sizeof(NSString*));
Then, before you free the array you need to release each string in the array (and make sure you do not store autoreleased strings; you will need to retain them first).
All in all, you have a lot more pitfalls here. You can go this route but using an NSArray will be easier to handle.
NSStrings can only be dealt with through pointers, so you'd just be making an array of pointers to NSString. Pointers have a defined length, so it's quite possible. However, an NSArray is usually the better option.
You should alloc/init... the NSString*s or use the class's factory methods. If you need an array of them, try NSArray*.
You should not use malloc to allocate data for Objective-C types. Doing this will allocate memory space but not much else. Most importantly the object will not be initialized, and almost as importantly the retain count for the object will not be set. This is just asking for problems. Is there any reason you do not want to use alloc and init?