How to Use Objective-C Categories - iphone

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.

Related

How to make inherited class be able to see parent's hidden methods in Objective-C [duplicate]

This question already has answers here:
What is the Objective-C equivalent of a public get/protected set property in C#
(3 answers)
Closed 9 years ago.
I have got two classes, Class1 and Class2, the second one inherited from the first one. I need to override -update method of Class1 to achieve my goals. The changes of -update method in inherited method are performed in the middle of code, so I can not use [super update]. Thats why I need to copy-paste original method from parent to inherited class. This method is using private methods of parent, so when I am trying to do overriding, I got warnings about absence of private methods because Class2 imports only Class1.h. To clarify, here is the code:
Class1.h:
#interface Class1 : NSObject
-(void) update;
#end
Class1.m:
#interface Class1 (Private)
-(void) private1;
-(void) private2;
#end
#implementation Class1
-(void) update
{
[self private1];
[self private2];
}
-(void) private1
{
// some code
}
-(void) private2
{
// another code
}
#end
Class2.h:
#interface Class2 : Class1
-(void) update;
#end
Class2.m:
#implementation Class2
-(void) update
{
[self private1]; // warning here
// do my own stuff between private methods, that is the reason of inheritance
[self private2]; // warning too
}
#end
Also, Class1 is not in my ownership, it is the one from open-source library (Cocos3D, to be precise), so I could not change it (and that is why I do inheritance).
The question is: how can I remove warnings? The only solution I can see is to copy private methods' signatures to Class2, but it seems to be a dirty trick. Or, it would be perfect if somebody points not how to remove warnings, but how to achieve my goal in changing method more nicely.
No need for swizzling, performSelector: or any other runtime buggery.
Just move or copy this:
#interface Class1 (Private)
-(void) private1;
-(void) private2;
#end
To the beginning of your subclass's .m file.
Normally, when trying to achieve something like an #protected scope for methods, the declaration of such would be in a header file like Class1_Private.h and that header would be set to the private role in Xcode.
As others have noted, exposing a private method via this mechanism is a bit dangerous. Given that Cocos2D is open source, that danger is mitigated somewhat or you could always just modify it directly. Of course, doing so means you effectively have a branch, which is costly to maintain.
If this is something that other developers are likely to do, I'd suggest filing a bug with Cocos2D requesting that the methods be exposed for subclassing purposes.
The way Apple does this is to make any methods that can be overridden in a subclass public, but document the fact that they shouldn't be called directly and are only there to be overridden. Documentation for Apple's code is (usually) comprehensive, and often referred to, so this works. Of course, typical third party code isn't as well documented, nor is documentation as likely to be read...
Another solution: I often create a second header called "MyClass+SubclassMethods.h", and publicly declare subclass-overrideable, but otherwise private methods in a category there. In source files for users of the base class and subclasses, this isn't imported, so the methods stay (effectively) private. In subclasses of the base class, I import "MyClass+SubclassMethods.h" to allow me to override/call these methods. It's not a perfect solution, but without the notion of 'protected' methods in Objective-C, I'm not sure there's a much better one.
A category, as suggested in another response is a good solution given that Class1 cannot be modified. If it were possible to make adjustments to source, a class extension declared in a separate header file and #imported in both the Class1 implementation, and the Class2 implementation file would be the way because then you can share not only private methods but also instance variables marked #public or #protected this way. Find out more about class extensions from here:
http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html
Briefly, a class extension is a bit like a category (sometimes called a private category) but it can only be declared in the same compilation unit where the class it extends is implemented, and it can declare (and auto-synthesize) properties and instance variables. There's a source code template included in Xcode for creating class extensions (see below.)
You should be able to solve this problem using categories to extend your original class and try method swizzling to achieve your desired behavior.

application wide function

