A method that takes both NSArray and NSMutableArray - objective-c

I want to create a method that can take an NSMutableArray or an NSArray object.
I think I can just use (id) as the parameter type... or convert the NSArray to NSMutableArray before passing. However I'm not really happy with these solutions.
In other languages I might pass an IList or some shared object that they both inherit from...
I need to enumerate through the array in my method.
What is the way people would typically do this in objective c?

Take NSArray. NSMutableArray derives from NSArray, so as long as you're only using the NSArray members (such as read-only enumeration), you'll be safe.
Relevant documentation (from Apple's developer site):
NSMutableArray class reference
NSArray class reference

Since NSMutableArray is a subclass of NSArray, just have your method take an NSArray parameter. That way, it will take anything that is or is a subclass of NSArray.

Use NSArray* -- NSMutableArray inherits from it and you can iterate both as if they were NSArrays.

I would stick with NSArray as the parameter because I prefer to use immutable objects wherever possible (looser coupling), and then use the -mutableCopy method when required to send them to an to a function that needs to modify the array.

Related

Is filteredArrayUsingPredicate: an instance method or a class method?

I'm confused about the method filteredArrayUsingPredicate:(NSPredicate *)predicate and how it works. It seems to be an NSArray method that creates a new array that contains all the objects that satisfy the predicate. I've looked under the class reference for NSArray and it looks to be an instance method.
But if it results in the creation of a new array, why isn't it classified as a class method?
This has thrown my understanding of class and instance methods out the window!
I've looked under the class reference for NSArray and it looks to be an instance method.
That is absolutely right.
But if it results in the creation of a new array, why isn't it classified as a class method?
Because it needs an instance of NSArray to start with. Otherwise, you would end up with a signature like this:
[NSArray filteredArray:someArray usingPredicate:somePredicate];
Although this signature would be OK too, the current one is perfectly fine. In fact, it is idiomatic for immutable types in Cocoa to return new objects from instance methods. NSArray and NSString have many such methods.
Mutable classes, on the other hand, often support similar methods that change the instance itself. For example, the counterpart of filteredArrayUsingPredicate: method of NSArray would be filterUsingPredicate: method of NSMutableArray.

XPC method not invoked with NSArray of NSValues as parameter

I have an XPC service compiled 32-bit for OS-X. One of it's methods takes a single argument as a parameter, and calls a given block back on the main process.
This block takes 3 NSStrings and an NSArray that's supposed to contain NSValues solely. Each NSValue object holds an NSRect.
For some reason, the given block is not being called when the NSArray is not nil.
If instead of holding NSValues I let the array hold NSStrings, the block is being called correctly.
I'm not sure how to approach this, as there is no apparent reason for this to happen. According to the apple documentation, NSValue does conform to NSSecureCoding.
I even tried whitelisting the NSValue class (this shouldn't really be needed) using:
NSSet *readerSelectionClasses =
[NSSet setWithObjects:[NSArray class], [NSValue class], nil];
[newConnection.exportedInterface setClasses:readerSelectionClasses forSelector:#selector(MyMethod:WithReply:) argumentIndex:3 ofReply:YES];
This doesn't seem change anything.
Any idea how to pass the NSArray? I guess I could format all NSRects to NSStrings, and pass an array of NSStrings, which I know works, but this would lead to a performance decrease I would rather avoid.

What if assigning NSMutableArray to NSArray without using 'copy'?

I'm new into Obj-C.
I would like to know will there be any problems if I assigning NSMutableArray to NSArray as like example code below:
NSMutableArray *mutableArray = [NSMutableArray arrayWithObjects:#"item1", #"item2", nil];
NSArray *array = mutableArray;
I know that if i use [mutableArray copy] it will duplicate the array as new set. My question is will my approach conflict with any guidance or will have any problems?
Thanks.
Both pointers will refer to the same object. The nature and behavior of that object will remain the same. It is not aware nor affected by which pointers refer to it.
If the array is mutated, those mutations will be observable regardless of which pointer you use to message or examine the array.
The compiler will complain if methods present in NSMutableArray but not NSArray are sent via the array pointer. However, if you ignore the compiler's complaints, the methods will still work as normal. The static types of the pointers is only significant at compile time. They are not represented in the compiled program (except, possibly, in debugging information, but that doesn't affect runtime behavior).
The catch is this:
if mutableArray changed somehow, and the array still think of it self immutable.
Then, things might get out of control.
Other than this, I don't see much problem here...
Your array will stay mutable, but compiler will think that it is immutable. So it won't allow you to use methods specific for NSMutableArray without typecasting.

Is there any way to monkey-patch or swizzle an NSArray (or other Class Cluster)?

Today I was working on a project in which I wanted to "alias" an alternative method for all instances of NSArray, and didn't think it would be too difficult with some good old-fashioned method swizzling.
I broke out JRSwizzle and…
[NSArray jr_swizzleMethod:#selector(objectAtIndex:) withMethod:#selector(objectAtIndex_accordingToMe:) error:nil];
To be clear, I paired this with the appropriate category on NSArray, an instance method called objectAtIndex_accordingToMe:.
However, I was just getting that same old object, at that same old index. Sigh. Ultimately, I figured out that despite not throwing any errors - I'm not going to achieve these results due to the fact that NSArray is a class cluster
I guess my question is more of an unwillingness to accept that "this" is really the end of the road trying to override NSArray methods. I mean, come on this is NSArray.. people must wanna muck around with it, no? One would think that Apple's foundation classes would be a prime target for swizzlers, everywhere!
So, is there a way to alter, alias, monkey-patch, override, or otherwise have your way with… an NSArray, etc. (without subclassing)?
It's not just that it's a class cluster. NSArray is toll-free bridged to CFArray, and you can't swizzle Core Foundation. So this is very unlikely to work in general.
But what are you trying to solve? If you want to add a new method, use a category. They work on class clusters just fine. Modifying the behavior of some built-in on NSArray seems a recipe for disaster (entertaining as it might be as an exercise).
Before going too far, you probably want to at least take a look at CFArray.c and understand how some of the underlying stuff is implemented.
EDIT: While I would never do this in production code, you may get some of what you want by hijacking individual array instances with ISA-swizzling. See ISASwizzle for some example code. The code explanation is in Chapter 20 of iOS:PTL. Search out for "isa swizzle" and you should find more on the net. It's how KVO is implemented. But with NSArray... wow, that's gotta be fragile.
Presumably you have a particular array for which you'd like this behavior. You can get that instance's class object, no matter what it is, and swizzle that quite easily:
[[myArray class] jr_swizzleMethod:#selector(objectAtIndex:) withMethod:#selector(objectAtIndex_accordingToMe:) error:nil];
There's also only a few concrete subclasses of NSArray:
NSArray * trees = [NSArray array];
NSArray * birds = [NSArray arrayWithObjects:#"Albatross", #"Budgerigar", #"Cardinal", nil];
NSMutableArray * dogs = [NSMutableArray arrayWithObjects:#"Airedale", #"Beagle", #"Collie", nil];
NSLog(#"%# %# %#", [trees class], [birds class], [dogs class]);
We get __NSArrayI for the first two and __NSArrayM for the third, so potentially (this is very fragile) you could use a runtime function to grab the class object by name:
[objc_getClass("__NSArrayI") jr_swizzleMethod:#selector(objectAtIndex:) withMethod:#selector(objectAtIndex_accordingToMe:) error:nil];

Objective C - Subclassing NSArray

I am trying to subclass NSArray, but it crashes the app when trying to access the count method. I know that NSArray is a class cluster.
But what does this mean?
Is there a work around to be able to subclass an NSArray?
I know that I can simply subclass NSObject and have my array as an instance variable but I would rather subclass NSArray.
EDIT:
Reason:
I am creating a card game, I have a class Deck which should subclass NSMutableArray to have a couple of extra methods (-shuffle, -removeObjects:, -renew, etc), and I think it will look cleaner to subclass NSArray rather than having a var.
The problem with adding a category on a class like this is that all instances of the class will inherit the additional methods. That's both unnecessary (since not every array needs to be able to be shuffled, etc.) and dangerous (because you can't benefit from typechecking to be sure the NSArray you're currently referring to is really one that was expected to be shuffled).
An alternative would be to create your own Deck class that has an NSMutableArray as an instance variable. There you can define actions on your deck exactly as you would like, and the fact that you are using an NSMutableArray becomes an implementation detail. This lets you take advantage of typechecking at compile-time and it lets you change the internal implementation of your Deck class without changing its clients. For instance, if you decided for some reason that an NSMutableDictionary would be a better backing store, you can make all those changes within the implementation of your Deck class without changing any of the code that creates and uses the Deck.
You usually won't need to subclass it, but in any case the suggestions made by Apple are:
Any subclass of NSArray must override the primitive instance methods count and objectAtIndex:. These methods must operate on the backing store that you provide for the elements of the collection. For this backing store you can use a static array, a standard NSArray object, or some other data type or mechanism. You may also choose to override, partially or fully, any other NSArray method for which you want to provide an alternative implementation.
Did you actually override countmethod? As they say you have to provide your own backing structure to hold array elements, and override suggested methods considering this..
If you're just adding new methods, and using the existing backing store, then a better approach is to add a category to NSArray. Categories are a really powerful part of objective-C - see cocoadev for some samples.
NSMutableArray already has a - (void)removeObjectsInArray:(NSArray *)otherArray;
You're going to be best off making an NSObject subclass with a mutable array property.
In this particular case, I'd shuffle the array using -sortedArrayUsingComparator: and make your comparator randomly return NSOrderedAscending or NSOrderedDescending.
E.G:
NSArray *originalArray; // wherever you might get this.
NSArray *shuffledArray = [orginalArray sortedArrayUsingComparator:
^(id obj1, id obj2) {
return random() % 2 ? NSOrderedAscending : NSOrderedDescending;
}];