I have a data model that includes a big list (array) of heterogeneous items. There are only 2-3 different kinds of item, each kind inheriting from a base class. Using classic examples, let's say the base class is Vehicle, and the subclasses are Car, Train, and Plane.
I have a larger owning model/controller that wants to operate on this ordered list of Vehicles, and while some of the operations are shared (and are in the base class and overridden in subclasses), many of the operations are specific to only one of the kinds of items.
So I end up with a lot of code that looks like this:
for (Vehicle * vehicle in vehicles) {
if (![vehicle isKindOfClass:[Car class]]) {
continue;
}
Car * car = (Car *)vehicle;
// Do stuff only with "car".
}
So I've got lots of -isKindOfClass: everywhere and lots of casting the base class to the subclass. This all works, of course, but there seems like enough "code smell" to make me think there might be a more elegant way of either writing this code, or designing my object model.
Thoughts? Thanks.
I guess the usual polymorphic pattern would be to push the body of the loop out into the classes in question, so your loop turns into
for (Vehicle * vehicle in vehicles) {
[vehicle doSubclassSpecificThing];
}
The question then is how to share the common part of the loop body. It's possible you could break it into a series of chunks:
for (Vehicle * vehicle in vehicles) {
/* common code */
[vehicle doThingy];
/* further common code */
[vehicle doOtherThingy];
}
Or you could have -doSubclassSpecificThing be required to call [super doSubclassSpecificThing] first, and put the common part in the base class if it all comes first.
Basically, it sounds like your loop body has a number of things going on in it. If you extract each one to a method you can choose which pieces to share or override, and your loop body becomes a very high level description of what to do instead of the details.
If you want to access behaviour that’s specific to the subclasses and hasn’t been defined by their common superclass, you can’t escape from doing some sort of checking. Polymorphism is usually related to behaviour defined in a superclass (or interface) that can be overridden by subclasses, and the system knows which behaviour is appropriate according to the actual object type.
That said, in your example that particular loop is interested in a subset of the elements of the array that belong to one of the subclasses, namely Car. In that case, you can use [-NSArray indexesOfObjectsPassingTest:] to create an index set containing only the indexes of Car objects. Having done that, you can iterate the index set knowing that it points to elements in the original array whose class is Car. For instance:
NSIndexSet *cars = [vehicles indexesOfObjectsPassingTest:^(id obj, NSUInteger idx, BOOL *stop) {
return (BOOL)([obj isKindOfClass:[Car class]]);
}];
[cars enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
Car *car = [vehicles objectAtIndex:idx];
// Do stuff that’s specific to cars
}];
I would eliminate the casts and isolate the the isKindOfClass checks with a set of methods that filter the collection to have only the elements of the desired type.
Header:
#interface VehicleManager : NSObject {
#private
NSArray *vehicles;
}
#property (readonly) NSArray *vehicles;
#property (readonly) NSArray *cars;
#property (readonly) NSArray *planes;
#property (readonly) NSArray *trains;
#end
Implementation File:
#implementation VehicleManager
#synthesize vehicles;
static NSArray *MyFilterArrayByClass(NSArray *array, Class class) {
NSMutableArray *result = [NSMutableArray array];
for (id object in array) {
if ([object isKindOfClass:class]) {
[result addObject:object];
}
}
return result;
}
- (NSArray *)cars {
return MyFilterArrayByClass([self vehicles], [Car self]);
}
- (NSArray *)planes {
return MyFilterArrayByClass([self vehicles], [Plane self]);
}
- (NSArray *)trains {
return MyFilterArrayByClass([self vehicles], [Train self]);
}
- (BOOL)areAllCarsParked {
BOOL allParked = YES;
for (Car *car in [self cars]) {
allParked = allParked && [car isParked];
}
return allParked;
}
#end
Related
This question already has answers here:
Would it be beneficial to begin using instancetype instead of id?
(5 answers)
Closed 7 years ago.
I've just been reading and learning about instancetype and how in most cases it should be used instead of id in modern objective-c. Can I just ask when, then, would it be advisable to actually use id and not instancetype?
Thanks.
id
id is the generic type variable. Id doesn't warn us at compile time but it will crash if there is any problem.
Instancetype
instancetype does type checking for us at compile time to warn us of problems.
eg:
Animal.h
#interface Animal : NSObject
+ (id)giveMeAnimalA;
+ (instancetype)giveMeAnimalB;
+ (Animal *)giveMeAnimalC;
#end
Animal.m
#implementation Animal
+ (id)giveMeAnimalA {
return [[[self class] alloc] init];
}
+ (instancetype)giveMeAnimalB {
return [[[self class] alloc] init];
}
+ (Animal *)giveMeAnimalC {
return [[[self class] alloc] init];
}
#end
Suppose if we use [[Animal giveMeAnimalA] count];
The compiler will warn us of nothing, but we will crash at runtime with an exception because Animal doesn't have a count method.
And If we use [[Animal giveMeAnimalB] count];
The compiler would immediately warn us that Animal does not have a count method, and we could avoid crashing at runtime. But wouldn't it be simpler just to make our return type Animal* ?
Imagine we have a Dog subclass of Animal:
#interface Dog : Animal
- (void)makeSound;
#end
Now if we tried to call
[[Dog giveMeAnimalC] makeSound];
This wouldn't work because we would have been returned an Animal that doesn't have a makeSound method.
For complete last answer, i suggest you an example when Id is supported. It's on the ForIn Loop (fast enumeration)
Imagine, you have an array with three different objects like below :
NSArray *anotherArray = #[#"One element of Another Array",#"Second Element of Another Array"];
NSArray *array = #[#"First",#[anotherArray],#(12)];
for (id item in array)
{
if ([item isKindOfClass:[NSString class]])
{
NSLog(#"Im a NSString");
}
if ([item isKindOfClass:[NSArray class]])
{
NSLog(#"Im a NSArray");
}
if ([item isKindOfClass:[NSNumber class]])
{
NSLog(#"Im a NSNumber");
}
}
The id is a generic data type which can hold any type of data like nsstring,uiimage,nsarray and remaining all,so if you are having the requirements like returning the objects dynamically from a method you better use the return type of that method as id,hope you will get it
You can not use instancetype as return type when the type of the value that is returned is not known beforehand. If a method might return either an NSButton or an NSString depending on context, you can only use id.
instancetype is just a placeholder for the class that it is being used in; if a method of class Foo is like
- (instancetype) getMeFoo
then it is equivalent to
- (Foo *) getMeFoo
It can not return an NSString; the compiler would complain. However,
- (id) getMeFoo
can return any class type.
You could theoretically use a common superclass of the possibly returned types (for example, NSObject); but then you would need to typecast it when assigning to a concrete variable, or the compiler would bug you with warnings.
- (NSObject *) getMeFoo {
return #"foo!";
}
NSString *myString = (NSString *)[self getMeFoo];
The id type is "automatically" cast:
- (id) getMeFoo {
return #"foo!";
}
NSString *myString = [self getMeFoo];
But never forget to check if you really got the expected type:
NSString *myString = [self getMeFoo];
if (![myString isKindOfClass:[NSString class]]) {
// Danger, Will Robinson!
}
"I've just been reading and learning about instancetype and how in most cases it should be used instead of id in modern objective-c. Can I just ask when, then, would it be advisable to actually use id and not instancetype? Thanks."
You learned wrong. Kind of. The problem is that a language like Objective-C is complicated, and every rule will come with a long list of "do this IF a and b and c"... which you have to understand.
instancetype is used in one very particular situation: As the return type of init methods. You can't use for example UIButton* because an init method of UIButton could be used by a subclass, so the init method doesn't actually a UIButton but some subclass. That's why "id" was used which means "some object but I have no idea which object actually". "instancetype" on the other hand tells the compiler "you are clever, you figure it out. So with [[UIButton alloc] init] the compiler knows it returns UIButton*. [[MyButtonSubclass alloc] init] the compiler knows it returns MyButtonSubclass*.
In no other situation would you use instancetype.
Always give the compiler as much information as you can. If you have an object declared as UIButton* the compiler knows it's a UIButton or a subclass. If you have an object declared as id the compiler knows nothing. That means the compiler can't tell you if you do something stupid (like assigning a UIButton* to an NSString*, or calling the length method on a UIButton).
I have a class method in a category to construct a Cocoa collection in some way that the built-in initializers don't allow. Due to the limited initializer functionality, I have to use the mutable version of the collection to actually build it. Here's an example for NS{Mutable}IndexSet:
#implementation NSIndexSet (WSSNonContiguous)
+ (instancetype)WSSIndexSetFromMask:(NSUInteger)mask
{
NSMutableIndexSet * set = [NSMutableIndexSet indexSet];
for( NSUInteger i = 0; i < (sizeof(NSUInteger) * 8); i++ ){
if( mask & (1l << i) ){
[set addIndex:i];
}
}
return set;
}
My return type is sometimes a lie here -- there's always a mutable collection being returned, whether the user is requesting an immutable version or not.
Is it still appropriate to use instancetype in cases like this, or should I go with id? If I do use instancetype, should I also be explicitly re-creating the collection:
// Ick?
return [[self alloc] initWithIndexSet:set];
to make sure an immutable copy is returned when the call is +[NSIndexSet WSSIndexSetFromMask:]?
Everything is okay:
NSIndexSet *set = [[NSIndexSet WSSIndexSetFromMask:0] addIndex:0];
No visible #interface for 'NSIndexSet' declares the selector 'addIndex:'
instancetype says to the sender, that you return a instance of the receivers type even it is a subtype. For the sender it is a NSIndexSet, because it is send to the class object of NSIndexSet.
An introspection that way, that someone looks to the return type and sees a subclass and takes any advantage out of this information, is malformed. The contract is build with the return type and this is in this case NSIndexSet.
In objective-C I find myself creating alot of Mutable objects and then returning them as non mutable objects. Is the way I am doing it here, simply returning the NSMutableSet as an NSSet a good practice? I was thinking maybe I should specify that i make a copy of it.
/** Returns all the names of the variables used in a given
* program. If non are used it returns nil */
+ (NSSet *)variablesUsedInProgram:(id)program
{
NSMutableSet* variablesUsed = [[NSMutableSet alloc]init];
if ([program isKindOfClass:[NSArray class]]) {
for (NSString *str in program)
{
if ([str isEqual:#"x"] || [str isEqual:#"y"] || [str isEqual:#"a"] || [str isEqual:#"b"])
[variablesUsed addObject:str];
}
}
if ([variablesUsed count] > 0) {
return variablesUsed;
} else {
return nil;
}
}
If I were you, I would do it this way.
+ (NSSet *)variablesUsedInProgram:(id)program
{
NSSet *variablesUsed;
if ([program isKindOfClass:[NSArray class]]) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF = 'x' or SELF = 'y' or SELF = 'z'"];
variablesUsed = [NSSet setWithArray:[program filteredArrayUsingPredicate:predicate]];
}
int count;
return (count = [variablesUsed count]) > 0 ? variablesUsed : nil;
}
I find using predicate to filter array quite comprehensive and easy. Rather than dealing with creating a new mutable type and then testing certain condition, adding until the loop; in this scenario, it seems to be easier to use predicate. Hope this helps you.
It depends how much safety you require. If you return the object as an NSSet it will still be an NSMutableSet, so it could easily be cast back to one and modified.
Certainly, if you're creating a public API, I'd recommend returning a copy. For in internal project, perhaps the method signature already makes the intention clear enough.
Its, worth noting that, generally the performance impact of returning a copy is negligible - copying an immutable instance is effectively free whereas each copy sent to a mutable-passing-as-immutable will create another copy. So I would say its good practice to default to.
No. This is an absolutely correct OOP approach (it takes advantage of polymorphism). Every NSMutableSet is a proper NSSet. Don't copy superfluously.
Not a full answer here, consider NSProxy's one, but I want to clarify something.
In your case you create your object from scratch, and you don't set any ivar to point to that object. In my opinion in a good percentage of cases you don't need to make a copy of the mutable object returned. But if there is a good reason to deny the class client from mutating the class, then you should copy the variable.
Consider a property like this:
#property (nonatomic,assign) NSSet* set;
The class client could do this:
NSMutableSet* set= ... ; // inizialized to some value
classInstance.set= set;
// Mutate the set
Once mutated the set it could make the class be in an inconsistent state.
That's why when I have a property with the type of a class that has also a mutable version, I always put copy instead of assign in the property.
Is it possible to create an Objective-C class that can have an arbitrary number of dynamic properties at runtime?
I want to be able to call mySpecialClass.anyProperty and intercept this inside my class to be able to provide my own custom implementation that can then return an NSString (for instance) at runtime with raising an exception. Obviously this all has to compile.
Ideal would be if I could refer to my properties using something similar to the new literal syntax, e.g. mySpecialClass["anyProperty"].
I guess in a way I want to create something like a dynamic NSDictionary with no CFDictionary backing store, that executes 2 custom methods on property getting and setting respectively, with the property name passed in to these accessor methods so they can decide what to do.
There are at least two ways to do this.
Subscripting
Use objectForKeyedSubscript: and setObject:forKeyedSubscript:
#property (nonatomic,strong) NSMutableDictionary *properties;
- (id)objectForKeyedSubscript:(id)key {
return [[self properties] valueForKey:[NSString stringWithFormat:#"%#",key]];
}
- (void)setObject:(id)object forKeyedSubscript:(id <NSCopying>)key {
[[self properties] setValue:object forKey:[NSString stringWithFormat:#"%#",key]];
}
Person *p = [Person new];
p[#"name"] = #"Jon";
NSLog(#"%#",p[#"name"]);
resolveInstanceMethod:
This is the objc_sendMsg executed by the runtime for all methods:
If you look at the bottom, you have the opportunity to resolveInstanceMethod:, which lets you redirect the method call to one of your choosing. To answer your question, you need to write a generic getter and setter that looks-up a value on a dictionary ivar:
// generic getter
static id propertyIMP(id self, SEL _cmd) {
return [[self properties] valueForKey:NSStringFromSelector(_cmd)];
}
// generic setter
static void setPropertyIMP(id self, SEL _cmd, id aValue) {
id value = [aValue copy];
NSMutableString *key = [NSStringFromSelector(_cmd) mutableCopy];
// delete "set" and ":" and lowercase first letter
[key deleteCharactersInRange:NSMakeRange(0, 3)];
[key deleteCharactersInRange:NSMakeRange([key length] - 1, 1)];
NSString *firstChar = [key substringToIndex:1];
[key replaceCharactersInRange:NSMakeRange(0, 1) withString:[firstChar lowercaseString]];
[[self properties] setValue:value forKey:key];
}
And then implement resolveInstanceMethod: to add the requested method to the class.
+ (BOOL)resolveInstanceMethod:(SEL)aSEL {
if ([NSStringFromSelector(aSEL) hasPrefix:#"set"]) {
class_addMethod([self class], aSEL, (IMP)setPropertyIMP, "v#:#");
} else {
class_addMethod([self class], aSEL,(IMP)propertyIMP, "##:");
}
return YES;
}
You could also do it returning a NSMethodSignature for the method, which is then wrapped in a NSInvocation and passed to forwardInvocation:, but adding the method is faster.
Here is a gist that runs in CodeRunner. It doesn't handle myClass["anyProperty"] calls.
You're asking different things. If you want to be able to use the bracket syntax mySpecialClass[#"anyProperty"] on instances of your class, it is very easy. Just implement the methods:
- (id)objectForKeyedSubscript:(id)key
{
return ###something based on the key argument###
}
- (void)setObject:(id)object forKeyedSubscript:(id <NSCopying>)key
{
###set something with object based on key####
}
It will be called everytime you use the bracket syntax in your source code.
Otherwise if you want to create properties at runtime, there are different ways to proceed, take a look at NSObject's forwardInvocation: method, or look at the Objective-C Runtime Reference for functions to dynamically alter a class...
Guillaume is right. forwardInvocation: is the way to go. This answer gives some more details: method_missing-like functionality in objective-c (i.e. dynamic delegation at run time)
This has even more details: Equivalent of Ruby method_missing in Objective C / iOS
And these are some other lesser known Obj-C features that might help you: Hidden features of Objective-C
Enjoy!
I've been reading about NSArrays and NSDictionaires and I think I need the later. I'm trying to populate an object from a small database table. So I can access the string values via a record id. I have to do this several times so putting it into an object makes sense.
I have the basics...
- (void)viewDidLoad {
// WORKING START
NSMutableDictionary *dictCategories = [[NSMutableDictionary alloc] init];
[dictCategories setValue:#"Utility" forKey:#"3"];
[dictCategories setValue:#"Cash" forKey:#"5"];
NSString *result;
result = [dictCategories objectForKey:#"3"];
NSLog(#"Result=%#", result);
// WORKING END
// Can't get this bit right, current error Request for member
// 'getCategories' in something not a structure or union
NSMutableDictionary *dictCategories2 = self.getCategories;
NSLog(#"Result2=%#", [dictCategories2 objectForKey:#"5"]);
[super viewDidLoad];
}
-(NSMutableDictionary*)getCategories {
NSMutableDictionary *dictCategories = [[NSMutableDictionary alloc] init];
[dictCategories setValue:#"Utility" forKey:#"3"];
[dictCategories setValue:#"Cash" forKey:#"5"];
return dictCategories;
}
you are calling the method wrong,try [self getCategories]
You're not being clear on what isn't working, but a few things that are obviously wrong (JonLOo might be spot on though) ...
Firstly. You're using the wrong methods, or at least there's a better one -- setValue:forKey: should/could be setObject:forKey: instead. This might be one of the reasons for your issue.
Secondly. You're over-allocating and not releasing properly. dictCategories2 in your viewDidLoad will vanish into the void and bring with it the allocated memory for dictCategories defined in the getCategories method. An easy standard fix for this is to change
NSMutableDictionary *dictCategories = [[NSMutableDictionary alloc] init];
in getCategories into
NSMutableDictionary *dictCategories = [NSMutableDictionary dictionary];
It will be autoreleased using the latter method by the system.
Thirdly. You want to read up on #property. Instead of getFoo, setBar, the Ob-C standard is to use #properties to (pre)define setters and getter methods. You can then override these to populate default data into your methods when appropriate. You also (probably) want to store the dictionary in your interface as an instance variable, rather than letting it be deallocated all the time. Example of a #property implementation that does this:
#interface foo {
NSMutableDictionary *ingredients;
}
#property (nonatomic, retain) NSMutableDictionary *ingredients;
#end
// ....
#implementation foo
#synthesize ingredients;
// ...
// the #synthesize command above will create getter and setter methods for us but
// we can override them, which we need to do here
- (NSMutableDictionary *)ingredients
{
if (ingredients != nil) {
// we've already got an ingredients variable so we just return it
return ingredients;
}
// we need to create ingredients
ingredients = [[NSMutableDictionary alloc] init];
[ingredients setObject:#"foo" forKey:#"bar"]
return ingredients;
}
In the viewDidLoad method (or anywhere else where you think ingredients might not have been initialized yet), you would do e.g.
NSMutableDictionary *dict = self.ingredients;
Anywhere else you can opt to use just ingredients without self, but if it's nil, your method will never be called, and you will get nil thrown at you.
This is useful in many cases, and is necessary if we want to ever read or write the ingredients variable from outside of our class. It's outside of what you're asking about, but I brought it up because you're trying to do something similar with self.getCategories.
Hope that helps.