Passing setters/getters of an instance variable up to a class - objective-c

I'm working on creating a UIView subclass (which I'm calling MarqueeLabel) that animates a subview UILabel ivar in a marquee fashion when the UILabel text is too long for the containing view.
I was hoping to have a clean implementation, without having to write methods in my MarqueeLabel class just to set/retrieve all the the standard UILabel (text, font, color, etc) instance variables of the UILabel ivar. I've found a way to do this with message forwarding - all unrecognized methods sent to MarqueeLabel are passed on to the UILabel ivar. In my case the methods unrecognized by MarqueeLabel are the methods typically used with UILabel.
There are some problems with that approach though:
1. You have to use [marqueeLabel setText:#"Label here"], rather than marqueeLabel.text
2. The compiler gives warnings on the above line, because:
'MarqueeLabel' may not respond to '-setText:'
which I would know to ignore but would annoy anyone else.
To avoid these problems, is there any way to "bring forward" the methods an ivar so that they're accessible to something using the class while still acting upon the ivar object?
Thanks!
Note: The way I've set this up may not be the best way to do it either. Perhaps subclassing or class continuing UILabel would be better, but I wasn't able to grasp how the animation + clipping (when the text scrolling off moves out of containing UIView and disappears) could be done using those methods.
Note 2: I know you can use marqueeLabel.subLabel.text where subLabel is the subview UILabel. And this may be the direction I take, but might as well see if there's a better solution!

For properties, you could define a property in the interface and use #dynamic in the implementation so that you can don't have to create stub implementations. Make sure you also override valueForUndefinedKey: and setValue:forUndefinedKey: and forward to your label.
For any methods which are not part of a property, you can use a category to declare the method without implementing it. This will get rid of warnings but still use the builtin forwarding.
//MarqueeLabel.h
#import <UIKit/UIKit.h>
#interface MarqueeLabel : UIView {}
#property (nonatomic, copy) NSString *text;
#end
#interface MarqueeLabel (UILabelWrapper)
- (void)methodToOverride;
#end
//MarqueeLabel.m
#import "MarqueeLabel.h"
#implementation MarqueeLabel
#dynamic text;
- (id)valueForUndefinedKey:(NSString *)key {
return [theLabel valueForKey:key];
}
- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
[theLabel setValue:value forKey:key];
}
#end

Related

Synthesis and Protected Instance Variables in "Modern" Objective-C?

I want to create a class that serves as a base (or "abstract") class to be extended by subclasses. The best way I can explain what I'm talking about is with a few examples. Here's a possible interface for my superclass:
#import <Cocoa/Cocoa.h>
#import "MyViewControllerDelegate.h"
#interface MyViewController : NSViewController
#property (nonatomic, weak) id<MyViewModeControllerDelegate> delegate;
#property (nonatomic, copy) NSArray *content;
#end
Writing it like that seems nice and clean, but I can't access the ivars from my subclasses.
After doing some research, I've concluded that a good way to provide subclasses with direct access to ivars is to use the #protected directive and include any declarations in the header file so subclasses can see it:
#import <Cocoa/Cocoa.h>
#import "MyViewControllerDelegate.h"
#interface MyViewController : NSViewController {
#protected
__weak id<MyViewControllerDelegate> _delegate;
NSMutableArray *_content;
}
#property (nonatomic, weak) id<BSDViewModeControllerDelegate> delegate;
#property (nonatomic, copy) NSArray *content;
#end
I personally don't have an issue with that, and it seems to work the way I want it to (e.g. subclasses can access the ivars directly, but other classes have to use accessors). However, I read blog posts or Stack Overflow answers every day that say instance variables should just be synthesized, or "I don't even touch instance variables anymore."
The thing is, I started learning Objective-C post-ARC, so I'm not fully aware of the ways in which developers had to do things in the past. I personally like the control I have when I implement my own getters/setters, and I like being able to actually see instance variable declarations, but maybe I'm old school. I mean, if one should "just let the compiler synthesize the instance variables," how does one include any sort of logic or "side effects" without implementing a bunch of KVO?
For example, if my instance variables and getters/setters are synthesized, how do I initialize stuff lazily? For example, I sometimes like to do this:
- (NSArray *)myLazyArray
{
if ( _myLazyArray == nil ) {
self.myLazyArray = #[];
}
return _myLazyArray.copy;
}
Or how do I make sure that a value being set isn't the same as the currently set value? I'll sometimes implement a check in my mutator method like this:
- (void)setMyLazyArray:(NSArray *)array
{
if ( [array isEqualToArray:_myLazyArray] )
return;
_myLazyArray = array.mutableCopy;
}
I've read all of Apple's documentation, but half their docs date back to 2008 (or worse in some cases), so I'm not exactly sure they're the best place to get information on the matter.
I guess the gist of my question is this: Is there a preferred "modern" way of handling instance variables, variable synthesis, inheritance, scope, etc. in Objective-C? Bonus points for answers that don't include "Bro, Swift." or "You aren't using Swift?"
Any guidance would be much appreciated. Thanks for reading!
Why do your subclasses need access to your ivars? Ivars are an implementation detail and subclasses shouldn't be concerned with that. There could be all sorts of side effects if the parent class is doing logic in the property setter/getters. Therefore, always access them through the property.
Assuming this is in your subclass and you are overriding a property getter:
- (NSArray *)myLazyArray
{
if ( super.myLazyArray == nil ) {
// do what you need to do to populate the array
// assign it to yourself (or super)
self.myLazyArray = #[];
}
return super.myLazyArray;
}
And then for the setter:
- (void)setMyLazyArray:(NSArray *)array
{
if ( [array isEqualToArray:super.myLazyArray] )
return;
super.myLazyArray = array.mutableCopy;
}

