Objective C - How to use inheritance to limit the types of classes entered into a method or array, and read those objects at a later time? [duplicate] - objective-c

This question already has answers here:
Is there any way to enforce typing on NSArray, NSMutableArray, etc.?
(11 answers)
Closed 9 years ago.
How can I limit the type of objects put in an array if the limited objects are all inherited from a superclass?
So for instance, I have a parent class called
parentObj
I then have 3 child classes that have parameters that are added and not available to the superclass (parentObj)
childClass1
childClass2
childClass3
Then I have some other classes that are not related but I want to stay out of the array I'm trying to build up
otherClass1
otherClass2
I have this mutable array
NSMutableArray *arrayOfChildren;
that I want built up primarily of the three child classes of parentObj and not be able to contain the otherClasses
I don't want to build a different method to read and write the child classes to the array for each child class, because there could very well be more child classes that I build!
How can I have one method to add those classes to the array, and one to read them, including the child's added parameters?

The primary way I handle this problem was actually taught to me by a PHP book I was reading. Obviously PHP is not as strict as Objective C, so I had to do some changes.
I feel that this is a very useful skill to know how to do, especially for game programmers, where an array might need to carry hundreds of objects, all restricted to a certain type.
The nice thing about inheritance, is that the child classes also take on the "type" of their parent/ grandparent classes (and as far as you can go up if your tree of classes is long).
For example, if I had a method that had a parameter of type parentObj, you could put any of its children in that parameter slot and the code will run.
- (BOOL) addObjectToArray: (parentObj *) obj {
[array addObjectToArray:obj];
return true;
}
BOOL worked = [self addObjectToArray:childClass1];
[self addObjectToArray:childClass2];
[self addObjectToArray:childClass3];
[self addObjectToArray:otherClass1];
the code will run all the way up to the last line, in which it won't work properly. otherClass1 is not of type parentObj, so it won't work. This has successfully allowed us to limit the types of classes that the array can hold in one easy method!
Now reading the parameters from the objects is our next step to tackle. I'm sure there are other easier ways to do it, but this is how I do it.
I put a parameter in the super class (an integer) that will hold a so called ID for the child class.
parentObj.h:
//...
- (id) initWithProperties...:() :() :()... andID: (int)idType;
#property(nonatomic) int type;
//...
parentObj.m:
//...
- (id) initWithProperties...:() :() :()... andID: (int)idType {
//...
self.type = idType;
//...
}
//...
childClass1.h:
//...
#property(nonatomic) int someOtherPropertyOfChild1;
//...
childClass1.m:
//...
- (id) init {
self = [super initWithProperties... ...andID:1];
if (self) {
}
return self;
}
//...
childClass2.h:
//...
#property(nonatomic) int someOtherPropertyOfChild2;
//...
childClass2.m:
//...
- (id) init {
self = [super initWithProperties... ...andID:2];
if (self) {
}
return self;
}
//...
etc...
You need to remember which ID correlates to which child class, otherwise you are bound to get errors.
So now say you had a for loop that cycled through all the objects in the array full of classes. And say we needed to print out that extra parameter in each child class, how would we do that? I will show you how.
let's assume the variable being iterated in the for loop is x.
switch([array objectAtIndex:x].type) {
case 1:
//remember that childClass1 is id of one
childClass1 *CC1 = [array objectAtIndex:x];
NSLog(#"%d", CC1.someOtherPropertyOfChild1);
break;
//...
if the default case is called, that means that the object it is getting from the array is a parentObj object, or a child class that is not ID'd correctly.
I hope that this helps you in your troubles, and I hope it helps you understand why inheritance is important, and why you should use it!

Create a wrapper method that you use to add objects to the array:
- (void)addObject:(id)object
{
if ([object isKindOfClass:[parentObj class]])
{
[self.arrayOfChildren addObject:object];
}
}
You could also add an isMemberOfClass check if you wanted to also exclude instances of the parentObj class itself.

Related

How to treat NSArray of mixed content as of unified content?

Class B is derived from Class A.
Class B overrides '- (NSString *) description', overridden in Class A too.
I made a new 'NSArray' of pointers to instances, both Class A and Class B.
Is it possible to treat all of them in a cycle as Class A instances, more precisely - is it possible to use Class A '- (NSString *) description' for all of them?
Currently, I use a check if it's a Class B instance, and call initializer that makes Class A instance in that case. But it seems unnecessary, I don't like this solution.
Yes you can do it.
for (ClassA item in myMixedArray)
NSLog("#%", item.description);
This is valid if myMixedArray has elements of ClassA, ClassB. In fact (although not correct and very confusing) it works for any class or object pointer, even if they are unrelated to Class A or B. The only issue is making sure that item implements description, otherwise it will fail.
In your specific case it is very safe, because any element would somehow be a ClassA object (either directly or inherited).
If some day you need to have totally unrelated objects you can use id as the iterator type.
for (id item in myMixedArray)
[id someMethodThatAllObjectsImplement];
EDIT (to clarify misunderstood question):
In Objective-C there's no easy way to force a call on the base class implementation, unlike C++, even type casting the pointer will prove useless. There's a tricky way to force calling the base class implementation explained on this question.
The following example may be a little easier on the eyes than the solution in the other thread mentioned in Merlevede's answer.
for ( id item in array )
{
if ( [item isMemberOfClass:[ClassA class]] )
{
NSLog( #"%#", item );
}
else
{
struct objc_super parent = { item, [item superclass] };
NSLog( #"%#", objc_msgSendSuper( &parent, #selector(description) ) );
}
}
The objc_msgSendSuper function can be used to call any method that returns an id
Edit - forgot to mention that you need to
#import <objc/message.h>
Edit 2 - based on feedback from OP in the comments, here is the ultimate solution
for ( id item in array )
{
struct objc_super baseClass = { item, [ClassA class] };
NSLog( #"%#", objc_msgSendSuper( &baseClass, #selector(description) ) );
}

Passing an integer to a class, and then creating an array with a size of that integer [duplicate]

This question already has answers here:
declaring array of an object X with unknown size objective c
(2 answers)
Closed 8 years ago.
I have a class named Calculator. This class accepts a bunch of test scores, and will store each of them into an array. This array is called scoreArray.
I want to declare the array like this, but I'm having trouble with the scope:
int scoreArray[numTestScores];
If I put that code into the #implementation, it doesn't know what numTestScores is, because it hasn't been passed to the class yet.
And if I try to do this:
-(id)init:(int)numTestScores_
{
if (self = [super init])
{
int scoreArray[numTestScores_];
}
return self;
}
then the array gets created, but the rest of the class doesn't know what scoreArray is.
How can I make it so that scoreArray is created with length "numTestScores" and has the same scope as if I had put it in the implementation block?
Using a native C array is an unnecessary pain. I'd rather use a NSMutableArray, declaring it as a property.
#property (nonatomic, copy) NSMutableArray *scores;
NSMutableArray automatically manages its memory, so you don't need to declare its size in advance.
Just initialize it as
_scores = [NSMutableArray array];
and then add values to it
[self.scores addObject:#(aResult)]; //assuming that aResult is an integer expression
#(...) wraps the value in a NSNumber since NSArray can only hold objects.
To retrieve a score, you can do
int score = [self.scores[0] intValue];
VLAs (variable-length arrays) only work in contexts where... um... where they make sense. In this case, you will rather want to utilize dynamic memory allocation and an instance variable:
#interface MyClass: NSObject {
int *array;
size_t size;
}
// ... etc ...
- (id)initWithSize:(size_t)n
{
if (self = [super init]) {
size = n;
array = malloc(size * sizeof array[0]);
}
return self;
}
// free the allocated memory upon destruction
- (void)dealloc
{
// ...
free(array);
// ...
[super dealloc];
}
As to why it doesn't really make sense to use a variable-length array as an instance variable: instance variables are part of an object. If you declared a VLA inside an object, then the size of the instance would depend on its initialization. That is not something immediately easy to implement, and it is not the way the Objective-C runtime works. (I'm not saying it's impossible, but it would be very, very impractical.)
All classes have their instance size deduced at compile time (well, mostly... nowadays it's rather the initialization of the runtime system), and it can't be changed later. As a consequence, the size of an object cannot vary from initialization to initialization.
On the assumption that you're writing a properly contained object oriented class, the implementation of your set of scores is not important to the outside world. If that is indeed the case, don't create an int array, create an NSMutableArray instead (and if you want to pre-fill numTestScores_ entries to make things easier later, do that, but there shouldn't be a need for it really).
If you must have an array, you will have to allocate it dynamically by declaring scoreArray to be an int * and using malloc. Be careful here though -- you will have to create a dealloc method in your class to free() the array if it has been created.

Objective-C: writing a smart reusable compare function [duplicate]

I'm trying to write an instance method for a Card class that compares a single card against an array. The class has some properties like: shape and color. The otherCards array is filled with other instances of this class, that also have their shapes and colors.
Now, I want to write a method that can check all of these attributes separately. How can I pass in a particular attribute, as in: [allAttributesIsEqual:otherCards compareWith: self.shape]? So I can pass in self.shape or self.color when actually comparing?
- (BOOL)allAttributesIsEqual: (NSArray *)otherCards
{
//self.shape is equal to othercards.shape
}
You can't just pass in self.shape, because that will give you the value of the property. Thanks to some of Cocoa/ObjC's dynamite, however, you can pass in the name of a property (or method) and get the results later.
The clever (dare I say, perhaps even "Pythonic") way:
// The name of the property we're interested in.
NSString * key = #"color";
// Get the values of that property for all the Cards in the array, then
// collapse duplicates, because they'll give the same results when comparing
// with the single card.
NSSet * vals = [NSSet setWithArray:[arrayOfCards valueForKey:key]];
// Now, if the set has only one member, and this member is the same
// as the appropriate value of the card we already have, all objects
// in the array have the same value for the property we're looking at.
BOOL colorIsEqual = ([vals count] == 1 && [vals containsObject:[myCard valueForKey:key]]);
Then your method can look like this:
- (BOOL)allOtherCards: (NSArray *)otherCards haveEqualAttribute: (NSString *)key;
Dan F's suggestion to implement - (BOOL)<#property#>Equal: (NSArray *)otherCards; for each property you're interested in is not at all a bad idea, however. Of course, each of these could call through to the base "clever" version.
The idea is that you (as the Card class) know what it means for two instances to be "equal". It sounds like in your case, two Cards are equivalent if their color and shape properties match. Start by implementing -isEqual: (along with -hash) on your custom Card class. This is the standard way of having an object expose a notion of whether it is the same as some other object. You can implement this however you need. Within this isEqual method, you can check all of the relevant properties:
- (BOOL)isEqual:(id)otherObject
{
if (![otherObject isKindOfClass:[self class]) {
return NO;
}
Card * otherCard = (Card *)otherObject;
// now compare the attributes that contribute to "equality"
return ([self.shape isEqual:otherCard.shape] && [self.color isEqual:otherCard.color]);
}
Now, once your custom object supports this -isEqual:, you can check all the cards in the array to see if any are equal to the candidate card. You could do the loop yourself and use the -isEqual:, but the nice thing about doing this in the system standard way is that you can also use system provided convenience methods to check for collection membership, like:
if ([myCardList containsObject:candidateCard]) {
// one of the cards compared as "equal"
}
If you would prefer to do this as you request in a method on your class, you could then structure it like so:
- (BOOL)isRepresentedInArray:(NSArray *)arr
{
return [arr containsObject:self];
}

Objective-C: override dynamic getter

I have an NSManagedObject subclass MyClass with a property myProp, which is defined #dynamic. There are various instances of reading myProp in my code, via [myClass myProp].
Now, I want to define a getter (that returns myProp after appending something to it) for myProp, without changing the various calls to [myClass myProp]. i.e. without creating a getter that is named something other than getMyProp.
My question is, if I create a getter getMyProp, which will override the getter created by NSManagedObject, how do I access the original value that is stored in the database?
To access the underlying values of a managed object you use the following two methods:
- (id)primitiveValueForKey:(NSString *)key
- (void)setPrimitiveValue:(id)value forKey:(NSString *)key
This is often used to convert NSNumber attributes into their 'real' type, for example a bool property:
- (BOOL)isShared
{
[self willAccessValueForKey:#"isShared"];
NSNumber *underlyingValue = [self primitiveValueForKey:#"isShared"];
[self didAccessValueForKey:#"isShared"];
return [underlyingValue boolValue];
}
The willAccessValueForKey: and didAccessValueForKey: are required by the underlying managed object class for handling faults and relationships etc.
And if you do end up writing a setter, you must also wrap the accessor in KVC methods:
- (void)setShared:(BOOL)isShared
{
NSNumber *newUnderlyingValue = [NSNumber numberWithBool:isShared];
[self willChangeValueForKey:#"isShared"];
[self setPrimitiveValue:newUnderlyingValue forKey:#"isShared"];
[self didChangeValueForKey:#"isShared"];
}
Having said this, I would personally not recommend you keep the same method name unless you have a good reason. For 'derived' values you generally want to create a brand new method with a different name. It doesn't take long to do a quick find/replace throughout your code.
EDIT: added willAccessValueForKey:/didAccessValueForKey: (thanks jrturton)

Objective-C pattern for class instance variables?

What would be a nice pattern in Objective-C for class variables that can be "overridden" by subclasses?
Regular Class variables are usually simulated in Objective-C using a file-local static variables together with exposed accessors defined as Class methods.
However, this, as any Class variables, means the value is shared between the class and all its subclasses. Sometimes, it's interesting for the subclass to change the value for itself only. This is typically the case when Class variables are used for configuration.
Here is an example: in some iOS App, I have many objects of a given common abstract superclass (Annotation) that come in a number of concrete variations (subclasses). All annotations are represented graphically with a label, and the label color must reflect the specific kind (subclass) of its annotation. So all Foo annotations must have a green label, and all Bar annotations must have a blue label. Storing the label color in each instance would be wasteful (and in reality, perhaps impossible as I have many objects, and actual configuration data - common to each instance - is far larger than a single color).
At runtime, the user could decide that all Foo annotations now will have a red label. And so on.
Since in Objective-C, Classes are actual objects, this calls for storing the Foo label color in the Foo class object. But is that even possible? What would be a good pattern for this kind of things? Of course, it's possible to define some sort of global dictionary mapping the class to its configuration value, but that would be kind of ugly.
Of course, it's possible to define some sort of global dictionary mapping the class to its configuration value, but that would be kind of ugly.
Why do you think this would be ugly? It is a very simple approach since you can use [self className] as the key in the dictionary. It is also easy to make it persistent since you can simply store the dictionary in NSUserDefaults (as long as it contains only property-list objects). You could also have each class default to its superclass's values by calling the superclass method until you find a class with a value.
+ (id)classConfigurationForKey:(NSString *)key {
if(_configurationDict == nil) [self loadConfigurations]; // Gets stored values
Class c = [self class];
id value = nil;
while(value == nil) {
NSDictionary *classConfig = [_configurationDict objectForKey:[c className]];
if(classConfig) {
value = [classConfig objectForKey:key];
}
c = [c superclass];
}
return value;
}
+ (void)setClassConfiguration:(id)value forKey:(NSString *)key {
if(_configurationDict == nil) [self loadConfigurations]; // Gets stored values
NSMutableDictionary *classConfig = [_configurationDict objectForKey:[self className]];
if(classConfig == nil) {
classConfig = [NSMutableDictionary dictionary];
[_configurationDict setObject:classConfig forKey:[self className]];
}
[classConfig setObject:value forKey:key];
}
This implementation provides no checking to make sure you don't go over the top superclass, so you will need to ensure that there is a value for that class to avoid an infinite loop.
If you want to store objects which can't be stored in a property list, you can use a method to convert back and forth when you access the dictionary. Here is an example for accessing the labelColor property, which is a UIColor object.
+ (UIColor *)classLabelColor {
NSData *data = [self classConfigurationForKey:#"labelColor"];
return [NSKeyedUnarchiver unarchiveObjectWithData:data];
}
+ (void)setClassLabelColor:(UIColor *)color {
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:color];
[self setClassConfiguration:data forKey:#"labelColor"];
}
my answer here may help:
What is the recommended method of styling an iOS app?
in that case, your annotation just holds a reference to a style (e.g. you need only one per style), and the size of a pointer for an entire style is not bad. either way, that post may give you some ideas.
Update
Jean-Denis Muys: That addresses the sample use case of my question, but not my question itself (a pattern to simulate class instance variables).
you're right, i didn't know how closely your example modeled your problem and i considered commenting on that.
for a more general and reusable solution, i'd probably just write a threadsafe global dictionary if your global data is nontrivial (as you mentioned in your OP). you could either populate it in +initialize or lazily by introducing a class method. then you could add a few categories to NSObject to access and mutate the static data -- do this for syntactical ease.
i suppose the good thing about that approach is that you can reuse it in any program (even though it may appear ugly or complex to write). if that's too much locking, then you may want to divide dictionaries by prefixes or create a simple thread safe dictionary which your class holds a reference to -- you can then synthesize an instance variable via the objc runtime to store it and declare an instance method to access it. the class method would still have to use the global data interface directly.