What is the actual use of categories instead of inheritance? - objective-c

I'm trying to understand the actual use of categories as opposed to inheritance in Objective-C. When should I prefer to use a category? A real-life example with code would be helpful.

When you need to add functionality to an existing class.
For example, your app is working with NSDate or NSString instances and you want to add some functionality. You cannot add the functionality to a subclass because you can't force system methods to return the subclass.
Just look into the examples in the API:
1/ NSDictionary is used to hold file attributes and has a method that returns file size.
2/ NSString has a UI category that extends it with drawing. Note that you don't want a separate subclass of string that can be drawn. You want all strings to have the ability to be drawn.

I used categories when I need to add some convenient functions that I will use repeatedly to the existing class without having a need to subclass to overwrite some existing functions of that class.
For example, when I want to check for an empty string, or remove all leading and trailing spaces of a string:
.h file:
#interface NSString (Extension)
-(BOOL)isEmptyString;
-(NSString *)trimLeadingAndTrailingWhiteSpaces;
#end
.m file:
#implementation NSString (Extension)
-(BOOL)isEmptyString
{
NSString *myString = [self stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
if (myString.length == 0)
return TRUE;
else
return FALSE;
}
-(NSString *)trimLeadingAndTrailingWhiteSpaces
{
NSString *myString = [self stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
return myString;
}
#end
To use it:
someString = [someString trimLeadingAndTrailingWhiteSpaces];
if ([someString isEmptyString])
{
//someString is empty, do whatever!
}

I see categories as a sort of easier way of achieving some of the functionality provided by inheritance. There are other things categories do which are not provided by inheritance. Categories allow you to extend the functionality of a existing class without subclassing them. You can also use them to replace existing methods in classes. A category method is sort of bolted onto the existing class unlike a subclass which direct descendant of the original class. Once category method is added to a objective c class it is available to all instances of the class including the ones not created by you.
For example , if you have to encryption function which you need to use on all data in your project and say most your data uses only NString for saving and manipulation. One of the ways you can go about it is to create a category for NSString
NSString-Encryption.h
#interface NSString (Encryption)
-(NSString*) encrypt;
#end
NSString-Encryption.m
#import "NSString-Encryption.h"
#implementation NSString (Encryption)
-(NSString*) encrypt
{
// your encryption method here
return encryptedString;
}
#end
UseNSString-encryption.m
NSString *testString = #"this is test";
NSString *encryptedString = [testString encrypt];
As you can see that the category is easier to use than subclass. This method can be called from NSMutableString also as it inherits from NSString. So it is powerful.
You can also use category to replace existing methods on classes.
Another use of a category is that it can be used for private methods as objective c does not have a formal private designation for methods. This done by putting a category in a .m file instead of .h file.
Phone.h
#interface Phone:NSObject
-(void) call;
#end
Phone.m
#interface Phone(Private)
-(void) validatePhoneEntry:(NSString*) phoneNumber;
#end
#implementation Phone
-(void) validatePhoneEntry:(NSString*) phoneNumber
{
}
-(void) call
{
}
#end
The disadvantage of categories is that you cant use them if you need to add member variable to classes.

Sulthan explains it well. Here is a code example of extending the functionality of NSString by adding a category. You can use categories this way to extend classes that you don't have the implementations for without subclassing them.

If you want to add methods to a class, use a category. If you want to change functionality of existing methods in a class, create a subclass.
It's kinda/sorta possible to use a category to replace an existing method, sort of like overriding in a subclass, but you shouldn't use categories for this. There are three reasons: 1) you lose access to the original method; 2) you won't break existing code; 3) if more than one category attempts to "override" the same method, the method that the class will end up with is undefined. Basically, the class will end up with the method from the last category applied to the class, but the order in which categories are applied can't be relied on. It's a recipe for a mess, so just don't do it.
So, you could use a category to add methods to NSData such as +(NSData*)dataWithCryptographicallyRandomBytes:(NSUInteger)length or -(void)base64Decode. But if you wanted to change the behavior of an existing method, like -writeToFile:atomically:, you should create a subclass of NSData and implement your new functionality there. For one thing, you might still want to use the existing behavior in your override, and subclassing lets you do that by calling [super writeToFile:file atmoically:YES]. Also, using a subclass here means that your new behavior will only affect your own code -- it won't affect uses of NSData elsewhere in the framework that may not expect your new functionality.