Proper way to perform additional code when setting properties

This might seem like a basic question but I'm still getting a handle on properties so please bear with me.
I have a custom NSView subclass that does its own drawing. I've set up support for different styles with a #property for setters and a typedef enum for human-readable integers. It works great, but the view won't redraw after setting its style unless I manually call setNeedsDisplay:YES on the control or resize its parent window.
Logically one would think the solution would be to simply do a [self setNeedsDisplay:YES] in the classes' setStyle: method, but I cannot for the life of me figure out how to properly do it. Whenever I try to override setStyle: it just complains, "Writable atomic property 'style' cannot pair a synthesized getter with a user defined setter".
What should be done in this situation?
Ideally, you would just declare your actual ivar/storage as a private property, then manually implement the setter setStyle:. In the implementation of setStyle:, set your private property/state, and perform your updates. So you just abstract the data from the client's interface. There are other ways to approach this, such as directly setting the ivar.
So an implementation may take the form:
MONThing.h
#interface MONThing : NSObject
- (void)setStyle:(t_style)pStyle; // << the client's interface
#end
MONThing.m
#interface MONThing ()
#property (nonatomic, assign, readwrite) t_style userStyle; // << the actual storage
#end
#implementation MONThing
- (void)setStyle:(t_style)pStyle
{
// validate parameter
// set our data
self.userStyle = pStyle;
// perform effects
[self setNeedsDisplay:true];
}
Over time, you will learn multiple ways to accomplish this, and when you would favor one over the other.
If you a setting your own setter then do not use #synthesize and #property. These are for automatic creation of the setter and getter methods. Declaring the variable in the interface file is enough.
Take a look at this question. To copy over the answer from the other question:
If you declare a #property to be atomic then do one of the following:
use #dynamic or;
use #synthesize and keep the synthesized setter and getter or;
provide a manual implementation of both the setter and the getter (without using one of the above directives).

Defining a property in iOS class extension

