Can the NSMultableArray mention which object inside the NSMultableArray? - objective-c

The NSMutableArray can store every NSObject, but can I mention the NSMutableArray can get store my item only, for example, a NSMutableArray that store NSString only?
I remember that the java array can do that, can the objective C array do the similar things? Thanks.

Objective-C does not have this kind of generic constraint on NSArray/NSMutableArray. You have therefore two solutions:
Subclass NSArray/NSMutableArray and check for element type. It is strongly discouraged as NSArray/NSMutableArray is a class "cluster" and not obvious to subclass.
Create a category with specific methods that check the right type. You will have a compile-time enforcement of the type.

You can try it like this -
NSMutableArray *arr = [[[NSMutableArray alloc] init] autorelease];
if([obj isKindOfClass:[NSString class]])
[arr addObject:obj];
This way you end up adding only NSString to your arr.

Not by default, no. NSArray and its mutable counterpart just store pointers which happen to point obj-c objects. These objects can of any type. It would be up to you to make sure that only NSString's get in your array.
You could potentially subclass NSArray and override the addObject: methods such that they throw an exception if you try to add a non-NSString object.

Related

How to check assignment since addObject doesn't access setter?

I just noticed that calling addObject: on an NSMutableArray doesn't access that array's setter.
E.g., for NSMutableArray self.myArray, [self.myArray addObject:object] does not use [self setMyArray:array] to add the object.
Previously I have been using custom setters and getter to check assignment before assigning; e.g., if I wanted an array that only accepted objects of class MyClass, I would do the following:
- (void)setMyArray:(NSMutableArray *)myArray
{
for (id object in myArray)
{
if (![object isKindOfClass:[MyClass class]]) return;
}
_myArray = myArray;
}
- (NSMutableArray *)myArray
{
if (!_myArray) _myArray = [[NSMutableArray alloc] init];
_myArray = myArray;
}
How do I go about achieving this same functionality when changing the array via addObject:, removeObject:, and other similar functions that may circumvent the setter?
Generally this kind of problem is the reason why NSMutableArray is usually avoided in preference of NSArray.
This is the simple solution, use NSArray instead of NSMutableArray:
self.myArray = [self.myArray arrayByAddingObject:foo];
However, if the array is really big that will cause performance issues. Then you've got two options:
you can have your own addObjectToMyArray: method in your class and always use that
you can create an NSArrayController and use that to access your array. It will implement key value observing and bindings and all of that stuff.
NSMutableArray is designed to perform addObject: with as few CPU instructions as possible and therefore does not proved any way for external code to be notified that the object was added. You have to have some other class wrapped around it.
Do not try to subclass NSMutableArray, because it is a "class cluster" making subclasses extremely complicated.
If what you wish to do is ensure objects in the array are of a particular class then this answer to the question "NSMutableArray - force the array to hold specific object type only" provides code to do exactly that.
If you wish to do other checks on assignment then you can use the code in that answer as a starting point.

NSMutableArray with a specific class of object to be contained

Is there any way to specify what specific type of object can be contained within an NSMutableArray?
EDIT More specifically... Is there a way to restrict the class that the object must belong to?
Well, you could always subclass NSMutableArray but as everyone else has said, it is hard to imagine a good reason to do this....
From Subclassing Notes in the docs you would basically have to over-ride the following functions and check for the proper class:
insertObject:atIndex:
addObject:
replaceObjectAtIndex:withObject:
the primitive methods of the NSArray class
You could check the class of the object before adding it to the array.
NSMutableArray *myArray = [NSMutableArray alloc] init];
if ([someObject isKindOfClass:[ClassYouWantInArray class]]){
[myArray addObject:someObject];
}

Is there any way to specify the class of objects of a NSMutableArray?

