I’m looking into ways to add a property (an integer in this case) to all UIView instances, whether they are subclassed or not. Is using objc_setAssociatedObject() and objc_getAssociatedObject() within a category the appropriate, Apple-endorsed way to do this?
I have heard some concerns that this constitutes a “runtime hack,” and can lead to problems that are difficult to track down and debug. Has anyone else seen this type of problem? Is there a better way to add an integer property to all UIView instances without subclassing?
Update: I can’t just use tag, because this needs to be used in a code base that already uses tag for other things. Believe me, if I could use tag for this, I would!
Associated objects come in handy whenever you want to fake an ivar on a class. They are very versatile as you can associate any object to that class.
That said, you should use it wisely and only for minor things where subclassing feels cumbersome.
However, if your only requirement is to add an integer to all UIView instances, tag is the way to go. It's already there and ready for you to use, so there's no need for involving run-time patching of UIView.
If you instead want to tag your UIView with something more than an integer, like a generic object, you can define a category like follows.
UIView+Tagging.h
#interface UIView (Tagging)
#property (nonatomic, strong) id customTag;
#end
UIView+Tagging.m
#import <objc/runtime.h>
#implementation UIView (Tagging)
#dynamic customTag;
- (id)customTag {
return objc_getAssociatedObject(self, #selector(customTag));
}
- (void)setCustomTag:(id)aCustomTag {
objc_setAssociatedObject(self, #selector(customTag), aCustomTag, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#end
The trick of using a property's selector as key, has recently been proposed by Erica Sadun in this blog post.
Use tag. That's what it was meant for.
Related
I have some forms for the authentications and signup views and I want that all UITextField inside those forms have a UIButton as an accessory view, just above the keyboard. I want to have the possibility to set the title and the action for this button
Because I want all those text field have one and each will have a title and an action, and to avoid redundancy, I thought about a protocol.
I want something like extending a custom protocol, for example UITextFieldAccessoryViewDelegate and to be conform to some functions like :
-buttonAccessoryView title ... -> String
-didClickOnAccessoryViewButton.. -> ()
My mind is closed. Someone can give me some ideas to do what I want ?
You could use associated objects to solve this problem. This lets you add a property and its synthesized getter/setters.
#interface UITextField(AccessoryButton)
#property(readwrite, strong, nonatomic) UIButton *accessoryButton;
#end
#import<objc/runtime.h>
#implementation UITextField(AccessoryButton)
-(UIButton*) accessoryButton
{
objc_getAssociatedObject(self, #selector(accessoryButton));
}
-(void) setAccessoryButton:(UIButton *)accessoryButton
{
objc_setAssociatedObject(self, #selector(accessoryButton), accessoryButton, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
Include this category into your forms that need the UIButton for the text fields. Then assign action and title to a UIButton like how you normally do.
Considering everything you want to do I think you are better off using a subclass of UITextField rather than an extension.
Extensions can't add instance variables to the objects they extend, but subclasses can. As lead_the_zeppelin points out in his answer, you can use associated objects to simulate adding instance variables with an extension but it seems like you're making it needlessly complicated when a subclass gives you everything you want.
I am using a a library which has quite complicated inherited structure (it consists of a couple of classes, inherited e.g. UITableViewController, UIView, UITableViewCell and others, where some of the classes are used to create custom objects.
I need to add some functionality (to be precise, to implement tap gesture recognizers). The easy solutions is to put a few lines of code into some of the classes of the library.
Generally, I like to have my code separated from libraries code. Is it possible somehow "override" these classes without rewriting them, or to add some extension?
Or the only idea is to write overrides of all the classes generating own classes and tons of useless code?
Or simply add own code to the library?
Additions:
It seems, that categories are right direction, but not particularly, what I want. Here's what I want exactly:
I have a in Class1:
- (void)someMethod {
doThis;
}
And without subclassing // editing the class I would like to transform this to:
- (void)someMethod {
doThis;
andThisToo;
}
Categories add other methods to a class, while I need to add some functionality in already existing method.
You need to use the decorator pattern.
The decorator pattern is used to extend or alter the functionality of
objects at run- time by wrapping them in an object of a decorator
class. This provides a flexible alternative to using inheritance to
modify behaviour.
Since the example you give is pretty sketchy, I can't provide a code example, but check out Wikipedia (http://en.wikipedia.org/wiki/Decorator_pattern)
EDIT
I thought about what you gave and here would be an example of a decorator:
#interface Decorator
#property (strong, nonatomic) id decoratedObject;
- (void)someMethod;
#end
#implementation Decorator
- (void)someMethod {
[self.decoratedObject doThis];
[self andDoThisToo];
}
#end
I have a UIButton object in my program.
I want to use it like follows
myButton.setImage:blablabla
mybutton.title:.......blabla
...
...
myButton.placeTextBelowImageWithSpacing:12
While calling my method "placeTextBelowImageWithSpacing:12" it must set the image and text accordingly. I have the method ready with me. How can i use it in the above way.
PS: I hate subclassing.
Thanks in Advance
Create a custom subclass of UIButton. I created a button called FinderButton that has an image and a title centered below it. It works great.
If you hate subclassing then you might want to think about a different line of work.
Being an Objective C programmer that hates subclassing is a bit like being a surgeon who hates blood or a farmer who hates dirt. Defining a class hierarchy is one of the main tools for doing development in an OO language like Objective-C.
You can do this by creating a UIButton category:
UIButton+MyCustomMethod.h
#interface UIButton (MyCustomMethod)
- (void)placeTextBelowImageWithSpacing;
#end
UIButton+MyCustomMethod.m
#implementation UIButton (MyCustomMethod)
- (void)placeTextBelowImageWithSpacing
{
// ...
}
#end
You can't. That isn't valid syntax in Objective-C. The closest you can get to that would be to explicitly declare new properties on UIButton that followed your naming convention. Using them would then look like:
myButton.setTitle = #"something"
Then you could override setTitle's setter (setSetTitle:), and making it call setTitle:forControlState:, which I'm assuming is your goal.
But this should only be done through subclassing (learn to love it, it's a big part of OOP), although if you really really want to, you can add the properties in a category using the Objective-C runtime objc_setAssociatedObject() function more info here: Objective-C: Property / instance variable in category
I have used #protocols many times but I think I have been doing it wrong all the time. They have worked always well, but now I want to improve my level so I am trying to do it the better I can.
I always have created a delegate like this:
#protocol CommentViewDelegate;
#interface LZCommentView : UIView
#property (assign, nonatomic) id <CommentViewDelegate> delegate;
#end
#protocol CommentViewDelegate
-(void)showAndHideCommentView;
#end
Now, I have seen that almost all the delegate methods that I see send their own object. Something like this:
-(void)showAndHideCommentView:(LZCommentView *)commentView;
What is the difference between what I did and this? Is one of them better than the other? I have seen that almost everyone who does this, does not use the object in the ViewController.
Another question is, should I use <NSObject> in the #protocol definition?
And the last one, what is better create the #property with assign or strong?
Thank you
Generally, the object that you pass to the delegate can be used so that the same delegate class can be used in different contexts. This gives you more flexibility in cases when a delegate class could potentially be reused.
For example, if showAndHideCommentView needs to interact with a view being shown or hidden, it has two ways of doing it:
Get the view as an argument, or
Reference the view directly, knowing that this delegate is attached to a particular view.
Example of the first approach:
#implementation MyDelegate
-(void)showAndHideCommentView:(LZCommentView *)commentView {
if (isShowing) {
[commentView doSomething];
}
}
#end
Example of the second approach:
#implementation MyDelegate
-(void)showAndHideCommentView {
if (isShowing) {
[self.commentView doSomething];
}
}
#end
The first approach is more flexible than the second one, because it lets you reuse the same code.
According to Apple, it’s best practice to define your protocols to conform to the NSObject protocol, so the answer to your second question is "yes".
As far as the third question goes, the best way to declare delegate properties is with the weak attribute to avoid retain cycles.
1) You should always make your protocol conform to the NSObject protocol. This lets you make use of all of the methods in that protocol.
#protocol CommentViewDelegate <NSObject>
2) Unless you have a good reason to do otherwise, most properties for delegates should be defined as weak. This avoids reference cycles and it ensure the delegate is automatically set to nil if the delegate object is deallocated.
#property (nonatomic, weak) id<CommentViewDelegate> delegate;
3) It's best to include the object in the protocol methods because it offers the most flexibility. It also allows a class to be the delegate of more than one instance. Then the class can tell which instance the protocol method is being called for. Think of a view controller handling multiple buttons or having two or more table views.
My app subclasses UICollectionViewFlowLayout and uses all of its properties except for minimumLineSpacing. To avoid confusion, I'd like to be able to "hide" minimumLineSpacing from the outside, so it looks like my subclass doesn't even support it. Is this possible?
Yes you can. Kind of. You can mark it with __attribute__((unavailable)), which will cause the compiler to throw an error if you use it. However, the property will still be accessible if your object is cast to its superclass type, as this is a compile-time-only thing.
#interface MyClass : UICollectionViewFlowLayout
#property (nonatomic) CGFloat minimumLineSpacing __attribute__((unavailable));
#end
I don't think you can actually hide it. You could of course overwrite the getter and setter and prevent the acutal value from beeing changed, if that is of importance. But they will always exist and be visible.