I have a subclass of NSMutableArray which in fact deals with a certain type of data i.e. say Employee. The problem is I don't like the inherented names of addObject insertObject and etc. and want to change them to something like addEmployee insertEmployee.
How should I deal with this?
If you are not going to inherit the methods of the superclass then you should not use that superclass!
When you inherit it is a 'is a' relationship between the sub and super classes. "Employer is a NSMutableArray" - no, that is not true and thus don't make Employer a subclass of NSMutableArray. Additionally, in the future you might use a dictionary to store employees (like mapping 'name' -> 'employee') and then having the representation being inherited as an array simply won't work.
#interface Employer : NSObject {
NSMutableArray *employees;
}
- (void) addEmployee: (Employee *) employee;
#end
Like such. Now addObject: isn't workable on instances of Employee; only addEmployee: works. Additionally, you'll only want to specialize methods like filteredArrayWithPredicate: eventually - so it won't be an advantage to inherit them.
You add a method addEmployee: and in that call addObject:. Similar for insertObject:
You can inherit NSMutableArray and add methods like -addEmployee: then add this in your .h file:
- (void)addObject:(id)anObject __attribute__((unavailable("Use -addEmployee:")));
This is a clang extension which will cause a complier error.
References:
How do I flag a function as being deprecated in an iPhone Objective C header file?
http://clang.llvm.org/docs/LanguageExtensions.html#messages-on-deprecated-and-unavailable-attributes
Related
When you implement a category of a class in a file, will all the instances of that class be of the category by default?
I'm new to Objective-C and I'm trying to make my uneditable UITextView non-selectable. I came across this answer using a category:
https://stackoverflow.com/a/8013538/1533240
Which has the following solution:
#implementation UITextView (DisableCopyPaste)
-(BOOL) canBecomeFirstResponder
{
return NO;
}
#end
I added the snippet to my code, but it doesn't seem to be working in that I can still select the text. My declaration of the UITextView is the usual:
titleLabel = [[UITextView alloc] initWithFrame:frame];
I tried changing the declaration to [DisableCopyPaste alloc] but that didn't seem to work.. haha.
Thanks!
You misunderstand the point of categories. Categories add methods to an existing class. They must never be used to override existing methods. Doing so is undefined behavior (technically only undefined in one case, but you can't predict that case, so you must assume it applies).
If you need to override methods, you must subclass, not use categories. See the top answer to the question you linked.
When you implement a category of a class in a file, will all the
instances of that class be of the category by default?
Yes. If you create a category, the methods in that category are added to the class. For example, if you create a category on NSString that returns the checksum of a string, you can use that method on any instance of NSString.
I added the snippet to my code, but it doesn't seem to be working in that I can still select the text.
Don't use categories to override existing methods.
For one thing, it's bad form. You're effectively changing the behavior of the class in a way that the author didn't expect. For another thing, you can't count on the override to work -- the order in which categories are added to classes isn't defined, so you never know if some other category might come along and replace the method that you tried to replace. It's simply not reliable. If you need to override methods, create a subclass instead.
What you need to do is to declare category in header .h file:
such as:
#interface UITextView (DisableCopyPaste)
-(BOOL) methodName
#end
then in .m define as
#implementation UITextView (DisableCopyPaste)
-(BOOL) methodName
{
return NO;
}
#end
You can do two thing,
You can write it in a class and import that to all classes you need this functionality.
Or write these lines eachs .h and .m (respectively) you need it.
I'm looking to create a class in Objective-C for an iOS project that is focused on fetching data. I'm familiar with how classes normally work, setter and getter methods and variables. However, for this class since it's only performing a function (returning NSMutableArrays) I don't want to have to create an instance of the class to use the methods inside the class.
Any idea how I can do this neatly and efficiently?
This is a little bit atypical in Objective-C. Since classes in Objective-C can't actually have state beyond what is available to ordinary functions (i.e. there are no class variables), a class that's never instantiated is relatively useless in most cases. The normal design patterns for this kind of functionality are:
A singleton class (if you need lots of state)
A set of functions (if you don't)
You want to make class methods?
#interface Foo : NSObject {}
+(NSMutableArray*)someClassMethod:(id)params;
#end
...
#implementation Foo
+(NSMutableArray*)someClassMethod:(id)params {
// whatever implementation
return nil;
}
#end
...
NSMutableArray* array = [Foo someClassMethod:nil];
If you're only performing functions, and you don't need to support subclassing etc, why not just write them as C functions rather than a class with methods?
If this is just a class that performs some functions, you could write it as a C function.
In your header file --
NSMutableArray *functionThatReturnsMutableArray(NSObject *param1, NSString *param2);
In your implementation file --
NSMutableArray *functionThatReturnsMutableArray(NSObject *param1, NSString *param2)
{
...
return aMutableArray;
}
And that just include the .h file in your class that needs these functions and call them directly.
NSMutableArray *anArray = functionThatReturnsMutableArray(param1, param2);
Depending on what you are doing (the same NSString operations, UIView manipulations, etc), you could implement a category (I answered a question yesterday with the explanation below -- copied for your convenience ;).
Categories extend an existing class with additional methods or with your version of existing methods. For example, let's say you want to add a method that returns the first letter of a string to NSString. To do this you would create a category as follows:
Interface - JULString.h
#import NSString
#interface NSString (JULString)
-(NSString *) firstLetter;
#end
Implementation - The typical convention is that the filename of the category is the name of the class you are extending followed by “+” and the name of the category. In this case the file would be called NSString+JULString.m
#import "NSString+JULString.h"
#implementation NSString ( JULString )
- (NSString *)firstLetter
{
return [NSString stringWithFormat:#"%C", [self characterAtIndex:1]];
}
#end
The neat thing about categories is that now they extend the behavior of ANY instance of the class you are working with. In other words, any NSString in your application will have your new methods (provided that you import the proper header file of course). Beware though, as with great power comes great responsibility. Overwriting class using a category behaviors may lead to undesired effects, so be cautious.
A couple of links you may want to check are:
Apple's guide to Objective-C
Learn Objective-C
Note:
I don't have my Mac with me so I'm writing this code basically off the top of my head (and using some code from the sites above as a reminder). So I apologize in advance for any mistakes ;)
in Objective-C if I need to add additional method to UIButton I can use Categories for that. Is there any easy way (except subclassing) to add additional instance variable or property (instance variable + getter/setter).
Let's say I want to store UIColor reference in UIButton.
PS: It is mor theory question. I already implemented that using subclassing but looking for "nicer" way.
Thanks.
One solution is to use associative references; not as fast as ivars, but quite useful.
I.e. given:
#interface UIButton (color)
#property (nonatomic, retain) UIColor *myPrefixColor;
#end
You could implement this as:
#implementation UIButton (color)
static const char *assocKey = "myPrefixColor associated object key";
- (void) setMyPrefixColor: (UIColor*) aColor
{
objc_setAssociatedObject(self, &assocKey, aColor, OBJC_ASSOCIATION_RETAIN);
}
- (UIColor*)myPrefixColor;
{
return objc_getAssociatedObject(self, &assocKey);
}
#end
The myPrefix stuff is because you should never add methods to existing classes without prefixing 'em with something such that the chance of a collision with an existing method (that you aren't aware of) are minimized.
You cannot add ivars using categories.
I don't think it's possible to add ivars to classes to which you don't have the source. Two technologies come close: Categories let you add instance methods, but not instance variables, and class extensions let you add instance variables by declaring properties, but the compiler enforces the implementation of those properties' methods from within the implementation block of the class, which you don't have access to. They're designed to aid the implementation of private interfaces, not really extend classes that already exist.
I want to extend a class using category NSArray+Populate.h:
#interface NSArray (Populate)
-(NSArray *) populateArray;
#end
How can I detect the situation if there is another category (from another module or library), extending NSArray with method having the same name ?
For example, if there is a NSArray+Fill.h:
#interface NSArray (Fill)
-(NSArray *) populateArray;
#end
As I understand, the run-time engine will choose one of the version silently, without any crash ?
You cannot detect this situation and you cannot determine which implementation of -populateArray takes precedence. Some developers prefer to prefix their category method names for this reason.
Quoting The Objective-C Programming Language document,
A category cannot reliably override methods declared in another category of the same class.
This issue is of particular significance since many of the Cocoa classes are implemented using categories. A framework-defined method you try to override may itself have been implemented in a category, and so which implementation takes precedence is not defined.
First off, I despise singletons with a passion. Though I should probably be trying to use one, I just don't want to. I want to create a data class (that is instantiated only once by a view controller on loading), and then using a different class, message the crap out of that data instance until it is brimming with so much data, it smiles.
So, how do I do that? I made a pointer to the instance of the data class when I instantiated it. I'm now over in a separate view controller, action occurs, and I want to update the initial data object. I think I need to reference that object by way of pointer, but I have no idea how to do that. Yes, I've set properties and getters and setters, which seem to work, but only in the initial view controller class.
There's no need to use a singleton if you don't like the pattern or if it doesn't fit. Assuming you are creating your second view controller in the first one, just declare an ivar and property for your model object in your second view controller and when you instantiate it, assign the model object to this property.
Make a global variable for your object and store it there on creation. You can wire that up in the init method (probably bad style), or from the caller or via interface builder. Just make your variable known in the files that use it.
Or - well - use some kind of singleton pattern and get the instance directly from that class. Looks much cleaner.
Seriously use a singleton. In case you don't like them cause you don't know the code:
#interface Order : NSObject {
NSMutableArray *order;
}
#property (nonatomic, retain) NSMutableArray *order;
+ (Order *)sharedInstance;
#end
#import "Order.h"
#implementation Order
#synthesize order;
+(Order *)sharedInstance {
static Order *myInstance = nil;
#synchronized(self) {
if(!myInstance) {
myInstance = [[Order alloc] init];
}
}
return myInstance;
}
-(void)dealloc {
[order release];
[super dealloc];
}
#end
Um. Hello. Isn't Core Data a good enough framework for you? It allows you to have a single persistent store and multiple contexts to manage updates and merging of changes in response to notifications. I may be out of line here, but seeing how you start the question with a strong opinion about a well accepted pattern in your first question indicates that you have not spent much time discovering the ways in which the objective c runtime and Foundation classes in iOS can collaborate to achieve a task. In any software, one object and only one object owns a specific resource. You should embrace singletons. I suggest you spend some time reading http://www.cocoadesignpatterns.com/. Oh yeah, check out the meaning of KVO.
Why not make it a property of your app delegate? That way you don't have to use the singleton pattern but you are taking advantage of Apple's already existing usage of the singleton pattern.
Don't forget that Objective-C is a superset of C.
Basically, a data class is a plain C struct.
If you want to access a variable of that class from another class, make it global.
mydata.h:
struct MyData {
// Whatever data fields that you need, e.g.:
NSInteger value;
};
extern struct MyData mydata;
mydata.c:
struct MyData mydata = {
// Whatever initial value that you find relevant, e.g.:
.value = 42;
};