Objective C Subclassing: Is this proper? - objective-c

So I'm working on an iPad game using the cocos2d framework.
My game requires different buildings to be spawned at the beginning of a "level". The user gets to choose which buildings they want to use.
An important functionality here is that different buildings will perform different actions when pressed.
I have a parent class that looks something like this:
BaseBuilding.h
#interface BaseBuilding : NSObject {
CGRect _hitBox;
NSString *_name;
CCSprite *_sprite;
int _identifier;
int _health;
int _coolDownDuration;
}
#property (atomic, retain) CCSprite *sprite;
#property (atomic, assign) int identifier;
#property (atomic, assign) int health;
- (void) onBuildingPressed;
- (id) initWithName:(NSString*)baseName;
- (id) initBuilding;
- (CGRect) hitBox;
- (void) sustainDamage;
So most of my buildings are very similar. Each building has their own initBuilding method which overrides the parent. The initBuilding method calls the super initWithName method which then looks up a plist with information about that building (sprite name, etc).
Each building also has their own onBuildingPressed method, which also overrides the parent method. This is very important functionality.
The part that I'm having trouble with:
I want to spawn the buildings based on what the player selects. What I'm currently doing is something like this:
BaseBuilding *building;
switch (choice) {
case 1:
building = [BuildingX alloc];
[building initBuilding];
break;
case 2:
building = [BuildingY alloc];
[building initBuilding];
break;
case 3:
building = [BuildingZ alloc];
[building initBuilding];
break;
}
return building;
Since BuildingX, BuildingY, BuildingZ are all subclasses of BaseBuilding, initBuilding does call the correct method.
Later on I can call [rightBuilding onBuildingPressed]; and it works as expected.
BuildingX has a removeAmmo method that the other buildings don't have, so I have to do this to call it: [(BuildingX*)centerBuilding removeAmmo].
Is this proper, or this a better way of doing this?

