Factory methods in Objective-C - objective-c

I make class factories like so,
#implementation Universe {
NSString *foo;
}
+ (instancetype)universeWithMeaning:(NSString *)meaning
{
return [[self alloc] initUniverseWithMeaning:meaning];
}
- (id)initUniverseWithMeaning:(NSString *)meaning
{
if (self = [super init]) {
foo = meaning;
}
return self;
}
- (void)showMeaning
{
NSLog(#"%#", foo);
}
#end
And create object like this,
Universe *universe = [Universe universeWithMeaning:#"42"];
[universe showMeaning]; // Prints 42
This works great, but the method signature of initUniverseWithMeaning: is the same as that of universeWithMeaning:, except that it's an instance method which allows it to save instance variables to the created object.
Is there a way to this without having to implement the initUniverseWithMeaning: instance method?
I know its necessary to be inside of an instance method to be able to access instance variables, so I've been experimenting with blocks. My idea was to pass a block containing instance variable assignations to the class method which would somehow execute it in the instance context.
Implementation,
#implementation Cat {
NSString *lives;
}
+ (Cat *)newCat:(void(^)(void))cat
{
cat(); // **Problem 1**
}
- (void)showLives
{
NSLog(#"%#", lives);
}
#end
Usage,
Cat *cat = [Cat newCat:^void (void) {
self.lives = 9; // **Problem 2**
}];
[cat showLives]; // I'd like this to print 9
Problem 1: How to create a Cat object and execute cat() inside it?
Problem 2: How to make self refer to the object in the block's execution environment?
Anyway, this is more of a curiosity than anything else, it's would only be practically useful to save me from writing alloc (I would just need to include a method prototype for initUniverseWithMeaning: in the .h file.)

For your problem 1 and 2, you can try this
#interface Cat ()
#property (strong) NSString *lives;
#end
#implementation Cat
+ (Cat *)newCat:(void(^)(Cat *me))cat
{
Cat *newcat = [[self alloc] init];
cat(newcat);
return newcat;
}
- (void)showLives
{
NSLog(#"%#", lives);
}
#end
Cat *cat = [Cat newCat:^(Cat *me) {
me.lives = 9;
}];
[cat showLives]; // print 9
but I can't see much use of it... Isn't this simpler?
Cat *cat = [Cat new];
cat.lives = 9;
[cat showLives];
For your real problem
Is there a way to this without having to implement the initUniverseWithMeaning: instance method?
+ (instancetype)universeWithMeaning:(NSString *)meaning
{
Universe *universe = [[self alloc] init];
if (universe) universe->foo = meaning;
return universe;
}

The first example you've posted is the correct way of creating Objective-C factory methods.
An Objective-C factory method is nothing more than a class method wrapper around an instance level init method. Generally speaking, every factory method should have a paired init method that takes the same number and type of arguments.
fooWithBar:(NSString *)bar should be paired with initWithBar:(NSString *)bar, etc.
An exception might come in when you have an init method that takes arguments, but you've create a handful of factory methods with default arguments for this method. For example:
- (instancetype)initWithString:(NSString *)string;
+ (instancetype)fooWithString:(NSString *)string {
return [[self alloc] initWithString:string];
}
+ (instancetype)fooWithBar {
return [[self alloc] initWithString:#"bar"];
}
Now, you can create the object with in the method, then modify it, and return the modified object.
For example:
+ (instancetype)fooWithString:(NSString *)string {
Foo *f = [[self alloc] init];
f.str = string;
return f;
}
But honestly, it's just better to have an initWithString: method.
Every class should have a designated initializer and every object of that class should go through the designated initializer.

Related

Better way than write dozens of empty getters?

I use lazy instantiation on my properties, to have my class created and used as fast as possible. To achieve this, I write lots of 'empty' getters like this:
- (VMPlacesListFilter *)currentFilter
{
if (!_currentFilter) {
_currentFilter = [[VMPlacesListFilter alloc] init];
}
return _currentFilter;
}
They are all the same: if the instance variable is nil, call the -alloc and -init on the class of the property, then return the instance variable. Very common and straightforward.
If I don't create this getter by myself, Objective-C's automatic synthesization creates a getter for me, which does only the returning part (does not init the object if the instance variable is nil).
Is there any way to avoid writing this boilerplate code?
Nope, I'm afraid there's no good way around it, if you really want to have lazy initialization. Personally, I usually save lazy initialization for stuff that could really be time consuming or memory intensive (say, loading images or view controllers), and initialize cheap stuff (like simple data structures or model objects) in init.
- (instancetype) init {
self = [super init];
if( self ) {
_cheapThing1 = [NSMutableArray array];
_cheapThing2 = [[MyModelObject alloc] init];
}
return self;
}
- (ExpensiveThing*) expensiveThing
{
if( _expensiveThing == nil ) {
_expensiveThing = [[ExpensiveThing alloc] init];
}
return _expensiveThing;
}
Unless you're loading something from disk or the network, I wouldn't worry too much about initialization time. Of course, profile it.
I know this is an Objective-C question, but it's worth noting that Swift has lazy initialization built-in.
lazy var currentFilter = VMPlacesListFilter()
First off, I totally agree with #zpasternack that "lazy load" should not be misused. However, automatically generating setters and getters is completely doable with the power of Objective-C runtime. In fact, CoreData is doing this.
Anyway, I have come up with some stupid code implementing a class called LazyClass, in which you can declare dynamic properties like lazyArray (see below). Using dynamic method resolution, when the property is accessed for the first time, a getter that calls the corresponding class's default +alloc and -init method will be automatically added to the class. All underlying instance variables are stored in an NSMutableDictionary called myVars. Of course you can manipulate ivars through the runtime API as well, but using a dictionary should save some work.
Please note that this implementation just shows the basic idea of how it works. It lacks error checking and is not supposed to be shipped.
LazyClass.h
#interface LazyClass : NSObject
#property NSMutableDictionary *myVars;
// lazily initialized property
#property NSArray *lazyArray;
#end
LazyClass.m
#import "LazyClass.h"
#import <objc/objc-runtime.h>
#implementation LazyClass
#dynamic lazyArray;
- (instancetype)init {
self = [super init];
self.myVars = [NSMutableDictionary dictionary];
return self;
}
- (NSMutableDictionary *)getMyVars {
return self.myVars;
}
// the generated getter method
id dynamicGetterMethodIMP(id self, SEL _cmd) {
// selector name, which is also the property name
const char *selName = sel_getName(_cmd);
NSString *selNSName = [NSString stringWithCString:selName encoding:NSUTF8StringEncoding];
NSString *keyPath = [NSString stringWithFormat:#"myVars.%#", selNSName];
if (![self valueForKeyPath:keyPath]) {
// get the actual type of the property
objc_property_t property = class_getProperty([self class], selName);
const char *attr = property_getAttributes(property);
NSString *attrString = [[NSString alloc] initWithCString:attr encoding:NSUTF8StringEncoding];
NSString *typeAttr = [[attrString componentsSeparatedByString:#","] firstObject];
NSString *typeName = [typeAttr substringWithRange:NSMakeRange(3, typeAttr.length - 4)];
// the default initialization
Class typeClass = NSClassFromString(typeName);
[self setValue:[[typeClass alloc] init] forKeyPath:keyPath];
}
return [self valueForKeyPath:keyPath];
}
// the generated setter method
void dynamicSetterMethodIMP(id self, SEL _cmd, id value) {
// get the property name out of selector name
// e.g. setLazyArray: -> lazyArray
NSString *propertyName = NSStringFromSelector(_cmd);
propertyName = [propertyName stringByReplacingOccurrencesOfString:#"set" withString:#""];
propertyName = [propertyName stringByReplacingOccurrencesOfString:#":" withString:#""];
propertyName = [NSString stringWithFormat:#"%#%#", [propertyName substringToIndex:1].lowercaseString, [propertyName substringFromIndex:1]];
NSString *keyPath = [NSString stringWithFormat:#"myVars.%#", propertyName];
[self setValue:value forKeyPath:keyPath];
}
// dynamic method resolution
+ (BOOL)resolveInstanceMethod:(SEL)aSEL {
if ([NSStringFromSelector(aSEL) containsString:#"set"]) {
class_addMethod([self class], aSEL, (IMP)dynamicSetterMethodIMP, "^?");
} else {
class_addMethod([self class], aSEL, (IMP)dynamicGetterMethodIMP, "v#:");
}
return YES;
}
#end
Documentation
If it's the verboseness that bothers you, I suppose you could compress lazy initialisers that only need one-line initialization using the ternary operator:
- (VMPlacesListFilter *)currentFilter
{
return _currentFilter ? : (_currentFilter = [[VMPlacesListFilter alloc] init]);
}
DISCLAIMER: I don't do this, but it's interesting that it can be done

Objective-C Class definitions

Is it possible to alter the definition of a class in Objective-C?
For example, I have a function that creates objects (bullets), and those bullets are all the same. However, if given an event, say a power-up, I want to alter those bullets. In either size, or color, or what-have-you.
In my code, I have
playerBullet = [[PlayerBullet alloc] init];
But that always initializes the new bullet, regardless of 'power-up', as the standard, template bullet.
Is there a way that I adjust the class definition such that all new allocations of the PlayerBullet class come with the new value?
PlayerBullet.setProjectileColor:#"red";
Or is this approach a bad one?
The answer to your question as asked is No. However, what you are trying to do is very easily achievable with the proper design.
You should create a customer initialization method in your PlayerBullet class that takes an argument.
- (id) initWithProjectileColor:(NSString*)color
{
self = [self init];
if (self)
{
self.projectileColor = color;
}
return self;
}
So you could make a method and then call something like:
[[PlayerBullet alloc] initWithProjectileColor:#"red"];
Alternatively, you could create public properties and set them after creating a "blank" projectile.
call [[PlayerBullet alloc] initWithColor:#"red"] where you need it:
-(id) initWithColor:(NSString*) color{
self = [self init];
[self setProjecticeColor:colour];
return self;
}
There's no way to do this automatically. You could do it yourself with something like this. (Memory management not included.)
static NSString *DefaultProjectileColor = #"black";
+(void) setDefaultProjectileColor:(NSString *)color {
DefaultProjectileColor = color;
}
+(NSString *) defaultProjectileColor {
return DefaultProjectileColor;
}
-(id) init {
...
self.projectileColor = [PlayerBullet defaultProjectileColor];
...
}
-(void) gotPowerUp {
...
[PlayerBullet setDefaultProjectileColor:#"red"];
...
}

NSArray become invalid when using selector

Following is my code(with some unrelated thing omitted):
#implementation HomeSceneController
...
#synthesize options = _options; // _options is a NSArray object with 4 elements
- (id)init
{
if (self = [super initWithNibName:#"HomeScene" bundle:nil]) {
_currentOptionIndex = 0;
// Following code add two key event observations, when up arrow or down arrow key is pressed, the corresponding function will be fired.
[self addObservation:_KEY_UPARROW_ selector:#selector(UpArrowPressHandler)];
[self addObservation:_KEY_DOWNARROW_ selector:#selector(DownArrowPressHandler)];
}
return self;
}
- (void)loadView {
[super loadView];
// init _options
_options = [NSArray arrayWithObjects:
_localGameOption,
_networkGameOption,
_controlSettingOption,
_quitOption,
nil];
[self selectOption:_localGameOption];
}
....
// in these two functions, _options become nil! I don't know why...
- (void)UpArrowPressHandler {
if (_currentOptionIndex > 0) {
[self deselectOption:_options[_currentOptionIndex]];
_currentOptionIndex--;
[self selectOption:_options[_currentOptionIndex]];
}
}
- (void)DownArrowPressHandler {
if (_currentOptionIndex < 3) {
[self deselectOption:_options[_currentOptionIndex]];
_currentOptionIndex++;
[self selectOption:_options[_currentOptionIndex]];
}
}
#end
when I press up arrow key, the UpArrowPressHandler function is fired. However, the problem is, the _options array become nil.
Can anyone tell me why and how to fix it?
//===========================================================================================
Additional problem:
In the following program:
import "Deep.h"
#implementation Deep
- (id)init {
if (self = [super init]) {
_name = #"Deep";
}
return self;
}
- (void)test {
NSLog(_name);
}
#end
The test method can correctly print "Deep" when I call it somewhere else.
However, according to #ATaylor's explanation, _name should be released.
So, where is my problem?
That's because _options is getting assigned an autoreleased object, which gets released once you leave the method it was called from.
Try assigning it to 'self.options', which will (most likely) call 'retain' on the object, or call 'retain' explicitly.
Once more in code:
Either use:
self.options = [NSArray ...];
Or:
_options = [[NSArray ...] retain];
Please don't forget to release your 'options', once you're done with it, either by:
self.options = nil;
or:
[_options release];
Please only go for ONE of these options, because otherwise you'll get weird behaviour with the retain count.
You see, Apple gives us a number of 'convenience functions', which return autoreleased objects, meaning we don't have to bother with their release.
As a general rule of thumb:
Call release for every alloc/retain you call yourself.
To answer the second question:
_name = #"Deep";
is an assignment to a variable, equivalent to 'const char *_name = "Deep";' from C.
There is no need to release that, for the simple reason, that you didn't create or retain it. (No new, No alloc, no retain, no copy).
The object will not get autoreleased either, because you didn't call any sort of method, which would cause the variable to be autoreleased.
Also, see this answer, which deals with the exact problem.
Just for clarification, to get a string, there are three types of methods.
NSString *someString;
someString = #"MyString"; //No retain, no release, static String.
someString = [NSString stringWithFormat...]; //Autoreleased object, disappears after the method expires.
someString = [[NSString alloc] initWithFormat...]; //Alloced object, must be released.

Singleton in iOS 5?

Hi I had an implementation previous versions of iOS for a singleton as follows:
.h file
#interface CartSingleton : NSObject
{
}
+(CartSingleton *) getSingleton;
.m file
#implementation CartSingleton
static CartSingleton *sharedSingleton = nil;
+(CartSingleton *) getSingleton
{
if (sharedSingleton !=nil)
{
NSLog(#"Cart has already been created.....");
return sharedSingleton;
}
#synchronized(self)
{
if (sharedSingleton == nil)
{
sharedSingleton = [[self alloc]init];
NSLog(#"Created a new Cart");
}
}
return sharedSingleton;
}
//==============================================================================
+(id)alloc
{
#synchronized([CartSingleton class])
{
NSLog(#"inside alloc");
NSAssert(sharedSingleton == nil, #"Attempted to allocate a second instance of a singleton.");
sharedSingleton = [super alloc];
return sharedSingleton;
}
return nil;
}
//==============================================================================
-(id)init
{
self = [super init];
}
However on the web I see people have implemented the Singleton design pattern using this code:
+ (id)sharedInstance
{
static dispatch_once_t pred = 0;
__strong static id _sharedObject = nil;
dispatch_once(&pred, ^{
_sharedObject = [[self alloc] init]; // or some other init method
});
return _sharedObject;
}
Could someone who is experience please guide me.
Im a newbie and thoroughly confused between the old iOS implementation of the Singleton and the new one and which is the correct one?
Thanks a lot
Strictly speaking, you must use:
+ (MySingleton*) instance {
static dispatch_once_t _singletonPredicate;
static MySingleton *_singleton = nil;
dispatch_once(&_singletonPredicate, ^{
_singleton = [[super allocWithZone:nil] init];
});
return _singleton;
}
+ (id) allocWithZone:(NSZone *)zone {
return [self instance];
}
Now you guarantee that one cannot call alloc/init and create another instance.
Explanation: The instance method is at the class level and is your main access method to get a reference to the singleton. The method simply uses the dispatch_once() built-in queue that will only execute a block once. How does the runtime guarantee that the block is only executed once? Using the predicate you supply (of type dispatch_once_t). This low-level call will guarantee that even if there are multiple threads trying to call it, only one succeeds, the others wait until the first one is done and then returns.
The reason we override allocWithZone is because alloc calls allocWithZone passing nil as the zone (for the default zone). To prevent rogue code from allocating and init-ializing another instance we override allocWithZone so that the instance passed back is the already initialized singleton. This prevents one from creating a second instance.
The dispatch_once snippet is functionally identical to other one. You can read about it at http://developer.apple.com/library/mac/#documentation/Darwin/Reference/Manpages/man3/dispatch_once.3.html.
This is what I use for singletons:
+ (MySingleton*) getOne {
static MySingleton* _one = nil;
#synchronized( self ) {
if( _one == nil ) {
_one = [[ MySingleton alloc ] init ];
}
}
return _one;
}
NOTE: In most cases, you do not even need to use #synchronized (but it is safe this way).
A singleton is a special kind of class where only one instance of the class exists for the current process. (In the case of an iPhone app, the one instance is shared across the entire app.) Some examples in UIKit are [UIApplication sharedApplication] (which returns the sole instance of the application itself), and [NSFileManager defaultManager] (which returns the file manager instance). Singletons can be an easy way to share data and common methods across your entire app.
Rather than create instances of the singleton class using alloc/init, you'll call a class method that will return the singleton object. You can name the class method anything, but common practice is to call it sharedName or defaultName.
Please check a link with best answer
:http://www.idev101.com/code/Objective-C/singletons.html

Does Objective-C support Generics?

I wonder whether Objective-C offers any support for generics?
For instance, consider a method:
-(void) sort: (NSMutableArray *) deck {
}
Is there any way for me to make it only deal with Deck of Cards?
Is something like this possible to enforce?
-(void) sort: (NSMutableArray <Card *>) deck {
}
Objective-C supports lightweight Generics since 2015, with the Xcode 7.
The Xcode 7 compiler will give you the compiler warning if there is a type mismatch.
For example, the following line will raise a compiler warning as the second object in the array causes type mismatch. The array allows only NSString objects.
NSArray <NSString *> *myArray = [#"str2", #1, #"str2"];
You can use the introspection tools offered by the objective-c runtime.
Basically, it means you can check if all objects in an array either are a kind of class (Class A or one subclass of it) or a member of class (class A), or if a objects conforms to a protocol or responds to a selector (a certain method is present).
-(void) sort: (NSMutableArray *) deck {
for(id obj in deck){
if(obj isKindOfClass:[A class]]){
//this is of right class
}
}
}
You could write a Category method on NSArray that checkouts this on every object.
BOOL allAreKindOfA = [array allObjectsAreKindOfClass:[A class]];
Normally you actually don't need this very often, as you know what you put inside a collection.
If you need to check the type or ability of an object in a Array, this might be an indicator, that your Architecture is broken
Another option could be a subclass of NSMutableArray that only accepts certain classes. But be aware of the subclassing notes for NSMutableArray and NSArray, as these are Class-Clusters and therefore not easy to subclass.
Note: In my other answer I created a NSMutableArray subclass, that uses a block to test, if a certain requirement is fulfilled. If you test against class-membership, this will do exactly what you want. Use the second block for error handling.
As of Xcode 7's release, Apple has added support for Objective-C generics.
NSArray <NSString *> *arrayOfStrings = #[#"a", #"b"];
NSDictionary <NSString *, NSDate *> *dictionaryOfDates = #{ #"a" : #1 };
Inspired by MonomorphicArray I came up with another idea:
Create a subclass on NSMutableArray, that takes two blocks:
AddBlock — a block that test, if one or more requirements are full filed and adds the object only, if its passes the test
FailBlock — a block, that defines what happens, if the test was not successful.
The AddBlock could test for a certain class membership like
^BOOL(id element) {
return [element isKindOfClass:[NSString class]];
}
and the FailBlock can raise an exception, fail silently or add the element, that failed the test, to another Array. If no failBlock is provided, a default block will raise an error.
The blocks will define, if an array acts like an generic array, or as a filter.
I will give an complete example for the second case.
VSBlockTestedObjectArray.h
#import <Foundation/Foundation.h>
typedef BOOL(^AddBlock)(id element);
typedef void(^FailBlock)(id element);
#interface VSBlockTestedObjectArray : NSMutableArray
#property (nonatomic, copy, readonly) AddBlock testBlock;
#property (nonatomic, copy, readonly) FailBlock failBlock;
-(id)initWithTestBlock:(AddBlock)testBlock FailBlock:(FailBlock)failBlock Capacity:(NSUInteger)capacity;
-(id)initWithTestBlock:(AddBlock)testBlock FailBlock:(FailBlock)failBlock;
-(id)initWithTestBlock:(AddBlock)testBlock;
#end
VSBlockTestedObjectArray.m
#import "VSBlockTestedObjectArray.h"
#interface VSBlockTestedObjectArray ()
#property (nonatomic, retain) NSMutableArray *realArray;
-(void)errorWhileInitializing:(SEL)selector;
#end
#implementation VSBlockTestedObjectArray
#synthesize testBlock = _testBlock;
#synthesize failBlock = _failBlock;
#synthesize realArray = _realArray;
-(id)initWithCapacity:(NSUInteger)capacity
{
if (self = [super init]) {
_realArray = [[NSMutableArray alloc] initWithCapacity:capacity];
}
return self;
}
-(id)initWithTestBlock:(AddBlock)testBlock
FailBlock:(FailBlock)failBlock
Capacity:(NSUInteger)capacity
{
self = [self initWithCapacity:capacity];
if (self) {
_testBlock = [testBlock copy];
_failBlock = [failBlock copy];
}
return self;
}
-(id)initWithTestBlock:(AddBlock)testBlock FailBlock:(FailBlock)failBlock
{
return [self initWithTestBlock:testBlock FailBlock:failBlock Capacity:0];
}
-(id)initWithTestBlock:(AddBlock)testBlock
{
return [self initWithTestBlock:testBlock FailBlock:^(id element) {
[NSException raise:#"NotSupportedElement" format:#"%# faild the test and can't be add to this VSBlockTestedObjectArray", element];
} Capacity:0];
}
- (void)dealloc {
[_failBlock release];
[_testBlock release];
self.realArray = nil;
[super dealloc];
}
- (void) insertObject:(id)anObject atIndex:(NSUInteger)index
{
if(self.testBlock(anObject))
[self.realArray insertObject:anObject atIndex:index];
else
self.failBlock(anObject);
}
- (void) removeObjectAtIndex:(NSUInteger)index
{
[self.realArray removeObjectAtIndex:index];
}
-(NSUInteger)count
{
return [self.realArray count];
}
- (id) objectAtIndex:(NSUInteger)index
{
return [self.realArray objectAtIndex:index];
}
-(void)errorWhileInitializing:(SEL)selector
{
[NSException raise:#"NotSupportedInstantiation" format:#"not supported %#", NSStringFromSelector(selector)];
}
- (id)initWithArray:(NSArray *)anArray { [self errorWhileInitializing:_cmd]; return nil;}
- (id)initWithArray:(NSArray *)array copyItems:(BOOL)flag { [self errorWhileInitializing:_cmd]; return nil;}
- (id)initWithContentsOfFile:(NSString *)aPath{ [self errorWhileInitializing:_cmd]; return nil;}
- (id)initWithContentsOfURL:(NSURL *)aURL{ [self errorWhileInitializing:_cmd]; return nil;}
- (id)initWithObjects:(id)firstObj, ... { [self errorWhileInitializing:_cmd]; return nil;}
- (id)initWithObjects:(const id *)objects count:(NSUInteger)count { [self errorWhileInitializing:_cmd]; return nil;}
#end
Use it like:
VSBlockTestedObjectArray *stringArray = [[VSBlockTestedObjectArray alloc] initWithTestBlock:^BOOL(id element) {
return [element isKindOfClass:[NSString class]];
} FailBlock:^(id element) {
NSLog(#"%# can't be added, didn't pass the test. It is not an object of class NSString", element);
}];
VSBlockTestedObjectArray *numberArray = [[VSBlockTestedObjectArray alloc] initWithTestBlock:^BOOL(id element) {
return [element isKindOfClass:[NSNumber class]];
} FailBlock:^(id element) {
NSLog(#"%# can't be added, didn't pass the test. It is not an object of class NSNumber", element);
}];
[stringArray addObject:#"test"];
[stringArray addObject:#"test1"];
[stringArray addObject:[NSNumber numberWithInt:9]];
[stringArray addObject:#"test2"];
[stringArray addObject:#"test3"];
[numberArray addObject:#"test"];
[numberArray addObject:#"test1"];
[numberArray addObject:[NSNumber numberWithInt:9]];
[numberArray addObject:#"test2"];
[numberArray addObject:#"test3"];
NSLog(#"%#", stringArray);
NSLog(#"%#", numberArray);
Note: This code is not fully tested. Probably some of the unimplemented method should be implemented for usage in real world programs.
Not directly, no. There a few ways to simulate it, but it requires a lot of wrapper code, boilerplate code, and runtime overhead. I just switch to Objective-C++ and use C++ templates when I want or need proper generics.
So if you wanted to introduce typesafety/checks to an NSArray, you could approach it using something like this:
template <typename T>
class t_typed_NSMutableArray {
public:
t_typed_NSMutableArray() : d_array([NSMutableArray new]) {}
~t_typed_NSMutableArray() { [d_array release]; }
/* ... */
T* operator[](const size_t& idx) {
T* const obj([this->d_array objectAtIndex:idx]);
assert([obj isKindOfClass:[T class]]);
return obj;
}
void addObject(T* const obj) {
assert([obj isKindOfClass:[T class]]);
[this->d_array addObject:obj];
}
private:
NSMutableArray * const d_array;
};
in use:
t_typed_NSMutableArray<Card> array([self cards]); // < note this exact constructor is not defined
Card * firstCard = array[0]; // << ok
NSString * string = array[0]; // << warning
then you also get type safety and overloading when passing the collection, so you could not pass t_typed_NSArray<Card> as an t_typed_NSArray<NSURL>.
There is an easy, effective way of doing this (I've been using it on projects for a couple of years now). Sadly, someone deleted the answer, and my attempts to get it re-instated were rejected. Here goes again:
You can re-implement a cut-down version of C++ templating within Obj-C because Obj-C encapsulates all of C (and C++ templates are C-macros with some improved compiler/debugger support):
This only needs to be done once, using a single header file. Someone has done it for you:
https://github.com/tomersh/Objective-C-Generics
You end up with 100% legal Obj-C code that looks like this:
NSArray<CustomClass> anArray= ...
CustomClass a = anArray[0]; // works perfectly, and Xcode autocomplete works too!
This all works fine in XCode, with autocomplete, etc.