How to use the same category in multiple classes? - objective-c

I have stack category for NSMutableArray that I use in Class A
#implementation NSMutableArray (StackUtil)
- (void)push:(id)stackObject {
[self addObject:stackObject];
}
- (id)pop {
id retVal = [[self lastObject]retain];
[self removeLastObject];
return [retVal autorelease];
}
#end
How do I properly include this category for for Class B which is unrelated to Class A? Also I noticed that when I #import Class A into the header for Class C the category methods work, but I get the " object may not respond to push message" warning. Could someone clear up how categories are "reused" and why they have names (StackUtil in this case), and how they are used.

You should have a corresponding #interface NSMutableArray (StackUtil) in a header file that declares the category. Importing that header should be enough to confer use of your new methods onto any NSMutableArray in the scope of the import.
#interface NSMutableArray (StackUtil)
- (void) push:(id)stackObject;
- (id) pop;
#end
Certainly the #interface, and usually the #implementation, should be in files of their own, independent of your classes A, B and C, since they are general purpose additions, not something belonging to one of those client classes.

Related

How can I grant certain classes to set my properties in Objective-C?

Two questions:
How can I allow only certain class to update a property? I tried using category but the NotFriendly class is still able to set the age property via Key-Value.
How come I get the "-[Person setAge:]: unrecognized selector sent" error when I tried to the age property during runtime?
Thanks,
Person.h
#interface Person
#property (readonly) NSString *age;
#end
Person.m
#implementation Person
#synthesize age;
#end
Person+Exclusive.h
#interface Person
#property (readwrite) NSString *age;
#end
Person+Exclusive.h
#implementation Person (Exclusive)
#end
Friendly.m
#import "Person+Exclusive.h"
#interface Friendly
- (void)viewDidLoad {
[super viewDidLoad];
Person *aPerson = [[Person alloc] init];
aPerson.age = #"12"; // -[Person setAge:]: unrecognized selector sent to instance 0x174013e90
}
NotFriendly.m
#import "Person.h"
#interface Friendly
- (void)viewDidLoad {
[super viewDidLoad];
Person *aPerson = [[Person alloc] init];
[aPerson setValue:#"1000000" forKey:#"age"]; // how can I stop this
}
You can't reliably. Any method added to an Objective-C class is visible to everyone in the runtime; available methods are how key-value coding works.
If it's just KVC you want to block then you can override setValue:forKey: and decline to act if the names key is on your blacklist; otherwise call up to super. But classes will still be able to performSelector:withObject: or even drop down to the C runtime, the latter of which you can ultimately do nothing about.
If you are in a position where Person can be responsible for creating those objects that are permitted to talk to it then it can demand an instance as an argument to the setter and continue only if that instance is one you created. Keep a list in one of the weak collections of everything you instantiated.
In any detected failing case, by whatever means, you can manually raise an NSException to create the type of failure you desire.
If it's just for debugging builds and you don't mind a bunch of hassle, you can call [NSThread callStackSymbols] and parse the result to find out which type of class is calling. The text returned has no formally defined format though it's been fairly stable until now; it's explicitly not reliable for release builds. But it shouldn't be too much work to keep it working within debug builds as a diagnostic tool.
Not sure why you would want to do this, but it is an interesting question.
I wonder if you could create a method in person that takes two arguments, age and class. Then when you call it, you use [self class] to pass in the class. Something like this:
- (void)checkTheClassBeforeSettingAge:(NSString *)age withClass:(NSClass *)class {
if (class isKindOfClass:[validClass class]) then {
self.age = age;
} else {
// Error handling code
}
}
Call it with:
checkTheClassBeforeSettingAge:#"12" withClass:[self class];

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.

Class Name with a "+"

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

In Objective-C Can you define a class in two different files?

I know one file can define many different classes. What about the other way around? Can one class is defined in several different files?
Say you want to add method or property of classes you wrote (rather than the framework classes). Can you do that?
Notice I do not want to change original .m file and I want to add property which is something category cannot do.
A class can only have one #implementation block, so no you can not define a class in multiple files.
If there is some reason you can't add code to the original class #implementation, the alternatives are subclassing or categories.
Not directly.
Alternatively:
nicely - create category in other file
not nicely - #include other file in main implementation file.
Yes you can do that with categories.
//File1.h
#interface Object : NSObject
-(void)method;
#end
//File1.m
#implementation Object
-(void)method
{
NSLog (#"hello");
}
#end
//File2.h
#interfacae Object(ObjectExtention) //How you declare a category
-(void)methodTwo;
#end
//File2.m
#implementation Object(ObjectExtention)
-(void)methodTwo
{
NSLog (#"Categorie Method");
}
#end
//File3.m
#import "File1.h"
#import "File2.h"
int main()
{
Object obj = [[Object alloc] init];
[obj method]; //Declared in first file
[obj methodTwo]; //Declared in second file where we had our categorie defined
return 0;
}
You can do this with methods, however, you can't add more instance variables to a object like a categorie.

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.