Categories are similar to Java interfaces (in java the implementation is not optional) - a way to group methods and make objects respond to a particular API - independent of the class type. Inheritance makes sense if you need to add additional ivars and the new class matches a isa relation (student isa person). Adding a few utility methods does not make an isa case - this is usualy done with protocols. Categories are often used with delegates where the methods in question are optional.

Related

extend class in objective-c with variable based property [duplicate]

This question already has answers here:
Objective-C: Property / instance variable in category
(6 answers)
Closed 2 years ago.
I've got a form implementation in objective-c and I'd like to extend my widgets (NSButton, NSTextField, etc..) to contain additional string representing their unique identifier string to be used after submit event occur, which trigger generation of json contain all widget id/value pairs.
I've tried using categories to extend NSControl which is the common parent of all those widgets in the following way.
NSControl+formItemSupport.h
-------------------------------
#interface NSControl (formItemSupport)
#property NSString * formItemId;
#end
NSControl+formItemSupport.m
-------------------------------
#implementation NSControl (formItemSupport)
-(NSString *)formItemId {
return self.formItemId;
}
-(void)setFormItemId:(NSString *)formItemId {
self.formItemId = formItemId;
}
in the form.m file I import from NSControl+formItemSupport.m but when I try to set this field in NSButton : NSControl object. However, when I try to set the property formItemId, I get into infinite loop. Perhaps there's another way for extending objc class with variable based property without using inheritance ?
you can
#synthesize formItemId = _formItemId;
//synthesize needs local declaration of _formItemId;
#implementation ExtraWurst {
NSString *_formItemId;
}
but this is done behind the scene for you from Xcode without #synthesize.
Sometime it is still easier to define the use of an internal variable for a property in this way.
apart from that you can and have to change your setter and getter methods in the following way.
-(NSString *)formItemId {
return _formItemId;
}
-(void)setFormItemId:(NSString *)formItemId {
_formItemId = formItemId;
}
this will prevent you from ending up in a loop.
Why?
Because self.formItemId = refers to -(void)setFormItemId:
So you would call the setter inside the setter that will set with the same again and again aka an endless loop.
You can take care of the getter the same way as shown above.
Where to use self.yourProperty then?
You can use self.formItemId anywhere in the class but not inside getter and setter of formItemId.
Correctly mentioned, Instance variables may not be placed in categories.
Meaning if you need such you have to subclass UIControl but that breaks the inheritance of your used UIControls. You would have to subclass all your SpecialUIControls you are using later.
Another solution, you could define a constant in your implementation and go with objective-C runtime functions and associate this constant yourself. Beware because you transform the ObjectModel for all UIControl classes then..
#import "NSControl+formItemSupport.h"
#import <objc/runtime.h>
#implementation UIControl (formItemSupport)
NSString const *key = #"formItemSupport.forItemKey";
-(void)setFormItemId:(NSString *)formItemId {
objc_setAssociatedObject(self, &key, formItemId, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(NSString *)formItemId {
return objc_getAssociatedObject(self, &key);
}
#end
still, its much easier and safer and flexible to subclass your own UIControl instead to extent all subclasses inherited from UIControl.
Why is subclassing easier here?
As you mentioned you want to json later on with the given formItemId per Control you can make use of an archiver / unarchiver design pattern of your subclasses which are nice to jsonify later.

How to allocate an NSObject subclass instance FROM an instance of its superclass?

Given a class structure such as...
#interface SuperClassView : NSView #end
#interface SubClassedView : SuperClassView #property int aProp; #end
How can one instantiate a SubClassedView from an instance of a SuperClassView?
Say a method returns an instance of the superclass SuperView....
SuperClassView *superInstance = [ViewFactory makeSuperClassView];
but I want to get an instance of the subclass SubClassedView? It is not possible to simply "cast" it...
SubClassedView *subClsInstance = (SubClassedView*)[ViewFactory makeSuperClassView];
and there is no built-in (or easily-imagined implementation of an) NSObject method like
self = [super initWithInstance:[superInstance copy]];`
Is the only way to either copy the superclass instance's desired properties to the newly instantiated subclass object, like...
SubClassedView *subClsInstance = SubClassedView.new;
for (NSString* someKey in #["frame",#"color",#"someOtherProperty])
[subClsInstance setValue:[superInstance valueForKey:someKey] forKey:someKey];
Or add (swizzle at runtime) the subclass' "additional property methods" (in this case setAProp: and aProp) to the superclass instance (and also cast it upwards)...
SubClassedView *subClsInstance = (SubClassedView*)[ViewFactory makeSuperClassView];
[subClsInstance addSwizzleMethod:#selector(setAProp:) viaSomeMagic:....];
[subClsInstance addSwizzleMethod:#selector(aProp) viaSomeMagic:....];
Hopefully this is an easy runtime trick that I simply don't know... not a sad sign that I am haplessly trying to trick ObjC into multiple-inheritance via some embarrassing anti-pattern. Either way, ideas?
EDIT: Pending #BryanChen posting his comment as an answer this is achieved easily via his suggested runtime function, or as a category on NSObject á la..
#implementation NSObject (SettingClass)
- (void)setClass:(Class)kls { if (kls) object_setClass(self, kls); } #end
What you are trying to do is pretty non-idiomatic... it feels like you are trying to do something like prototype based OOP. A couple of quick points:
Don't do the swizzle. You can't swizzle onto an instance, you swizzle onto the class definition, so if you do that you won't be adding the subclasses methods onto "an" instance of the superclass, you will be adding them onto all instances of the superclass.
Yes, if you want to do this you just need to copy the the properties you want from the super into the new instance of the subclass.
You can have a factory method in the superclass that returns a subclass, and encapsulate all the the copying in there (so, -[SuperClassView makeSubclassView] that returns SubClassedView *. That is actually relatively common, and is how many of the class clusters are implemented (though they return private subclasses that conform to the implementation of the superclass)
object_setClass is not the droid you're looking for.
Yes, it will change the class of the instance. However, it will not change the size of it. So if your SubClassView declares extra properties or instance variables that are not declared on SuperClassView, then your attempts to access them on this frankenstein instance will result in (at best) buffer overflows, (probably) corrupted data, and (at worst) your app crashing.
It sounds like you really just want to use self in your factory method:
+ (instancetype)makeView {
return [[self alloc] init];
}
Then if you call [SuperClassView makeView], you get back an instance of SuperClassView. If you call [SubClassView makeView], you get back an instance of SubClassView.
"But," you say, "how do I customize the properties of the view if it's a SubClassView?"
Just like you would with anything else: you override the method on SubClassView:
#implementation SubClassView
+ (instancetype)makeView {
SubClassView *v = [super makeView];
v.answer = 42;
return v;
}
#end
object_setClass may or may not be the "runtime trick" you are looking for. It does isa swizzle which change the class of an instance at runtime. However it does have many constrains such as that the new class cannot have extra ivars. You can check this question for more details.
I think the better way to do is that instead of making view using [ViewFactory makeSuperClassView], make it [[SuperClassView alloc] initWithSomething]. Then you can do [[SubClassView alloc] initWithSomething]
or if you really want use ViewFactory, then make it [ViewFactory makeViewOfClass:]

Does Objective-C have an equivalent to java annotations?

Does Objective-C have an equivalent to java annotations?
What's I'm trying to do is create a property and be able to somehow access some metadata about it.
I want to be able to determine what type of classes should go in my array so I'd like to annotate it somehow to say so. Then later be able to access that annotation via something like the runtime library where I can access lists of properties and their names.
//Put some sort of annotation giving a class name.
#property (strong) NSArray *myArray;
You said:
I want to be able to determine what type of classes should go in my array so I'd like to annotate it somehow to say so. Then later be able to access that annotation via something like the runtime library where I can access lists of properties and their names.
There are a few ways to do this sort of thing in Objective-C. Apple's frameworks do this sort of thing by adding a class method that returns the required information. Examples: dependent keys in KVO, +[CALayer needsDisplayForKey:] and related methods.
So, let's create a class method that returns an array of classes that can go into your container property, given the property name. First, we'll add a category to NSObject to implement a generic version of the method:
#interface NSObject (allowedClassesForContainerProperty)
+ (NSArray *)allowedClassesForContainerPropertyWithName:(NSString *)name;
#end
#implementation NSObject (allowedClassesForContainerProperty)
+ (NSArray *)allowedClassesForContainerPropertyWithName:(NSString *)name {
if (class_getProperty(self, name.UTF8String)) {
return #[ [NSObject class] ];
} else {
[NSException raise:NSInvalidArgumentException
format:#"%s called for non-existent property %#", __func__, name];
abort();
}
}
#end
As you can see, this default version of the method doesn't do anything particularly useful. But adding it to NSObject means we can send the message to any class without worrying about whether that class implements the method.
To make the message return something useful, we override it in our own classes. For example:
#implementation MyViewController
+ (NSArray *)allowedClassesForContainerPropertyWithName:(NSString *)name {
if ([name isEqualToString:#"myArray"]) {
return #[ [UIButton class], [UIImageView class] ];
} else {
return [super allowedClassesForContainerPropertyWithName:name];
}
}
...
We can use it like this:
SomeViewController *vc = ...;
SomeObject *object = ...;
if ([[vc.class allowedClassesForContainerPropertyWithName:#"bucket"] containsObject:object.class]) {
[vc.bucket addObject:object];
} else {
// oops, not supposed to put object in vc.bucket
}
There is no native support of this functionality, but you may to take a look at following solution — https://github.com/epam/lib-obj-c-attr/ It is compile time implementation of attributes. Definition of attributes based on defines but not on comments as in other solutions like ObjectiveCAnnotate.
Objective C does not support generics like in Java but ofcourse the language is very flexible that you can accomplish almost anything with simple tricks and knowledge. To implement a generic like feature you could create a category on NSArray class and create your own method to initialize the array and then check to see if the object is really the type of the object you want.
I would write a simple category on NSArray to have such functionality. Say suppose, I want my array to hold objects of class MyClass only then my category would look like,
#interface NSArray(MyCategory)
#end
#implementation NSArray(MyCategory)
-(NSArray*)arrayWithMyClasses:(NSArray*)classes{
if([classes count] > 0){
NSMutableArray *array = [[NSMutableArray alloc] init];
for(id anObj in classes){
NSAssert([anObj isKindOfClass:[MyClass class]], #"My array supports only objetcts of type MyClass");
[array addObject:anObj];
}
return array;
}
return nil;
}
#end
Of course, there is some limitations to it. Since you have created your own category, you should use your own method to initialize and create your own array.
No, Objective-C has no annotation or generics support.
A way to implement such a thing would be to hack Clang to read comments and associate a metadata object to the original object. But, you would be tied to your hacked compiler.
NSString *v1 = [[NSString alloc] init];
// associate
static char key;
NSString *v2 = [[NSString alloc] init];
objc_setAssociatedObject (
v1,
&key,
v2,
OBJC_ASSOCIATION_RETAIN
);
// retrieve
NSString *associate = (NSString *)objc_getAssociatedObject(v1, &key);
Qualifying with a protocol wouldn't be much trouble, and you could test if the collection implements it, but along the way you would need to create a category for each type on the same collection. This would require a different collection at compile time using macros. Overly complicated.
#interface Tomato:NSObject #end
#implementation Tomato #end
#protocol TomatoNSArray <NSObject>
- (Tomato*)objectAtIndexedSubscript:(NSUInteger)index;
- (void)setObject:(Tomato*)tomato atIndexedSubscript:(NSUInteger)index;
#end
// here is the problem, you would need to create one of this for each type
#interface NSMutableArray (TomatoNSArray) <TomatoNSArray>
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
NSMutableArray<TomatoNSArray> *tomatoes = [[NSMutableArray alloc] initWithCapacity:2];
tomatoes[0] = [Tomato new];
tomatoes[1] = [NSObject new]; // warning: incompatible pointer types
}
}
Does Objective-C have an equivalent to java annotations?
Not exactly an equivalent, but there is, and it's better. In Objective-C, the compiler has to store some type and name information in the compiled code (because the language is highly dynamic, a lot of things happen at runtime as opposed to compile time), for example method names ("selectors"), method type signatures, data about properties, protocols, etc. The Objective-C runtime library then has access to this data. For example, you can get the list of properties an object has by writing
id object = // obtain an object somehow
unsigned count;
objc_property_t *props = class_copyPropertyList([object class], &count);
Or you can check what class an object belongs to:
if ([object isKindOfClass:[NSArray class]]) {
// do stuff
}
(Yes, part of the runtime library is itself wrapped into some methods of NSObject for convenience, others only have C function APIs.)
If you specifically want to store custom metadata about an object or a class, you can do that using associated references.
I expect it should be clear now, the answer is NO, not at the moment.
Some people found some alternatives which seem to work in their specific use cases.
But in general there is no comparable feature yet in objective-c. IMHO clang metadata seems to provide a good foundations for this, but as long as there is not support from Apple this will not help, as far as i understood it.
Btw. I guess it should be clear, but just to repeat for all: two changes are required to support annotations as provided in java.
The language need an extension the annotate e.g. methodes, properites, classes, ... in the source code.
A standard interface is required to access the annotated information. This can only provide by apple.
Most alternativ soltuions move the annotation information into runtime and define their own interface. The objective-c runtime provide a standard interface but only with some trick you can annotate properties and still the isse of runtime population.
The typical use case for suche a feature is an IOC container (in Java e.g. Spring) which use the annotated information to inject other objects.
I would suggest to open an feature requrest for Apple to support this.
The answer to your question is that Objective-C does not have a direct equivalent of annotations as found in Java/C#, and though as some have suggested you might be able to engineer something along the same lines it probably is either far too much work or won't pass muster.
To address your particular need see this answer which shows how to construct an array which holds objects of only one type; enforcement is dynamic and not static as with parametric types/generics, but that is what you'd be getting with your annotation so it probably matches your particular need in this case. HTH.
What you need maybe a metadata parser for Objective-C. I have used ObjectiveCAnnotate (compile time retrievable) and ROAnnotation(runtime retrievable).

Class Name with a "+"

I am working on an iOS project in Xcode and I see some classes that have names with a "+"; for example:
TableViewController+TableView.h and then the class is named: #interface RKTableViewController (TableView) as opposed to RKTableViewController+TableView.
What is this + and the (TableView)? If its subclassing UITableView shouldn't the class be declared as: Subclassed name : Parent class name format?
The + in the filename isn't semantically important. Naming a file "ClassName+CategoryName.h/m" is just a popular convention for naming files containing categories.
#interface RKTableViewController (TableView)
#end
declares a category called "TableView" on the RKTableViewController class. Categories are used to add methods to a class outside its main implementation. See the Apple documentation on categories here: http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/chapters/occategories.html
These are categories. The are very helpful at times.
You can add methods to a class by declaring them in an interface file
under a category name and defining them in an implementation file
under the same name. The category name indicates that the methods are
additions to a class declared elsewhere, not a new class. You cannot,
however, use a category to add additional instance variables to a
class.
The methods the category adds become part of the class type. For
example, methods added to the NSArray class in a category are included
as methods the compiler expects an NSArray instance to have in its
repertoire. Methods added to the NSArray class in a subclass, however,
are not included in the NSArray type. (This matters only for
statically typed objects because static typing is the only way the
compiler can know an object’s class.)
Category methods can do anything that methods defined in the class
proper can do. At runtime, there’s no difference. The methods the
category adds to the class are inherited by all the class’s
subclasses, just like other methods.
http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/chapters/occategories.html
Example:
Here is an example of a category I use all the time. I don't own NSMutableArray but I would love for there to be a simple move function. Instead of subclassing just to add a simple function I attach a category.
// NSMutableArray+Move.h
#interface NSMutableArray (move)
- (void)moveObjectFromIndex:(NSUInteger)from toIndex:(NSUInteger)to;
#end
// NSMutableArray+Move.m
#implementation NSMutableArray (move)
- (void)moveObjectFromIndex:(NSUInteger)from toIndex:(NSUInteger)to
{
if (to != from) {
id obj = [self objectAtIndex:from];
[self removeObjectAtIndex:from];
if (to >= [self count]) {
[self addObject:obj];
} else {
[self insertObject:obj atIndex:to];
}
}
}
This allows me to do new things with a class thats already been created all over my app. So anywhere I use an NSMutableArray I can call my added method like so
NSMutableArray *myArray = [NSMutableArray arrayWithObjects:#"Object A", #"Object B", #"Object C", nil];
[myArray moveObjectFromIndex:0 toIndex:2];

Should I subclass the NSMutableArray class

I have an NSMutableArray object that I want to add custom methods to. I tried subclassing NSMutableArray but then I get an error saying "method only defined for abstract class" when trying to get the number of objects with the count method. Why is the count method not inherited?
I read somewhere else that I will have to import some NSMutableArray methods into my custom class if I want to use them. I just want to add a custom method to the NSMutableArray class. So should I subclass NSMutableArray, or should I do something else?
NSMutableArray is not a concrete class, it is just the abstract superclass of a class cluster. The documentation for NSMutableArray does have information about how to subclass, but also strongly advises you not to! Only subclass if you have a special need for actual storage.
A class cluster means that the actual class will be chosen at run-time. An array created empty, may not use the same class as an array created with 1000 items. The run-time can do smart choices of what implementation to use for you. In practice NSMutableArray will be a bridged CFArray. Nothing you need to worry about, but you might see it if you inspect the type of your arrays in the debugger, you will never see NSArray, but quite often NSCFArray.
As mentioned before, subclassing is not the same as extending a class. Objective-C has the concept of categories. A category is similar to what other programming languages call mix-ins.
If you for example want a convenience method on NSMutableArray to sort all members on a property, then define the category interface in a .h file as such:
#interface NSMutableArray (CWFirstnameSort)
-(void)sortObjectsByProperty:(NSString*)propertyName;
#end
And the implementation would be:
#implementation NSMutableArray (CWFirstnameSort)
-(void)sortObjectsByProperty:(NSString*)propertyName;
{
NSSortDescriptor* sortDesc = [NSSortDescriptor sortDescriptorWithKey:propertName ascending:YES];
[self sortUsingDescriptors:[NSArray arrayWithObject:sortDesc]];
}
#end
Then use it simply as:
[people sortObjectsByProperty:#"firstName"];
If you're just adding a custom method, use a category on NSMutableArray. It's a class cluster, so the implementation is provided by undocumented subclasses. You need to provide a few methods to generate your own subclass. However, if you just add a category then your custom method will work on all NSMutableArrays in your app.
For comparison, here's an example I wrote a while back of implementing a custom NSMutableArray subclass.
Objective-C has a mechanism for adding methods to existing classes called Categories. That way you don't have to create your own subclass.
This is an old post, but thought I'd add my experience. #PayloW's answer is a good answer and I think answers your question perfectly, however, no one really answered your question the other way around, so I'll do that here.
Should you subclass NSMutableArray (or NSArray)? Depends on what you want to achieve. If you only want to add a method to extend an array's BASIC functionality, like sorting, then #PayloW's answer Categories are the way. However, if you want to create a custom class that behaves like an array then yes, subclassing NSMutableArray is quite easy. But because it's a Class Cluster it doesn't exactly subclass as you'd expect. Normally in subclassing the methods available in the Super Class are available to your subclass or you may override them. With Class Clusters you MUST instead include the Super's methods that you're going to use and provide a _backend instance of the super class to wrap those methods around.
Below is an example of how you'd subclass NSMutableArray (or any Class Cluster):
The interface:
#interface MyCustomArrayClass : NSMutableArray {
// Backend instance your class will be using
NSMutableArray *_backendArray;
}
// *** YOUR CUSTOM METHODS HERE (no need to put the Super's methods here) ***
-(bool)isEmpty;
-(id)nameAtIndex:(int)index;
-(int)rowAtIndex:(int)index;
-(int)columnAtIndex:(int)index;
#end
The implementation:
#implementation MyCustomArrayClass
-(instancetype)init {
if (self = [super init]) {
_backendArray = [#[] mutableCopy];
}
return self;
}
// *** Super's Required Methods (because you're going to use them) ***
-(void)addObject:(id)anObject {
[_backendArray addObject:anObject];
}
-(void)insertObject:(id)anObject atIndex:(NSUInteger)index {
[_backendArray insertObject:anObject atIndex:index];
}
-(void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject {
[_backendArray replaceObjectAtIndex:index withObject:anObject];
}
-(id)objectAtIndex:(NSUInteger)index {
return [_backendArray objectAtIndex:index];
}
-(NSUInteger)count {
return _backendArray.count;
}
-(void)removeObject:(id)anObject {
[_backendArray removeObject:anObject];
}
-(void)removeLastObject {
[_backendArray removeLastObject];
}
-(void)removeAllObjects {
[_backendArray removeAllObjects];
}
-(void)removeObjectAtIndex:(NSUInteger)index {
[_backendArray removeObjectAtIndex:index];
}
// *** YOUR CUSTOM METHODS ***
-(bool)isEmpty {
return _backendArray.count == 0;
}
-(id)nameAtIndex:(int)index {
return ((MyObject *)_backendArray[index]).name;
}
-(int)rowAtIndex:(int)index {
return ((MyObject *)_backendArray[index]).row;
}
-(int)columnAtIndex:(int)index {
return ((MyObject *)_backendArray[index]).column;
}
#end
Then to use like so:
MyCustomArrayClass *customArray = [[MyCustomArrayClass alloc] init];
// Your custom method
int row = [customArray rowAtIndex:10];
// NSMutableArray method
[customArray removeLastObject];
// Your custom class used just like an array !!!
index = 20;
MyObject *obj = customArray[index];
It all works very nicely, is clean and actually pretty cool to implement and use.
Hope it helps.
I have to agree with both node ninja and PeyloW because technically they have both right. Actually, that does not help me much.
Preamble:
There are many arrays in code that all to one contain only one but different type of data e.g. classA, classB, classC.
Problem:
I can easily mix arrays by passing wrong one to e.g. some selector because they are all NSMutableArray. There is no static check, only runtime one.
Solution - 1st try:
Make subclass of NSMutableArray so compiler makes static check and warns about wrong data type.
That is good because compiler warns you even when you pass wrong type to -addObject or -objectAtIndex when you overload that ones.
That is bad because you cannot instantiate NSMutableArray superclass this way.
Solution - 2nd try:
Make new (proxy) class of some type e.g. NSObject as for NSMutableArray and add class member of type NSMutableArray.
This is good because you can instantiate NSMutableClass and compiler checks when you pass wrong type to -addObject or -objectAtIndex when you overload that ones.
The bad side of that is that you need to overload every selector of the NSMutableArray that you use, not only that ones that differs in class that array contains.
Conclusion:
When you build some sophisticated code that has many class types in its arrays, believe me it is worth to try. Simply by doing this compiler showed me several errors that I would not recognize until I will face it in runtime. Or even worse, when end user would face it.
From the Apple reference for NSArray, in the Methods to Override section:
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.
As a side note, in Objective-C, there is no actual feature that allows you to declare a class as an abstract class, per se, as in Java, for instance. So, what they do instead is call something like the code below, from within some method that they want to force to be overridden by a subclass. In effect, they give the class 'abstract class' semantics.
This method definition acts as an abstract method, which raises an Exception if not overridden, with the following output:
-someAbstractFooMethod only defined for abstract class. Define -[YourClassName someAbstractFooMethod]!
- (void) someAbstractFooMethod
{
//Force subclassers to override this method
NSString *methodName = NSStringFromSelector(_cmd);
NSString *className = [self className];
[NSException raise:NSInvalidArgumentException
format:#"-%# only defined for abstract class. Define -[%# %#]!", methodName, className, methodName];
}