Im having the following problem:
I've made a NSMutableArray "array" that is going to contain objects of a class named "Class". At the start that array should be empty and it must be filled during the program's execution.
As I never actually told the compiler that my NSMutableArray will be holding elements of the class Class, when I try to write the appropriate methods the compiler wont let me do it.
This is my first experience on Objective-C and iPhone development. I used to code in C/C++ where I declared my arrays in the following way:
Class array[NUMBEROFELEMENTS];
Is there any way to do this in Objective-C?
Thanks!
The truth is that is doesn't matter to the NSMutableArray what type of object it is. NSMutableArray simply stores pointers to all the objects they contain, or reference.
The trick is when you pull the object back out of the array you need to create a new pointer based on the appropriate type:
MyObject *myObject = [myArray objectAtIndex:0];
Then you can use the object however you like:
[myObject doThatThingWithThisValue:10];
Or whatever you need.
Arrays in Objective-C Cocoa are objects (as well as other collections, sets, dictionaries). Arrays can contain references to objects of any type, so the type for the array is simply NSArray, NSMutableArray, etc...
Since they are objects, you can send them messages to manipulate their content.
I suggest you take a look at Apple's excellent Collections Programming Topics, which explain the rudiments of collections.
Here is a quick example :
// two objects of different types
NSNumber *n = [NSNumber numberWithInteger:10];
NSString *s = #"foo";
// alloc/init a new mutable array
NSMutableArray *a = [NSMutableArray arrayWithCapacity:10];
// add an object
[a addObject:n];
[a addObject:s];
// array a now contains a NSNumber and a NSString
Well, you can still have C-style arrays in Objective-C.
However, the characteristics of Objective-C (some people will call it strength, other will call it weakness) is that it has dynamic typing of objects and dynamic dispatch.
It has NSArray and NSMutableArray which are not specialized for the certain class. It can store objects of non-compatible classes.
You can use the following idiom: [obj isMemberOfClass: [Class type]] to make sure an array element is of the desired type and then cast to Class*.
You can also use for-each loop (aka Fast Enumeration):
NSMutableArray* array = //... initialize your array
for (Class* elm in array) {
elm.your_property = 10;
}

Static Variable in Cocoa Class Category

I have a cocoa 'category' for adding inflections (pluralize, singularize, etc.) to NSString. The code requires loading a set of regular expression rules and exceptions from a PLIST into dictionaries and arrays, as well as adding manual exceptions from code. I need a way to persist these data structures (as class members) between multiple calls to the inflection code (all instance methods). I attempted:
+ (NSMutableArray *)uncountables
{
static NSMutableArray *uncountables = nil;
if (uncountables == nil) uncountables = [NSMutableArray array];
return uncountables;
}
However, it appears to fail occasionally. Does a good way of doing this exist? I don't want to subclass NSString if possible. Thanks.
[NSMutableArray array];
returns an autoreleased array. Use this instead:
[[NSMutableArray alloc] init];
I think this code is OK. I use the same thing a lot for singletons. But be aware that it is not thread safe this way. Maybe you calling it from different threads?
As drawnonward already mentioned, [NSMutableArray array]; returns an autoreleased array. But I don't think, it's a good idea to return non-autoreleased array, because it contradicts with Cocoa memory management conceptions - only alloc, copy and new should be released manually. All other initializations are autoreleased.
So, you should just use
interface:
NSArray *a;
...somewhere in a code...
a = [[NSString uncountables] retain];
...
- (void)dealloc {
[a release];
}
to get properly retained/released objects.

How careful are you with your return types in Objective-C?

Say you have a method that returns a newly generated NSArray instance that is built internally with an NSMutableArray. Do you always do something like this:
- (NSArray *)someArray {
NSMutableArray *mutableArray = [[NSMutableArray new] autorelease];
// do stuff...
return [NSArray arrayWithArray:mutableArray]; // .. or [[mutableArray copy] autorelease]
}
Or do you just leave the mutable array object as-is and return it directly because NSMutableArray is a subclass of NSArray:
- (NSArray *)someArray {
NSMutableArray *mutableArray = [[NSMutableArray new] autorelease];
// do stuff...
return mutableArray;
}
Personally, I often turn a mutable array into an NSArray when I return from methods like this just because I feel like it's "safer" or more "correct" somehow. Although to be honest, I've never had a problem returning a mutable array that was cast to an NSArray, so it's probably a non-issue in reality - but is there a best practice for situations like this?
I used to do the return [NSArray arrayWithArray:someMutableArray], but I was slowly convinced that it doesn't offer any real benefit. If a caller of your API is treating a returned object as a subclass of the declared class, they're doing it wrong.
[NB: See bbum's caveat below.]
It's very common to return an NSMutableArray cast as an NSArray. I think most programmers would realize that if they downcast an immutable object and mutate it, then they're going to introduce nasty bugs.
Also, if you have an NSMutableArray ivar someMutableArray, and you return [NSArray arrayWithArray:someMutableArray] in a KVC accessor method, it can mess up KVO. You'll start getting "object was deallocated with observers still attached" errors.
NSArray is in fact a class cluster, not a type, anyway. So anywhere you see an NSArray, chances are it's already one of several different types anyway. Therefore the 'convert to NSArray' is somewhat misleading; an NSMutableArray already conforms to the NSArray interface and that's what most will deal with.
CocoaObjects fundamentals
In any case, given that you're returning an array (and not keeping it afterwards, thanks to the autorelease) you probably don't need to worry whether the array is mutable or not.
However, if you were keeping the array, then you might want to do this, to prevent the clients from changing the contents.