I want to know Can I define any function application wide ? like say I need a function that gives me dynamic height of label. Where and how can define this function so that I can use it in any numbers of files ?
Also looking for good code of calculating the dynamic height of label based on text. I tried many codes from web sites but that made me confused.
Thanks..
You can create a global function by creating a .h/.m pair of files, but without declaring Objective-C classes in them.
Instead you can utilize C-style functions, like so:
//Foo.h
CGFloat GetHeight(UIView *view);
//Foo.m
CGFloat GetHeight(UIView *view) {
return view.frame.size.height;
}
Then you can include the header file in your pch file to have it included everywhere.
However I think it's more appropriate in many cases to use Category Methods instead. A category method "attaches" methods to a class.
//UIView+MyAdditions.h
#interface UIView (MyAddittions)
- (CGFloat)height;
#end
//UIView+MyAdditions.m
#implementation UIView (MyAdditions)
- (CGFloat)height {
return self.frame.size.height;
}
#end
Then you can just use it like [myView height] as long as the .h file is included at the top (or globally).
You can write a function either in Appdelegate or a static method in a custom NSObject class which has an argument NSString. And based on the length of NSString you can set the height and return the height from the method which can be used by the class that calls this method.
Usually I import such classes in the prefix header (this includes categories on objects I use application-wide). All imports in the prefix header are application wide. The prefix header in an iOS projects looks like this by default:
appName_Prefix.pch
How about making a category extending the interface of UILabel by a method returning the height of the label?
Alternatively (however, not that elegant) is to make a singleton class containing the static methods you need.

How to set up non-instantiated classes in Objective-C (Classes with just methods)

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 ;)

Objective-C sub-classing basics, how to add custom property;

I am having a issue subclassing MKPolygon.
I want to add a simple int tag property but I keep getting an instance of MKPolygon instead of my custom class, so calling setTag: causes an exception.
The problem is that MKPolygons are created using a class method: polygonWithCoordinates: count: and I dont know how to turn that into an instance of my class (which includes the tag property).
How would you go about adding a tag property to MKPolygon?
Thank you!
You should both use a category (as #Seva suggests) and objc_setAssociatedObject (as #hoha suggests).
#interface MKPolygon (TagExtensions)
#property (nonatomic) int tag;
#end
#implementation MKPolygon (TagExtensions)
static char tagKey;
- (void) setTag:(int)tag {
objc_setAssociatedObject( self, &tagKey, [NSNumber numberWithInt:tag], OBJC_ASSOCIATION_RETAIN );
}
- (int) tag {
return [objc_getAssociatedObject( self, &tagKey ) intValue];
}
#end
You may also want to look at Associative References section of the ObjC Guide, in addition to the API #hoha linked to.
Looks like developers of MKPolygon didn't make it inheritance friendly. If all you want is to add some tag to this instances you can
1) keep a map (NSDictionary or CFDictionary) from MKPolygon instance addresses to tags. This solution works well if all tags are required in the same class they are set.
2) use runtime to attach tag to polygons directly - objc_setAssociatedObject (Objective-C Runtime Reference)
I'm facing the same problem. A simple solution is to just use the Title property of the MKPolygon to save what you would save in Tag. At least in my case where I don't need an object reference but a simple number, it works
SpecialPolygon *polygon = [SpecialPolygon polygonWithCoordinates:count:];
[polygon setInt: 3];
The key is that by using the SpecialPolygon factory method instead of the MKPolygon one, you'll get the desired SpecialPolygon subclass.
Are you talking about MKPolygons created by your code, or elsewhere? If the former, just override the polygonWithStuff method. If the latter, consider a category over MKPolygon. Then all MKPolygons in your project will have a tag in them.
since it looks like the authors went out of their way to prevent you from subclassing (at least, that's one possible motivation for the public interface), consider using a form of composition:
http://en.wikipedia.org/wiki/Object_composition

Is it possible to declare a chain of super classes in interface declaration?

This may be a silly question but I haven't found any information on it.
Let's say several of the classes in my program derive from 'MySubView' which is derived from another class, UIViewController.
I would declare it like this:
#interface NewViewController : MySubView {
// code ...
}
#end
In the future the client wants a change, and desires another view with a table. So I would need to make another class, called MySubTableView, that is a UITableViewController subclassed from MySubView.
I was thinking this would be easier if I could do something like this:
#interface NewViewController : UITableViewController : MySubView {
// code ...
}
#end
But this doesn't work.
Is there a way to do this with Xcode, or do I have to specifically make the class itself?
EDIT:
I'm not looking for multiple inheritance. A straight inheritance hierarchy would follow:
NewViewController
UITableviewController
MySubView
UIViewController
No, Objective-C doesn't support declaring those kind of (vertical) inheritance chains. You can only specify the direct super class.
Even if it was possible, there would be problems like calling the correct initializers as they won't be called automatically. Consider a hierarchy like A : B : C - now you can initialize B using e.g. [super init] in As initializer, but how would B know what initializer you want it to call for C?
Objective-C doesn't support multiple inheritance... But Objective-C programmers rarely miss it, because you can accomplish many of the same tasks using Categories instead. Read up on Objective-C Categories.