Does anyone know of a way to add additional attribute types to the #property keyword without modifying the compiler? Or can anyone think of another way to genericize getter/setter creation?
Basically, I have a lot of cases in a recent project where it's handy for objects to lazily instantiate their array properties. This is because we have "event" objects that can have a wide variety of collections as properties. Subclassing for particular events is undesirable because many properties are shared, and it would become a usability nightmare.
For example, if I had an object with an array of songs, I'd write a getter like the following:
- (NSMutableArray *)songs {
if (!songs) {
songs = [[NSMutableArray alloc] init];
}
return songs;
}
Rather than writing dozens of these getters, it would be really nice to get the behavior via...
#property (nonatomic, retain, lazyGetter) NSMutableArray *songs;
Maybe some fancy tricks via #defines or something? Other ideas?
You can always use macros. Even if you modified the compiler, you would probably still want to do this in #synthesize instead of #property, since there is no need to publish this implementation detail. And with a macro it is easy to use any init method. Unfortunately the macros are not aware of the getter= property attribute.
#define synthesizeLazyGetterWithInit(PROPERTY,TYPE,INIT)\
-(TYPE *) PROPERTY { if ( !PROPERTY ) { PROPERTY=[[TYPE alloc] INIT]; } return PROPERTY; }
#define synthesizeLazyGetter(PROPERTY,TYPE)\
synthesizeLazyGetterWithInit(PROPERTY,TYPE,init)
#implementation MyClass
synthesizeLazyGetter(songs,NSMutableArray)
synthesizeLazyGetterWithInit(other,NSMutableArray,initWithCapacity:0)
#end
Edit:
#define synthesizeLazyGetterOptional(PROPERTY,TYPE,INIT);\
-(TYPE *) PROPERTY:(BOOL)inAllocate { if ( !PROPERTY && inAllocate ) { PROPERTY=[[TYPE alloc] INIT]; } return PROPERTY; }\
-(TYPE *) PROPERTY { return [self PROPERTY:YES]; }\
-(BOOL) PROPERTY##Initialized { return nil != PROPERTY; }
Related
I'm new to Objective C, and am using the Stanford CS193P course for the basics. In the first lecture, according to what I understood from it, anytime I declare a property in the header file, Objective C auto-generates the setter and getter for the property, where the getter name is the same as the property name. The professor also mentioned about the #synthesize keyword, which is used to set something as the instance variable name, like so #synthesize card = _card.
So now, any changes can be made to the property by using _card directly as well.
However, he mentions many times that all this happens behind this scenes and none of this appears to us in the implementation file.
If that's the case, then in the code below:
//
// PlayingCard.h
// CardGame
//
// Created by Manish Giri on 9/19/15.
// Copyright (c) 2015 Manish Giri. All rights reserved.
//
#import "Card.h"
#interface PlayingCard : Card
#property (nonatomic,strong) NSString *suit; //one of club, heart, spade, diamond
#property (nonatomic) NSUInteger rank; //numbers from 0 through 13
#end
//
// PlayingCard.m
// CardGame
//
// Created by Manish Giri on 9/19/15.
// Copyright (c) 2015 Manish Giri. All rights reserved.
//
#import "PlayingCard.h"
#implementation PlayingCard
//#synthesize suit = _suit;
//override the getter method "contents" to return the description- rank&suit of the playing card
-(NSString *) contents {
//return [NSString stringWithFormat:#"%lu %#", (unsigned long)self.rank, self.suit];
//if rank is 0 or not set, return ?
NSArray *rankStrings = #[#"?", #"1", #"2", #"3", #"4", #"5", #"6", #"7", #"8", #"9", #"10", #"11", #"12", #"13"];
return [rankStrings[self.rank] stringByAppendingString:self.suit];
}
-(NSString *) suit {
//return ? if suit is nil or not set
return _suit?_suit:#"?";
}
-(void) setSuit:(NSString *)suit {
//check the suit to be set is valid
if([#[#"♥︎", #"♦︎", #"♠︎", #"♣︎"] containsObject:suit]) {
_suit = suit;
}
}
#end
Why do I get the error:
Use of undeclared identifier _suit. Did you mean suit?
After I got this error, I added the line #synthesize suit = _suit, and the error was fixed, but isn't this done automatically? If not, what's the difference?
The professor, most certainly, did not have the #synthesize line in his code (in the slide) while still using _suit.
You have defined your own getter/setter and so have turned off this automatic generation of the instance variable. You will need to add your own instance variable:
#implementation PlayingCard () {
NSString *_suit;
}
#end
#implementation PlayingCard
...
#end
I would also strongly suggest you use an enum for the suits as #"♥︎", #"♦︎", #"♠︎", #"♣︎" are presentation formats and are less useful to your code. For example:
// This should be in the header file
typedef enum {
SUIT_NONE,
SUIT_HEARTS,
SUIT_DIAMONDS,
SUIT_SPADES,
SUIT_CLUBS
} Suit;
#implementation PlayingCard () {
Suit _suit;
}
#end
It's now much easier and efficient to do:
if (_suit == SUIT_CLUBS) { ... }
than:
if ([_suit isEqualToString:#"♣︎"]) { ... }
Enums can also be used in switch statements and you will also find it easier for code using this class as well, for example:
if (cardsOnTable[i].suit == _cardsInHand[j].suit) {
points++;
}
It is done for you, unless you explicitly implement both the setter and getter methods yourself. I think you should also have seen a compile warning that this was happening on the property definition.
When you specify a property you're defining the thread and memory management, but if you then implement the accessor methods there is no guarantee you actually follow those rules. In this case you might actually be using some other memory all together so you need to tell the compiler what you want it to do.
All that magic is gone once you override a getter method. When you override it, you have to manually declare ivar backing the property or use #synthesis which essentially does the same. You can still define custom setter though and find that ivar is still there.
When using associated objects, an Objective-C runtime feature available starting from iOS 4 and OSX 10.6, it's necessary to define a key for storing and retrieving the object at runtime.
The typical usage is defining the key like follows
static char const * const ObjectTagKey = "ObjectTag";
and then use is to store the object
objc_setAssociatedObject(self, ObjectTagKey, newObjectTag, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
and retrieve it
objc_getAssociatedObject(self, ObjectTagKey);
(example taken by http://oleb.net/blog/2011/05/faking-ivars-in-objc-categories-with-associative-references/)
Is there a cleaner way to define the associated object key, that doesn't involve the declaration of extra variables?
According to this blog entry by Erica Sadun (whose credits go to Gwynne Raskind), there is.
objc_getAssociatedObject and objc_getAssociatedObject require a key to store the object. Such key is required to be a constant void pointer. So in the end we just need a fixed address that stays constant over time.
It turns out that the #selector implementation provides just about what we need, since it uses fixed addresses.
We can therefore just get rid of the key declaration and simply use our property's selector address.
So if you are associating at runtime a property like
#property (nonatomic, retain) id anAssociatedObject;
we can provide dynamic implementations for its getter/setter that look like
- (void)setAnAssociatedObject:(id)newAssociatedObject {
objc_setAssociatedObject(self, #selector(anAssociatedObject), newAssociatedObject, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (id)anAssociatedObject {
return objc_getAssociatedObject(self, #selector(anAssociatedObject));
}
Very neat and definitely cleaner than defining an extra static variable key for every associated object.
Is this safe?
Since this is implementation-dependent, a legitimate question is: will it easily break?
Quoting the blog entry
Apple would probably have to implement a completely new ABI for that to happen
If we take those words to be true, it's then reasonably safe.
If you need access to the key from outside the scope of a single method, a nice pattern for this which leads to more readable code is to create a pointer which simply points to its own address in the stack. For example:
static void const *MyAssocKey = &MyAssocKey;
If you only need access from within the scope of a single method, you can actually just use _cmd, which is guaranteed to be unique. For example:
objc_setAssociatedObject(obj, _cmd, associatedObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
A slight variation on the idea #Gabriele Petronella discussed is to associate a dictionary to every object:
//NSObject+ADDLAssociatedDictionary.h
#import <Foundation/Foundation.h>
#interface NSObject (ADDLAssociatedDictionary)
- (void)addl_setAssociatedObject:(id)object forKey:(id<NSCopying>)key;
- (id)addl_associatedObjectForKey:(id<NSCopying>)key;
#end
//NSObject+ADDLAssociatedDictionary.m
#import <objc/runtime.h>
#interface NSObject (ADDLAssociatedDictionaryInternal)
- (NSMutableDictionary *)addl_associatedDictionary;
#end
#implementation NSObject (ADDLAssociatedDictionary)
- (void)addl_setAssociatedObject:(id)object forKey:(id<NSCopying>)key
{
if (object) {
self.addl_associatedDictionary[key] = object;
} else {
[self.addl_associatedDictionary removeObjectForKey:key];
}
}
- (id)addl_associatedObjectForKey:(id<NSCopying>)key
{
return self.addl_associatedDictionary[key];
}
#end
#implementation NSObject (ADDLAssociatedDictionaryInternal)
const char addl_associatedDictionaryAssociatedObjectKey;
- (NSMutableDictionary *)addl_associatedDictionaryPrimitive
{
return objc_getAssociatedObject(self, &addl_associatedDictionaryAssociatedObjectKey);
}
- (void)addl_setAssociatedDictionaryPrimitive:(NSMutableDictionary *)associatedDictionary
{
objc_setAssociatedObject(self, &addl_associatedDictionaryAssociatedObjectKey, associatedDictionary, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSMutableDictionary *)addl_generateAssociatedDictionary
{
NSMutableDictionary *associatedDictionary = [[NSMutableDictionary alloc] init];
[self addl_setAssociatedDictionaryPrimitive:associatedDictionary];
return associatedDictionary;
}
- (NSMutableDictionary *)addl_associatedDictionary
{
NSMutableDictionary *res = nil;
#synchronized(self) {
if (!(res = [self addl_associatedDictionaryPrimitive])) {
res = [self addl_generateAssociatedDictionary];
}
}
return res;
}
#end
Then in our category on some subclass Derived of NSObject
//Derived+Additions.h
#import "Derived.h"
#interface Derived (Additions)
#property (nonatomic) id anAssociatedObject;
#end
//Derived+Additions.m
#import "NSObject+ADDLAssociatedDictionary.h"
#implementation Derived (Additions)
- (void)setAnAssociatedObject:(id)anAssociatedObject
{
[self addl_setAssociatedObject:anAssociatedObject forKey:NSStringFromSelector(#selector(anAssociatedObject))];
}
- (id)anAssociatedObject
{
return [self addl_associatedObjectForKey:NSStringFromSelector(#selector(anAssociatedObject))];
}
#end
One benefit of the associated dictionary approach in general is the added flexibility that comes from being able to set objects for keys that are generated at runtime, not to mention the much nicer syntax.
A benefit particular to using
NSStringFromSelector(#selector(anAssociatedObject))
is that NSStringFromSelector is guaranteed to give an NSString representation of the selector which will always be an acceptable dictionary key. As a result, we don't have to worry at all (though I don't think it's a reasonable concern) about ABI changes.
I'm trying to create a structure, which should be used as an array.
Then at the same time I need to access it from a different view, so I'm guessing I need to make it global for now, until I learn a better way (have tried many suggestions from google searches)
But how do I declare a variable twice?
I mean both as my structure, and as an array?
Sometimes "global variables" can be appropriate, but not simply as "global variables". The proper way to do it in Objective C is with singletons.
Here is part of the .h implementation below:
#interface MySingleton : NSObject
#property (nonatomic, strong) NSMutableArray *myArray;
+ (MySingleton *)sharedSingleton
#end
And here is what the .m implementation would look like:
#implementation MySingletion
#synthesize myArray;
static MySingleton *shared = nil;
- (id)init
{
self = [super init];
if(self)
{
myArray = [[NSMutableArray alloc] init];
}
return self;
}
+ (MySingleton *)sharedSingleton
{
if(shared == nil)
{
shared = [[MySingleton alloc] init];
}
return shared;
}
Then, whenever you wanted to access this from a given class, you would #import "MySingleton.h" and access your array like so:
[MySingleton sharedSingleton].myArray
Edit: However, this does not mean that every time you need to pass data it should be done with a singleton. Singletons are correct in certain situations, but as others have mentioned, you should pass the data to the next class yourself in most situations.
Well i just created a new h and m fil, where i put a lot of varibales
using extend in the.h file
and alloc init in the .m file
Then i just include this file where i need the variables. Probably there is a much better way, but this was the only one i could figure out.
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.
I've implemented a bit of code from one of the many Apple code examples, but I'm having a bit of trouble, because the retain attribute of one of the properties doesn't appear to be working. Here's the property declaration:
#property (nonatomic, retain) EditingViewController *editingViewController;
And here's the code:
- (EditingViewController *)editingViewController {
// Instantiate the editing view controller if necessary.
if (editingViewController == nil) {
EditingViewController *aController = [[EditingViewController alloc] init];
editingViewController = aController;
[aController release];
}
return editingViewController;
}
I understand that (retain) is supposed to cause the retain count to increase by 1 on assignment; however, the code fails unless I do send [aController retain] myself, or don't send [aController release]. What am I missing here?
When you reference editingViewController, it is equivalent to self->editingViewController, i.e. an access to an ivar.
If you want to use a getter or setter, you need to use self.editingViewController, or equivalently [self setEditingViewController:aController].
This is why I prefer to use an ivar with a different name to the property, for example:
EditingViewController* i_editingViewController;
#property (nonatomic, retain) EditingViewController *editingViewController;
#synthesize editingViewController = i_editingViewController;
Then you can write your lazy getter as:
- (EditingViewController *)editingViewController {
// Instantiate the editing view controller if necessary.
if (i_editingViewController == nil) {
i_editingViewController = [[EditingViewController alloc] init];
}
return i_editingViewController;
}
or
- (EditingViewController *)editingViewController {
// Instantiate the editing view controller if necessary.
if (i_editingViewController == nil) {
EditingViewController *aController = [[EditingViewController alloc] init];
self.editingViewController = aController;
[aController release];
}
return i_editingViewController;
}
I would probably use the former method (not invoking the setter) because the value of editingViewController (as seen by any observer) has not really changed, but either way should work fine and the different name (for ivar and property) help avoid the confusion or accidental misused. It is also a mild encouragement to use the property (since it avoids the slightly ugly prefix).
Note that Apple reserves the _ prefix, and that setters and getters should not be used in the init/dealloc routines.
You have to write self.editingViewController in order to use the property. Just "editingViewController" is a direct access to the Class member variable, whereas self.editingViewController is equivalent to [self setEditingViewController:...] and will do the appropriate retain/release job.