What is objc_setAssociatedObject() and in what cases should it be used? - iphone

In a project I have taken on, the original author has opted to use objc_setAssociatedObject() and I'm not 100% clear what it does or why they decided to use it.
I decided to look it up and, unfortunately, the docs aren't very descriptive about its purpose.
objc_setAssociatedObject
Sets an associated value for a given object using a given key and association policy.
void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy)
Parameters
object
The source object for the association.
key
The key for the association.
value
The value to associate with the key key for object. Pass nil to clear an existing association.
policy
The policy for the association. For possible values, see “Associative Object Behaviors.”
So what exactly does this function do and in what cases should it be used?
Edit after reading answers
So what is the point in the following code?
Device *device = [self.list objectAtIndex:[indexPath row]];
DeviceViewController *next = [[DeviceViewController alloc] initWithController:self.controller
device:device
item:self.rootVC.selectedItem];
objc_setAssociatedObject(device, &kDeviceControllerKey, next, OBJC_ASSOCIATION_RETAIN);
What is the point in associating the device with the view controller if it's already an instance variable?

objc_setAssociatedObject adds a key value store to each Objective-C object. It lets you store additional state for the object, not reflected in its instance variables.
It's really convenient when you want to store things belonging to an object outside of the main implementation. One of the main use cases is in categories where you cannot add instance variables. Here you use objc_setAssociatedObject to attach your additional variables to the self object.
When using the right association policy your objects will be released when the main object is deallocated.