I would like to add a property to UITableView in a Class Extension:
#interface UITableViewController ()
#property NSString *entityString;
#end
Then I import the extension and then I use entityString property in a subclass of UITableViewController:
#implementation CustomerTableViewController
- (void)viewDidLoad {
self.entityString = #"Customer";
...
[super viewDidLoad];
}
...
Apple documentation says:
the compiler will automatically synthesize the relevant accessor
methods (...) inside the primary class
implementation.
But when I try to execute it I get this error:
-[CustomerTableViewController setEntityString:]: unrecognized selector sent to instance 0x737b670
What am I doing wrong? maybe the property cannot be accessed by subclasses?
Try using a category with Associative References instead. It is much cleaner and will work on all instances of UIButton.
UIButton+Property.h
#import <Foundation/Foundation.h>
#interface UIButton(Property)
#property (nonatomic, retain) NSObject *property;
#end
UIButton+Property.m
#import "UIButton+Property.h"
#import <objc/runtime.h>
#implementation UIButton(Property)
static char UIB_PROPERTY_KEY;
#dynamic property;
-(void)setProperty:(NSObject *)property
{
objc_setAssociatedObject(self, &UIB_PROPERTY_KEY, property, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(NSObject*)property
{
return (NSObject*)objc_getAssociatedObject(self, &UIB_PROPERTY_KEY);
}
#end
//Example usage
#import "UIButton+Property.h"
UIButton *button1 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button1.property = #"HELLO";
NSLog(#"Property %#", button1.property);
button1.property = nil;
NSLog(#"Property %#", button1.property);
A class extension is used to declare additional interface -- methods and properties -- whose implementation contract will be met within the class's primary #implementation.
Which is exactly why you can't add storage -- add ivars -- via a class extension. A class extension is an interface, no more, no less. #synthesize is what creates storage for #property declarations, but #synthesize of an #property can only appear in the #implementation of the class (whether explicitly or as a default behavior of the compiler).
Since you can't recompile the framework class, you can't add ivars to it.
#prashat's answer is one way to add storage to an existing class. However, going that route is generally undesirable; hanging state off of framework classes willy-nilly is a sign of poor design and will make your application significantly more difficult to maintain over time.
Far better to revisit your design, understand why you currently require attaching state to an object that can't directly contain it, and refactoring that requirement away.
The docs state:
Class extensions are like anonymous categories, except that the methods they declare must be implemented in the main #implementation block for the corresponding class.
When you use #property, it is roughly equivalent to declaring accessor methods. So this means you can only do such a thing if you are also the author of the "main" #implementation block of the class, which with UITableViewController, you are not.
Your only option here is Categories, which cannot add instance variables.
The docs link, and note the very last line of that page:
The implementation of the setValue: method must appear within the main #implementation block for the class (you cannot implement it in a category). If this is not the case, the compiler emits a warning that it cannot find a method definition for setValue:.

Why do I declare a #property when I can use an inline variable instead?

I have spent a few days learning Objective-C and have a few questions about #property. I have experience with C# so understand the need for pointers, initialization etc.
So as an example:
#interface MyClass : NSObject
{
IBOutlet UIImageView *image;
}
#property (retain, nonatomic) UIImageView *image
#end
#implementation MyClass
#synthesise image
#end
I understand that #synthesise is used to create the #property. But I have a few questions just to help me clear things up:
Does the #property duplicate or replace my original definition, or does it merely set up the mutibility and atomicity of the original?
Does #synthesise remove my need to use image = [[UIImageView alloc] init]?
If I do not provide a #property and still go ahead creating and destroying my variable manually, does that make any difference?
Ultimately, is the difference between the 2, #property gives you more flexibility with regards to memory management and multi-threading and the normal one gives you the defaults.
Does the #prototype duplicate or replace my original definition, or does it merely set up the mutibility and atomicity of the original?
The ivar declaration of image is redundant when using the most recent compiler releases.
The former declares an ivar (type + name + instance storage).
The property declaration specifies the type, name, storage (in more recent compiler releases), declares the accessor methods (e.g. - (UIImageView *)image; and - (void)setImage:(UIImageView *)pImage;), and other property specifiers (which are used when the accessors are generated by the compiler).
Does #synthesise remove my need to use image = [UIImageView alloc]?
No. You still need to implement your initializer and dealloc (in MRC) appropriately.
If I do not provide an #property and still go ahead creating and destroying my variable manually, does that make any difference?
That would be fine, when you do not want/need boilerplate accessor methods generated for you. It's a design choice. Not every ivar needs accessor methods.
Ultimately, is the difference between the 2, #property gives you more flexibility with regards to memory management and multi-threading and the normal one gives you the defaults.
The biggest reason they exist is convenience. Properties save a lot of boilerplate code.
There is no more flexibility with properties -- properties implement the most practical uses.
It's infrequent that atomicity (in this context) is equivalent to proper thread safety and correct concurrent execution.
1) The property does not replace the class member. A property is a declaration that you want the accessors (getter and setter) for a class member to perform certain "automatic" tasks and have a certain name.
For example:
#interface MyClass : NSObject
{
NSInteger __myInt;
}
#property (assign) NSInteger myInt;
#end
#implementation MyClass
#synthesize myInt=__myInt;
#end
The above code, for all intents and purposes, is causing the following methods to be automatically generated at compile time:
-(NSInteger) myInt
{
return self->__myInt;
}
-(void) setmyInt:(NSInteger)val_
{
self->__myInt = val_;
}
Of course, what happens "in the background" when Xcode compiles your program is a bit different and more nuanced, but this is basically what happens.
2) I'm not entirely clear what you mean by this one... You always need to alloc and init your variables, regardless of accessor synthesis.
3) No. Properties/synthesis are only needed for a) convenience, be it syntactic or atomicity for multithreading, and b) external access to members inside your class.
EDIT:
To clarify on multithreading and properties, declaring a property nonatomic does a great deal for thread safety. This, and my response to #3, addresses your last concern in your question.
You can do this:
#interface MyClass : NSObject
#property (retain, nonatomic) IBOutlet UIImageView *image;
#end
#implementation MyClass
#synthesize image;
#end
Does the #prototype duplicate or replace my original definition, or does it merely set up the mutibility and atomicity of the original?
The property adds things on-top of the ivar like KVO and thread safety if it's atomic.
Does #synthesise remove my need to use image = [UIImageView alloc]?
No
If I do not provide an #property and still go ahead creating and destroying my variable manually, does that make any difference?
If you don't make a property you lose out on the things a property gets you like KVO, it's a judgment and api call on how the variable will be used. Under arc it is much easier to use straight up ivars because you don't have to replicate the retaining and releasing the property did automatically.
The 'image' in #property (retain, nonatomic) UIImageView *image line is just a name of the property and IBOutlet UIImageView *image; is an ivar which you access through self.image. I always name an ivar for property the same as the name but add _ :
UIImage * image_;
#property (retain, nonatomic) UIImageView *image;
#synthesize image = image_;
If you will not create an ivar for your property the Xcode do it automatically for you (the name of the ivar will be the same as the name of property)

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.