The only thing that I notice is that, if you have many subclasses - provided that all subclasses have some alphabetical order - you do it faster and more elegantly by getting a class object according to the value of choice:
NSString* className= [NSString stringWithFormat: #"Building%c",'X'-1+choice];
Class class= NSClassFromString(className);
BaseBuilding* building= [[class alloc] initBuilding];
But this is just a matter of style and rapidity (writing the code, I mean). Your method is perfectly fine and there's nothing wrong with it.

You can verify the class at run time to guarantee there's no room for error here like so:
if ( [centerBuilding respondsToSelector:NSSelectorFromString(#"removeAmmo")] ) {
[centerBuilding performSelector:NSSelectorFromString(#"removeAmmo"];
}

Seems logical to me. I don't know if it's stylish, the snobs can determine that for you. If it works, it works. I don't see the point in fixing things that aren't broken.

Yes, it's correct.
But:
1) If you are casting all your subclasses to the parent class, and you need to use a particular method of your subclass, then it's useless casting to the right subclass ONLY in certain circumstances. Try to make the removeAmmo a base method...
or
2) make your building subclass calling the removeAmmo method from its inside, and declare the removeAmmo method in a category
From an external point of view, the BaseClass has not the removeAmmo method, so it's not logic to declare a public method which could be called only re-casting your object to a specific subclass.

Related

Any way to apply Objective-C category only to current class (or equivalent effect)?

Let's say I have a custom subclass of UIView called MyCustomView. Let's also say that I have a category on UIView called UIView+Dictionary that adds an NSDictionary property called dictionary to every UIView.
If I were to import UIView+Dictionary.h into MyCustomView.m then every view referenced within MyCustomView.m would have this added dictionary property, which in many situations is exactly the desired behavior.
However, if I wanted UIView+Dictionary applied only to MyCustomView itself and not to every UIView referenced within MyCustomView.m, is there a way to do so (or achieve a similar effect)?
I'd like to avoid making MyCustomView a subclass of another custom subclass (e.g., MyViewWithDictionary), as I'd ideally like to be able to import multiple categories for something akin to multiple inheritance (e.g., UIView+Dictionary, UIView+Border, UIView+CustomAnimations).
In my actual own scenario, I've written a category to automatically implement a custom UINavigationBar in a view controller, but I'd like that category to apply only to the view controller into which I am importing the category and not any other view controllers that may be referenced in that file.
Any and all insights are appreciated! And I apologize in advance as I am fairly certain there are more correct terminologies for the effect described above.
However, if I wanted UIView+Dictionary applied only to MyCustomView itself [...] is there a way to do so [...]?
Only by changing the category to be on MyCustomView and not UIView.
The header has nothing to do with whether the category's methods are present on any given instance. If the category is compiled into your program, the methods are there, no matter where the instance is created. This is the reason that prefixes are so important on methods that are added to framework classes: categories have global effect, and name collisions are undefined behavior.
The header only affects the visibility of the methods as far as the compiler is concerned. You can use the usual tricks to call them at runtime regardless.
The category takes effect on the class itself, when the runtime is initialized at launch. If you want the methods of the category to be available only on a certain class, the category must be defined on that class.
As Josh pointed out, any methods added in categories are basically inert unless you call them. The issue that I was having was for generated properties and swizzled methods in categories (since, as Josh also pointed out, there are no mixins in Objective-C).
I was able to solve this by adding in a custom BOOL in my category that defaults to NO and acts as a "switch" for whatever category methods and properties I want to specify.
E.g., if I wanted my dictionary property to be lazily instantiated but only within MyCustomView, I could do the following:
// UIView+Dictionary.h
#interface UIView (Dictionary)
#property (nonatomic) BOOL enableDictionary;
#property (nonatomic, strong) NSDictionary *dictionary;
#end
// UIView+Dictionary.m
#import "UIViewController+CustomNavigationBar.h"
#import <objc/runtime.h>
#implementation UIView (Dictionary)
- (void)setEnableDictionary:(BOOL)enableDictionary {
objc_setAssociatedObject(self, #selector(enableDictionary), #(enableDictionary), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)enableDictionary {
NSNumber *enableDictionaryValue = objc_getAssociatedObject(self, #selector(enableDictionary));
if (enableDictionaryValue) {
return enableDictionaryValue.boolValue;
}
objc_setAssociatedObject(self, #selector(enableDictionary), #NO, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
return self.enableDictionary;
}
- (void)setDictionary:(NSDictionary *)dictionary {
objc_setAssociatedObject(self, #selector(dictionary), dictionary, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSDictionary *)dictionary {
if (!self.enableDictionary) {
return nil;
}
NSDictionary *dictionary = objc_getAssociatedObject(self, #selector(dictionary));
if (dictionary) {
return dictionary;
}
objc_setAssociatedObject(self, #selector(dictionary), #{}, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
return self.dictionary;
}
#end
And then within -[MyCustomView viewDidLoad] I could simply call self.enableDictionary = YES. That way, only instances of MyCustomView will have a non-nil lazily instantiated NSDictionary. (Note that, in this example, all instances of UIViews will still respond to the selector #selector(dictionary), but our behavior will differ based on whether enableDictionary is YES or NO.)
While that is a trivial example, the same strategy can be used for methods that are swizzled within categories. (Again, swizzling methods within categories is probably bad form but a necessary evil in certain scenarios.)

Subclassing iOS Model Objects - Appropriate Design Pattern

I fear this is a rather simple question, but after much googling I think I have overshot my intended result. I believe my question to be related to a design pattern, but alas I could be wrong.
My application calls an RESTful API and gets back what amounts to a list of model objects represented by an NSDictionary. Each of which I will call NNEntity. There are (conceptually) multiple different subtypes of NNEntity. All subtypes of NNEntity share the property of entityID, but each have their own unique properties as well. All instances of NNEntity have a method called readFromDict:(NSDictionary *)d that populates their respective properties. This method is enforced by a protocol that all NNEntity subtypes conform to. It looks like this:
//NNEntity.h
#interface NNEntity : NSObject <NNReadFromDictProtocol>
#property (nonatomic, strong) NSString *entityID;
#end
//NNEntity.m
#implementation NNEntity
- (void)readFromDict:(NSDictionary *)d {
//set common properties from values in d
self.entityID = [d objectForKey:#"ID"];
}
#end
//NNSubEntity1.h
#interface NNSubEntity1 : NSEntity <NNReadFromDictProtocol>
#property (nonatomic, strong) NSString *favoriteColor;
#end
//NNSubEntity1.m
#implementation NNSubEntity1
- (void)readFromDict:(NSDictionary *)d {
[super readFromDict:d];
//set unique properties from values in d
self.favoriteColor = [d objectForKey:#"colorPreference]:
}
#end
//NNSubEntity2.h
#interface NNSubEntity2 : NSEntity <NNReadFromDictProtocol>
#property (nonatomic, strong) NSString *middleName;
#end
//NNSubEntity2.m
#implementation NNSubEntity2
- (void)readFromDict:(NSDictionary *)d {
[super readFromDict:d];
//set unique properties from values in d
self.middleName = [d objectForKey:#"middleName]:
}
#end
I have read various pieces on the use of a Factory or Builder Desing pattern for similar use cases but I am curious if that is necessary in this rather simple case. For example, does my current code end up creating both and instance of NNEntity and NNSubEntity2 if I were to call something like this:
NNEntity *newEntity = [[NNSubEntity2 alloc] init];
//assume dict exists already and is properly keyed
[newEntity readFromDict:dict];
I assume not, but would newEntity have both the common property of entityID as well as the unique property of middleName set correctly? Also, much appreciated if you have thoughts on a better or more efficient design approach.
This looks like exactly how you should be doing it. You have a base class which read in the common attributes, and subclasses which read in their specific attributes.
For example, does my current code end up creating both and instance of NNEntity and NNSubEntity2? NNEntity *newEntity = [[NNSubEntity2 alloc] init];
Nope. When you run this, you instantiate NNSubEntity2 and store the result in a variable typed by it's superclass, which is totally valid. This allows you to call any methods defined on the superclass, but the actual instance is still of the subclass.
Would newEntity have both the common property of entityID as well as the unique property of middleName set correctly?
It sure would. It inherits the instance variables, properties and methods in the superclass.
Rest assured, as far as I can tell this looks sound and is a pattern I've used before.
I do it like this.
// NNEntity.h
#interface NNEntity : NSObject
#property (nonatomic, retain) NSString *entityId;
#end;
// NNEntity.m
#implementation NNEntity
#end;
// NNEntity+KVC.h
#interface NNEnity (KVC)
-(void)setValue:(id)value forUndefinedKey:(NSString *)key {
#end
// NNEntity+KVC.m
#implementation NNEntity (KVC)
-(void)setValue:(id)value forUndefinedKey:(NSString *)key {
// Handle this as appropriate to your app.
// A minimal implementation will throw an exception.
}
#end
And similarly for your various subclasses. You don't (necessarily) need the category on your subclasses.
Then, given NSDictionary *dict with your stuff in it:
NNEntity *entity = [[NNEntity alloc] init];
[entity setValuesForKeysWithDictionary:dict];
Violá! You're done. There are some criticisms of this method, but given a strong implementation of setValue:forUndefinedKey:, I think it's safe and incredibly flexible.
The secrets are in Apple's beautiful Key-Value Coding technology. Essentially, setValuesForKeysWithDictionary: iterates the keys the dict you give it, and for eachinvokes setValue:forKey: in its receiver. It looks something like this (though I'm sure Apple optimizes it under the hood):
-(void)setValuesForKeysWithDictionary:(NSDictionary *)dictionary {
NSArray *keys = [dictionary allKeys];
for (NSString* key in keys) {
[self setValue:[dictionary valueForKey:key] forKey:key];
}
}
I also like this approach because a conversion to CoreData is simple; when you tell CoreData to 'render' your model, it simply overwrites your stubbed model classes, keeping your KVC Category intact. What is more, if your implementation of setValue:forUndefinedKey: is smooth, you can make model changes to your backend without crashing the app (this is a bit of a no-no, but it's not much different from your factory solution).
Of course, I have not addressed your need to selectively choose which class to instantiate. But that is a larger design issue that could be affected even by the design of your API and backend. So I defer.
Also, as you noted in your comment below, the property names must match up. This is a show-stopper for some developers, especially so if you cannot control both the backend and the client.
Give it a try. Feedback is welcome.

Objective-c categories

I'm learning Objective-C and now I'm stuck with categories. Here is the code
#import <Foundation/Foundation.h>
#interface NSMutableArray (NSMutableArrayCategory)
-(NSMutableArray *)removeLastObject;
#end
#import "NSMutableArrayCategory.h"
#implementation NSMutableArray (NSMutableArrayCategory)
-(NSMutableArray *)removeLastObject
{
return [self removeObjectAtIndex:[self count] - 1];
}
#end
The problem is that I get
Returning void from a function with incompatible result type 'NSMutableArray' *
What is wrong here ?
You probably want to declare removeLastObject as returning void, not NSMutableArray.
#interface NSMutableArray (NSMutableArrayCategory)
- (void)removeLastObject;
#end
Then the implementation will look like this:
- (void)removeLastObject
{
[self removeObjectAtIndex:[self count] - 1];
}
This matches the other mutation methods on NSMutableArray.
The underlying problem is that removeLastObject is already defined and does what you want (so people don't recreate it). Some of the answers here are a little confusing, however, in that they suggest it's ok to use a category to override an existing method (or at least don't explicitly state that you must not do this). You must not do this. It is poorly defined behavior, and in some cases it is completely undefined behavior. You generally should subclass to override a method.
There is an advanced technique called swizzling that will allow you to do this without a subclass, but it is a very specialized and sometimes fragile technique, not for general use.
But in no case should you use a category to override an existing method.
Im not familiar with objective c. but i imagine that removeObjectAtIndex returns void. so I would try
[self removeObjectAtIndex:[self count] -1];
return self;
return self after removing item..

Instance Variables for Objective C Categories

I have a situation where it seems like I need to add instance variables to a category, but I know from Apple's docs that I can't do that. So I'm wondering what the best alternative or workaround is.
What I want to do is add a category that adds functionality to UIViewControllers. I would find it useful in all my different UIViewControllers, no matter what specific UIViewController subclass they extend, so I think a category is the best solution. To implement this functionality, I need several different methods, and I need to track data in between them, so that's what led me to wanting to create instance methods.
In case it's helpful, here's what I specifically want to do. I want to make it easier to track when the software keyboard hides and shows, so that I can resize content in my view. I've found that the only way to do it reliably is to put code in four different UIViewController methods, and track extra data in instance variables. So those methods and instance variables are what I'd like to put into a category, so I don't have to copy-paste them each time I need to handle the software keyboard. (If there's a simpler solution for this exact problem, that's fine too--but I would still like to know the answer to category instance variables for future reference!)
Yes you can do this, but since you're asking, I have to ask: Are you absolutely sure that you need to? (If you say "yes", then go back, figure out what you want to do, and see if there's a different way to do it)
However, if you really want to inject storage into a class you don't control, use an associative reference.
Recently, I needed to do this (add state to a Category). #Dave DeLong has the correct perspective on this. In researching the best approach, I found a great blog post by Tom Harrington. I like #JeremyP's idea of using #property declarations on the Category, but not his particular implementation (not a fan of the global singleton or holding global references). Associative References are the way to go.
Here's code to add (what appear to be) ivars to your Category. I've blogged about this in detail here.
In File.h, the caller only sees the clean, high-level abstraction:
#interface UIViewController (MyCategory)
#property (retain,nonatomic) NSUInteger someObject;
#end
In File.m, we can implement the #property (NOTE: These cannot be #synthesize'd):
#implementation UIViewController (MyCategory)
- (NSUInteger)someObject
{
return [MyCategoryIVars fetch:self].someObject;
}
- (void)setSomeObject:(NSUInteger)obj
{
[MyCategoryIVars fetch:self].someObject = obj;
}
We also need to declare and define the class MyCategoryIVars. For ease of understanding, I've explained this out of proper compilation order. The #interface needs to be placed before the Category #implementation.
#interface MyCategoryIVars : NSObject
#property (retain,nonatomic) NSUInteger someObject;
+ (MyCategoryIVars*)fetch:(id)targetInstance;
#end
#implementation MyCategoryIVars
#synthesize someObject;
+ (MyCategoryIVars*)fetch:(id)targetInstance
{
static void *compactFetchIVarKey = &compactFetchIVarKey;
MyCategoryIVars *ivars = objc_getAssociatedObject(targetInstance, &compactFetchIVarKey);
if (ivars == nil) {
ivars = [[MyCategoryIVars alloc] init];
objc_setAssociatedObject(targetInstance, &compactFetchIVarKey, ivars, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[ivars release];
}
return ivars;
}
- (id)init
{
self = [super init];
return self;
}
- (void)dealloc
{
self.someObject = nil;
[super dealloc];
}
#end
The above code declares and implements the class which holds our ivars (someObject). As we cannot really extend UIViewController, this will have to do.
I believe it is now possible to add synthesized properties to a category and the instance variables are automagically created, but I've never tried it so I'm not sure if it will work.
A more hacky solution:
Create a singleton NSDictionary which will have the UIViewController as the key (or rather its address wrapped as an NSValue) and the value of your property as its value.
Create getter and setter for the property that actually goes to the dictionary to get/set the property.
#interface UIViewController(MyProperty)
#property (nonatomic, retain) id myProperty;
#property (nonatomic, readonly, retain) NSMutableDcitionary* propertyDictionary;
#end
#implementation UIViewController(MyProperty)
-(NSMutableDictionary*) propertyDictionary
{
static NSMutableDictionary* theDictionary = nil;
if (theDictionary == nil)
{
theDictioanry = [[NSMutableDictionary alloc] init];
}
return theDictionary;
}
-(id) myProperty
{
NSValue* key = [NSValue valueWithPointer: self];
return [[self propertyDictionary] objectForKey: key];
}
-(void) setMyProperty: (id) newValue
{
NSValue* key = [NSValue valueWithPointer: self];
[[self propertyDictionary] setObject: newValue forKey: key];
}
#end
Two potential problems with the above approach:
there's no way to remove keys of view controllers that have been deallocated. As long as you are only tracking a handful, that shouldn't be a problem. Or you could add a method to delete a key from the dictionary once you know you are done with it.
I'm not 100% certain that the isEqual: method of NSValue compares content (i.e. the wrapped pointer) to determine equality or if it just compares self to see if the comparison object is the exact same NSValue. If the latter, you'll have to use NSNumber instead of NSValue for the keys (NSNumber numberWithUnsignedLong: will do the trick on both 32 bit and 64 bit platforms).
This is best achieved using the built-in ObjC feature Associated Objects (aka Associated References), in the example below just change to your category and replace associatedObject with your variable name.
NSObject+AssociatedObject.h
#interface NSObject (AssociatedObject)
#property (nonatomic, strong) id associatedObject;
#end
NSObject+AssociatedObject.m
#import <objc/runtime.h>
#implementation NSObject (AssociatedObject)
#dynamic associatedObject;
- (void)setAssociatedObject:(id)object {
objc_setAssociatedObject(self, #selector(associatedObject), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (id)associatedObject {
return objc_getAssociatedObject(self, #selector(associatedObject));
}
See here for the full tutorial:
http://nshipster.com/associated-objects/
It mentioned in many document's online that you can't create create new variable in category but I found a very simple way to achieve that. Here is the way that let declare new variable in category.
In Your .h file
#interface UIButton (Default)
#property(nonatomic) UIColor *borderColor;
#end
In your .m file
#import <objc/runtime.h>
static char borderColorKey;
#implementation UIButton (Default)
- (UIColor *)borderColor
{
return objc_getAssociatedObject(self, &borderColorKey);
}
- (void)setBorderColor:(UIColor *)borderColor
{
objc_setAssociatedObject(self, &borderColorKey,
borderColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
self.layer.borderColor=borderColor.CGColor;
}
#end
That's it now you have the new variable.
Why not simply create a subclass of UIViewController, add the functionality to that, then use that class (or a subclass thereof) instead?
Depending on what you're doing, you may want to use Static Category Methods.
So, I assume you've got this kind of problem:
ScrollView has a couple of textedits in them. User types on text edit, you want to scroll the scroll view so the text edit is visible above the keyboard.
+ (void) staticScrollView: (ScrollView*)sv scrollsTo:(id)someView
{
// scroll view to someviews's position or some such.
}
returning from this wouldn't necessarily require the view to move back, and so it doesn't need to store anything.
But that's all I can thinkof without code examples, sorry.
I believe it is possible to add variables to a class using the Obj-C runtime.
I found this discussion also.

Objective-C Protocols within Protocols

I recently began trying my hand at using protocols in my Objective-C development as an (obvious) means of delegating tasks more appropriately among my classes. I completely understand the basic notion of protocols and how they work. However, I came across a roadblock when trying to create a custom protocol that in turn implements another protocol. I since discovered the solution, but I am curious why the following DOES NOT work:
#protocol STPickerViewDelegate < UIPickerViewDelegate >
- ( void )customCallback;
#end
#interface STPickerView : UIPickerView
{
id < STPickerViewDelegate > delegate;
}
#property ( nonatomic, assign ) id < STPickerViewDelegate > delegate;
#end
Then in a view controller, which conforms to STPickerViewDelegate:
STPickerView * pickerView = [ [ STPickerView alloc ] init ];
pickerView.delegate = self;
- ( void )customCallback
{
...
}
- ( NSString * )pickerView:( UIPickerView * )pickerView titleForRow:( NSInteger )row forComponent:( NSInteger )component
{
...
}
The problem was that pickerView:titleForRow:forComponent: was never being called. On the other hand, customCallback was being called just fine, which isn't too surprising. I don't understand why STPickerViewDelegate, which itself conforms to UIPickerViewDelegate, does not notify my view controller when events from UIPickerViewDelegate are supposed to occur. Per my understanding of Apple's documentation, if a protocol (A) itself conforms to another protocol (B), then a class (C) that conforms to the first protocol (A) must also conform to the second protocol (B), which is exactly the behavior I want and expected.
What I ended up doing was removing the id< STPickerViewDelegate > delegate property from STViewPicker and instead doing something like the following in my STViewPicker implementation where I want to evoke customCallback:
if ( [ self.delegate respondsToSelector:#selector( customCallback ) ] )
{
[ self.delegate performSelector:#selector( customCallback ) ];
}
This works just fine, but I really am puzzled as to why my original approach did not work.
The problem was that UIPickerView already has a delegate member variable, and you were declaring another one in a subclass which was what was being set, while the superclass's delegate variable remained nil and therefore any delegate methods would not be called on the class you expect it to be called on. In this case what you did is pretty much the only way to do it; if you need to extend the given protocol do so, have a class implement that, then just set the class as the UIPickerView's delegate.
Edit: btw, awesome avatar :)
I tried to do something similar - I assume you want to group extra methods you add to the UIImagePickerControllerDelegate in one file? I was running a UIImagePicker from two places and wanted it to behave the same way without duplicating code.
What I did was to add a category to the UIViewController, like this (below.) I'm fairly new to Objective-C (have used C++ for many years) so assuredly this probably violates the spirit of how you are "supposed" to do things (extending the protocol makes more sense), but my approach accomplished what I wanted, so I thought I'd toss it out.
UIViewController+imagePickerDelegate.h:
#interface UIViewController (ImagePickerDelegate) <UINavigationControllerDelegate, UIImagePickerControllerDelegate>
-(void)configurePicker:(UIImagePickerController*)picker;
...
#end
UIViewController+imagePickerDelegate.m:
#import "UIViewController+imagePickerDelegate.h"
#implementation UIViewController (ImagePickerDelegate)
-(void)configurePicker:(UIImagePickerController*)picker
{
picker.delegate = self;
picker.allowsEditing = YES;
}
....
#end