From the reference documents on Objective-C Runtime Reference:
You use the Objective-C runtime
function objc_setAssociatedObject to
make an association between one object
and another. The function takes four
parameters: the source object, a key,
the value, and an association policy
constant. The key is a void pointer.
The key for each association must be unique. A typical pattern is to
use a static variable.
The policy specifies whether the associated object is assigned,
retained, or copied, and whether the
association is be made atomically or
non-atomically. This pattern is
similar to that of the attributes of
a declared property (see “Property
Declaration Attributes”). You specify
the policy for the relationship using
a constant (see
objc_AssociationPolicy and
Associative Object Behaviors).
Establishing an association between an array and a string
static char overviewKey;
NSArray *array =
[[NSArray alloc] initWithObjects:#"One", #"Two", #"Three", nil];
// For the purposes of illustration, use initWithFormat: to ensure
// the string can be deallocated
NSString *overview =
[[NSString alloc] initWithFormat:#"%#", #"First three numbers"];
objc_setAssociatedObject (
array,
&overviewKey,
overview,
OBJC_ASSOCIATION_RETAIN
);
[overview release];
// (1) overview valid
[array release];
// (2) overview invalid
At point 1, the string overview is
still valid because the
OBJC_ASSOCIATION_RETAIN policy
specifies that the array retains the
associated object. When the array is
deallocated, however (at point 2),
overview is released and so in this
case also deallocated. If you try to,
for example, log the value of
overview, you generate a runtime
exception.

Here is a list of use cases for object associations:
one: To add instance variables to categories. In general this technique is advised against, but here is an example of a legitimate use. Let's say you want to simulate additional instance variables for objects you cannot modify (we are talking about modifying the object itself, ie without subclassing). Let's say setting a title on a UIImage.
// UIImage-Title.h:
#interface UIImage(Title)
#property(nonatomic, copy) NSString *title;
#end
// UIImage-Title.m:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
static char titleKey;
#implementation UIImage(Title)
- (NSString *)title
{
return objc_getAssociatedObject(self, &titleKey);
}
- (void)setTitle:(NSString *)title
{
objc_setAssociatedObject(self, &titleKey, title, OBJC_ASSOCIATION_COPY);
}
#end
Also, here is a pretty complex (but awesome) way of using associated objects with categories.. it basically allows you to pass in a block instead of a selector to a UIControl.
two: Dynamically adding state information to an object not covered by its instance variables in conjunction with KVO.
The idea is that your object gains state information only during runtime (ie dynamically). So the idea is that although you can store this state info in an instance variable, the fact that you're attaching this info into a an object instantiated at runtime and dynamically associating it with the other object, you are highlighting the fact that this is a dynamic state of the object.
One excellent example that illustrates this is this library, in which associative objects are used with KVO notifications. Here is an excerpt of the code (note: this KVO notification isn't necessary to run make the code in that library work.. rather it's put there by the author for convenience, basically any object that registers to this will be notified via KVO that changes have happened to it):
static char BOOLRevealing;
- (BOOL)isRevealing
{
return [(NSNumber*)objc_getAssociatedObject(self, &BOOLRevealing) boolValue];
}
- (void)_setRevealing:(BOOL)revealing
{
[self willChangeValueForKey:#"isRevealing"];
objc_setAssociatedObject(self, &BOOLRevealing,
[NSNumber numberWithBool:revealing], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[self didChangeValueForKey:#"isRevealing"];
}
bonus: take a look at this discussion/explanation of associated objects by Mattt Thompson, author of the seminal AFNetworking library

To answer your revised question:
What is the point in associating the device with the view controller if it's already an instance variable?
There are several reasons why you might want to do this.
the Device class doesn't have a controller instance variable, or property and you can't change it or subclass it e.g. you don't have the source code.
you want two controllers associated with the device object and you can't change the device class or subclass it.
Personally, I think it is very rare to need to use low level Objective-C runtime functions. This looks like a code smell to me.

Related

CoreData Object typing won't work

Can someone explain to me why this doesn't work:
CoreDataClass *classObject = (CoreDataClass *)[some method that returns a dictionary with exact KVC pairs that match CoreDataClass];
NSString *myString = classObject.stringProperty;
But this does:
CoreDataClass *classObject = (CoreDataClass *)[some method that returns a dictionary with exact KVC pairs that match CoreDataClass];
NSString *myString = [classObject valueForKey:#"stringProperty"];
EDIT:
What's the easiest way to cast the dictionary as my NSManagedObjectClass CoreDataClass so I can access properties directly?
It doesn't work since KVC compliance is not at all what defines classes or makes them castable - the class hierarchy exists for a reason, and just ensuring adherence to certain methods doesn't magically make something an instance of a completely different class. Keep in mind that the dot-accessor syntax is just sugar for a method send, so these two are equivalent:
classObject.stringProperty
[classObject stringProperty]
...and the latter obviously isn't valid for instances of NSDictionary (i.e. [[NSDictionary class] instancesRespondToSelector:#selector(stringProperty)] is NO).
Your latter example works because of the very premise of your question: if something is KVC-compliant for the key stringProperty, and you ask it for a value for that key, then obviously you get something back. Furthermore, both NSDictionary and CoreDataClass respond to the selector -valueForKey:, so the message send actually works at runtime.
The best way to get the two across isn't a "cast" at all - it's a complete conversion, at the property level, of the data involved. You might consider creating a custom -initWith... method on CoreDataClass that lets you instantiate its properties from a dictionary, or finding a way to get your method to return an actual instance of CoreDataClass instead of an NSDictionary.
Note that this solution may differ from the "easiest" way to get the data across, which is effectively to keep doing what you're doing and use -valueForKey: (though preferably without the cast, which is misleading).
Casting objects only appears to work (in the sense that you won't get type-checking errors) because it's a hint to the compiler, but it doesn't actually change anything about what the pointer points to, so you are still pointing to an NSDictionary. This is because, at the end of the day, you are essentially casting a pointer to a pointer, but telling Xcode that you are allowed to send a different set of selectors to it.
For NSManagedObjects, creation from a dictionary depends on a few things, but the recommended way is to make a class method on your custom class which will use NSEntityDescription and you NSManagedObjectContext, and sets the properties from the dictionary to the object:
+(CoreDataClass *) coreDataObjectWithDictionary:(NSDictionary *) spec {
CoreDataClass *myInstance = [NSEntityDescription insertNewObjectForEntityForName: #"CoreDataClass" inManagedObjectContext: [myMOCProvider sharedMOC];
myInstance.someProp = [spec valueForKey:#"someProp"];
}

What is the meaning of the console message: "snarfed from ivar layout..."?

I have a console message that appears to be triggered by apparently unrelated events.
The message states:
snarfed from ivar layout: [propertyName] = [constantString]
Where [propertyName] is the name of a property to which I set the value of a string constant [constantString].
What causes this message and what does it means?
I also ran into this issue recently. I was able to fix my specific issue, but I don't think that is exactly what the questioners are running into, since my issue was only being exposed in VoiceOver mode. As such, I'll offer thoughts on what I think is generally occurring and then I'll speak to my specific issue.
As for the general issue, I think that the Apple Framework is deciding to look through all of the ivars of a particular class in order to extract some information that it wants, but that is not provided by other parts of the UI element. This seems a little bizarre to me, but that is what I encountered.
So, to continue with the general case, and in answer to the initial question. If you're like me, then your property name is probably the same as your ivar. Try explicitly defining a getter method for that property. Then, set a breakpoint within that getter if you will be returning a non-nil value. Look at the stacktrace and that should tell you which piece of the apple frameworks is deciding to loop through your ivar layout in order to get the information it wants. (If you're not using the the same name for your property and ivar, then just define a property and getter with the ivar name and do the same thing with the breakpoint.)
My specific case was for a Custom Table Cell (like one of the commenters). In that cell,I had a property that was the same name as its ivar. I also had an explicitly defined getter for that property. I also referenced that custom table cell from the Nib file. So, it looked something like this:
class CustomTableViewCell:UITableViewCell
{
NSString *s ;
}
#property(nonatomic,retain) NSString *s ;
and in the implementation:
#synthesize s ;
-(NSString *)s
{
if( !s )
return self.reuseIdentifer ;
return s ;
}
I put a breakpoint in the return self.reuseIdentifier line,and that showed me a stacktrace from the Accessibility functions. The stacktrace showed my method being called by an Apple internal method that was looping through all of my ivars looking for something to use as the accessibilityLabel for my table cell. The name of the selector is '_accessibilityRetrieveTableViewIvarsText'.
To make matter worse, in my case, this was not just a debugger issue, it was messing up my Accessibility interface by using the wrong thing as the accessibilityLabel.
I came up with 3 fixes for my specific problem:
1) I added a value for the accessibilityLabel for the table cell inside the Nib. This satisfied the Apple framework to the point where it did not go searching through my ivars. This was not the solution I went with, however, because I did not want a static accessibility label.
2) I subclassed my CustomTableViewCell with an empty implementation and interface, and I used that as my Table cell class inside the Nib. This solved the problem because the Apple Framework looped through that class's ivars, of which there were none, and there weren't any values to 'snarf'. I did not use that solution either, but it might be the best one because it keeps Apple's frameworks from inspecting my ivars.
3) The solution I decided on was to make my ivar private and to define the property with a different name. That seems to be the standard way that a lot of folks use properties. This is what it looks like:
class CustomTableViewCell:UITableViewCell
{
#private
NSString *_s ;
}
#property(nonatomic,retain) NSString *s ;
and in the implementation:
#synthesize s = _s ;
-(NSString *)s
{
if( !_s )
return self.reuseIdentifer ;
return _s ;
}
This fixed the problem because nil is returned when Apple inspects the ivar, and, thus, nothing is 'snarfed'. I'm still not sure whether this or #2 is more appropriate.
"snarfed from ivar" basically autofills your accessibilityLabel. If you do that yourself, the message goes away, as there is no more need for sneeking into your UITableViewCell.
For future reference. The message is logged by the accessibility framework, which apparently looks through an UIView ivars for strings.
If you have a custom subclass you can define the custom attributes as specified in the following link:
Accessibility Programming Guide
Alternatively you can make the view subclass not accessible:
- (BOOL)isAccessibilityElement
{
return NO;
}
However, note:
If your application contains a custom individual view with which users need to interact, you must make the view accessible.

Subclassing NSMutableDictionary

I am trying to implement a subclass of NSMutableDictionary that returns nil instead of throwing a NSUndefinedKeyException when the key is not present in the Dictionary.
However when I try to add objects to my dictionary I get
[NSMutableDictionary setObject:forKey:]: method only defined for abstract class
NilDictionary.h
#interface NilDictionary : NSMutableDictionary {
}
#end
NilDctionary.m
#implementation NilDictionary
- (id)valueForUndefinedKey:(NSString *)key {
return nil;
}
#end
Do I really have to implement all the methods from NSMutableDictionary again in my subclass or is there some other class I should be subclassing?
Clarification: My original problem came down to me not being able to read the documentation properly.
If you need to subclass NSMutableDictionary check out the correct answer. If you want a dictionary that returns nil when your key is not present, NSMutableDictionary does that already.
NSMutableDictionary Class Reference says:
In a subclass, you must override both of its primitive methods:
1. setObject:forKey:
2. removeObjectForKey:
You must also override the primitive methods of the NSDictionary class.
NSDictionary Class Reference says:
If you do need to subclass NSDictionary, you need to take into account that is represented by a Class cluster—there are therefore several primitive methods upon which the methods are conceptually based:
1. count
2. objectForKey:
3. keyEnumerator
4. initWithObjects:forKeys:count:
In a subclass, you must override all these methods.
NSDictionary’s other methods operate by invoking one or more of these primitives. The non-primitive methods provide convenient ways of accessing multiple entries at once.
It seems that you need to override all these six methods to make your NSMutableDictionary subclass work perfect.
Here's your problem. NSDictionary (and its mutable counterpart) is part of a class cluster (read more about them here, under the 'Class Cluster' heading), and should not be subclassed because it causes problems such as what you've mentioned (read the subclassing notes in the NSDictionary Class Reference). Whatever it is you need to do, you're going to have a way to extend the classes you want to use in order to do what you want to do. For instance, the above code can easily be placed in a category (read more about categories here).
Are you sure you are not getting the exception when passing in "nil" for a KEY (not a value)?

Global Objects iphone

What is the easiest way to create a global object. I have tried declaring the object outside the method with no luck.
#implementation UV_TouchpadViewController;
NSMutableString *string = [NSMutableString stringWithFormat:#"text"];
Very close -- you can't initialize a non-local variable with a non-const expression, and a method call is inherently non-const, even if it looks like it should be. So basically, change it to
NSMutableString *string;
but if it's only going to be used inside the implementation file (eg. other classes would only get at it through UV_TouchpadViewController, not get/set it directly (this is also the recommended pattern)), then qualify it as static, like so
static NSMutableString *string;
If on the other hand you do want to be able to access it directly from outside UV_TouchpadViewController, leave off the static, but add
extern NSMutableString *string;
to your header file (outside the class #interface), and whomever includes the header will be able to access it. (Note that you could instead just put NSMutableString *string; in your header file, however this is quickly becomes unclear)
Also, if you are trying to do this for a singleton class, (I can't think of a good reason to have a global mutable string -- you know they're not thread safe right?) I recommend reading Apple's docs on singletons first, where they suggest you use ivars, not global variables, even for singletons. However, UV_TouchpadViewController should not even be a singleton (if it is in any way a view controller), it should just have a single instance, if that's all you want.
If on the other hand you just want all UV_TouchpadViewControllers to have access to this one variable, note that across almost all languages this is considered a bad design pattern (globals are bad), and that you should instead stick it in, say, your app delegate (which is guaranteed to have a single globally accessible instance), where it can be an ivar+accessors, and generally considered a setting and (with a little extra code) persisted.
EDIT:
If you want to have a singleton that maintains global state, which I still recommend against -- you should create a class, like for instance ApplicationState, which handles all of the application's global state as a model object in the traditional model-view-controller pattern. I wont go into detail here because that would be highly redundant of a google search.
In your Application Delegate, somewhere, add an ivar ApplicationState *state, and a corresponding #property (and #synthesize in the implementation file).
There are few easier ways to shoot yourself in the foot than by using global variables.
You should never expose a dumb object like a string which has no access control to every object in the app. Any random piece of code anywhere in the app can change the mutable string leading to chaos as the app grows larger.
Usually when people want a global variable what they actually need is either the user defaults or a data model.
The user defaults (NSUserDefaults) is the preference persistence system that saves application state and user's settings both between launches and as the app runs. You can park small bits of data, such as strings, in the defaults and access them easily from anywhere in the app.
A data model is dedicated object that holds the applications data and manages access to it such that only the data model has final control. This makes it easy to tell what has changed the data and how. The data model can be a simple custom class or something elaborate such as core date. You can park the data model in the app delegate or create it as a singleton as the other answered have explained.
I have been using the Apple API for years and I have never needed to use a real global variable. If you think you need one, you probably have misunderstood something about application design in the Apple API. You might want to post a question explaining what you're trying to do with a global variable and what the best strategy should be for doing it without the dangers of using a global variable.
Do you need it for each instance of the class? If so, you should make it an Instance variable. Put
NSMutableString *string;
In your header
And then you can set it in any method in your class.
If this isn't what you meant, update your question or comment.
You can achieve that by implementing getter and setters in the delegate class.
In delegate .h file
Include UIApplication delegate
#interface DevAppDelegate : NSObject <UIApplicationDelegate>
NSString * currentTitle;
- (void) setCurrentTitle:(NSString *) currentTitle;
- (NSString *) getCurrentTitle;
In Delegate implementation class .m
-(void) setCurrentLink:(NSString *) storydata{
currentLink = storydata;
}
-(NSString *) getCurrentLink{
if ( currentLink == nil ) {
currentLink = #"Display StoryLink";
}
return currentLink;
}
So the variable you to assess is set in the currentlink string by setters method and class where you want the string ,just use the getter method.
AppDelegate *del=(AppDelegate *)[[UIApplication sharedApplication]delegate];
TO set:
[del setCurrentLink];
TO Get:
NSString *value=[del getCurrentLink];
All the best
Add:
NSMutableString *globalString = nil;
to any .m file of any object. The nil initialization adds a little safety, since nil objects can be "safely" messaged without outright crashing the app.
Add:
extern NSMutableString *globalString;
to the headers of any other objects that needs to access this global.
Add:
if (globalString == nil) {
globalString = [ [ NSMutableString stringWithFormat:#"text"] retain ];
}
to the init of any class(es) that could be the very first to touch this global, or to some init that happens even earlier.
Globals are a less verbose form of singleton, but with no access restriction or tracking. Use with caution.
actually as per my r&d i got that by use of extern we have to create an instance but the final thing is to #define your variable and can access any where you want without any creating of instance and other thing just directly use variable by its name....

How to tell if an object has already been allocated, so it isn't allocated multiple times?

Please be nice.. I am a noob with this stuff. I want to allocate and use an object through a method that I call many times, and instead of making it a global variable and allocating it in my initWithFrame method I want to alloc it in just the one method, and make it a local variable that I allocate then and use then.
You need to step back and think about how many different things need to access said object.
If it is everyone, then you should likely implement the shared instance pattern. Something like:
+ (MyObject *) mySharedObjectThatEveryoneUses
{
static MyObject *sharedInstance;
if (!sharedInstance) {
sharedInstance = [MyObject alloc];
sharedInstance = [sharedInstance init];
}
return sharedInstance;
}
Note that the above does not account for threading on initialization. I.e. if multiple threads simultaneously call the method for the first time, you might end up with multiple instances. Also note that the splitting of +alloc and -init is a pattern that is unique to this situation. Doing so avoids the situation where the -init method causes + mySharedObjectThatEveryoneUses to be invoked, creating a second instance before the assignment can happen.
Now, if you are talking about access within an object graph; access to the single instance within some subset of objects in your application, then you will need to have an instance variable somewhere that all of the objects can get to.
For example, say you are implementing a game where there is a Player object and that Player object has a bunch of Piece instances. When a Piece is created, you would typically have a property that refers to the owning player and that would be your single point of reference.
If you need something more transient, you should likely look to what should be your controller layer. I.e. there has to be some object somewhere that can act as a coordinator for the set of objects that need access to that single instance.
If I understood your question correctly, you're looking for a static variable. A static variable keeps its value like a global variable, but it "lives inside" a function like a local variable. Your function could look something like this:
+ (MyObject *)getSingletonObject {
static MyObject *obj = [[MyObject alloc] init];
return obj;
}
obj would be created the first time you called this method, but calling the method again would return the same object it had already created.
What you want is called a Singleton. You can read up on Apple's recommended way to create singletons here:
http://developer.apple.com/Mac/library/documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/CocoaObjects.html#//apple_ref/doc/uid/TP40002974-CH4-SW32
Look for "Creating a Singleton Instance" in the "Cocoa Objects" section. There are a few more methods it's good to define beyond just a class level initializer.
I'm not entirely sure I understand your question, but you can't really allocate the same object many times.
Each time you want a new instance, you ask the Class object to allocate a new instance for you (yes, classes being objects themselves is mind blowing). So this code:
NSString* s;
s = [[NSString alloc] init];
s = [[NSString alloc] init];
Here you are creating two instances of NSString. This code is bad because we have alloc'd the first instance and have not released, and assigned its only reference (s) to a new instance. This would cause a memory leak.