The EOC introduction and case
Sometimes it is necessary to store the relevant information in the object. At this time we typically inherit a subclass from the class to which the object belongs, and then use the subclass object instead. However, not all situations can do so. Sometimes instances of a class may be created by some mechanism, and developers cannot make this mechanism to create instances of their own subclasses, and there is a powerful feature in OC that solves the problem of " associative objects ".
You can associate many other objects with an object. These objects are distinguished by "keys". Storage objects are affordable to indicate a "storage policy" to maintain the corresponding "memory management semantics". The storage policy is defined by an enumeration named Objc_associationpolicy. The following is the value of the enumeration that lists the equivalent @property property: If the associated object becomes a property, then it will have the corresponding semantics.
@property property equivalent to the association type
Objc_association_assign ASSIGN
Objc_association_retain_nonatomic Nonatomic,retain
Objc_association_copy_nonatomic nonatomic,copy
Objc_association_retain RETAIN
Objc_association_copy COPY
The following methods can manage associated objects.
void Objc_setassociatedobject (ID object, void* key, id value, Objc_associationpolicy policy)
This method sets the associated object value for an object with the given key and policy.
ID objc_getassociatedobject (ID object, void* key)
This method obtains the corresponding associated object value from an object based on the given key.
void Objc_removeassociatedobjects (ID object)
This method removes all associated objects from the specified object.
We can think of an object as nsdictionary. The value associated to the object is understood as an entry in the dictionary, so accessing the value of the associated object is equivalent to invoking the [object Setobject:object Valueforkey on the Nsdictionary object: Key] and the [object Objectforkey:key] method. However, there is an important difference between the two: when you set the associated object, the key (key) is an opaque pointer, and if you call IsEqual on a two key: The return value of the method is yes then nsdictonary think the two are equal but for the associated object you want two keys to match the same value The two must be exactly the same pointer. Therefore, when you set the associated object value, you typically use a static global variable to do the key.
Examples of usage of associated objects:
The Uialertview class is used at development time, and this class provides a standard view. You can display a warning message to the user. When the user presses the button to close the view, the proxy method is required to handle the action but to set up this proxy mechanism requires separating the code that creates the warning view and the action of the button.
Such as:
-(void) Showalertview { Uialertview * alert = [[Uialertview alloc]initwithtitle:@ "question" message:@ "What do you want to do" delegate: Self cancelbuttontitle:@ "Cancel" otherbuttontitles:@ "continue", nil]; [Alert show];} -(void) Alertview: (Uialertview *) Alertview Clickedbuttonatindex: (Nsinteger) Buttonindex { if (Buttonindex = = 0) { NSLog (@ "%@", @ "Cancel"); } else { NSLog (@ "%@", @ "continue");} }
If you want to work with multiple warning views in the same class then the code becomes more complex, we have to check the parameters of the incoming Alertview in the delegate method, and then choose the appropriate logic, it would be much easier to write the logic of each button directly when creating the warning view. This can be done by associating objects. After creating the warning view, set a "block" associated with it and wait until the delegate method is executed to read it out. The implementation code for this scenario is as follows.
#import <objc/runtime.h> @interface Homeviewcontroller () <UIAlertViewDelegate> @end @implementation homeviewcontrollerstatic void * Myalertviewkey = "Myalertviewkey";-(void) viewdidload {[Super viewdidload]; }-(void) Createalertview {uialertview * alert = [[Uialertview alloc]initwithtitle:@ ' hint ' message:@ ' you want to do ' delegate:self C ancelbuttontitle:@ "Cancel" otherbuttontitles:@ "OK", nil]; void (^block) (Nsinteger) = ^ (Nsinteger buttonindex) {if (Buttonindex = = 0) {[Self docancel]; } else if (Buttonindex = = 1) {[Self docontinue]; } }; Objc_setassociatedobject (Alert, Myalertviewkey, block, Objc_association_copy); [Alert show];} -(void) Alertview: (Uialertview *) Alertview Clickedbuttonatindex: (nsinteger) buttonindex {void (^block) (NSInteger) = Objc_getassociatedobject (Alertview, Myalertviewkey); Block (Buttonindex);} -(void) Docancel {NSLog (@ "%@", @ "Cancel");} -(void) docontinue {NSLog (@ "%@", @ "continue");}
amazing!!! That's great!
In this way, the code that creates the warning view and the result of the processing operation is put together so that it is easier to read than the original, because we do not need to walk back and forth between the two parts of the code to understand the usefulness of the warning view. However, when using this scheme it is important to note that blocks may need to capture certain variables which may cause "circular references". (The method of unbinding a circular reference: a weak reference). The 40th article describes the problem in detail.
This is useful but should only be considered when other methods do not work, and if misused, the code can easily get out of control, making it difficult to debug. The reason for the "circular reference" is difficult to pinpoint because the relationship between the associated objects is not formally defined, and the memory management semantics are defined at the time of association rather than pre-determined in the interface. Use this method to be careful not just because somewhere you can use the notation you have to use it to create this kind of uialertview there is also a way to inherit a subclass from which to save the block as a property in a subclass. This is better than using associated objects if you need to use alertview multiple times.
Points:
(1) Two objects can be connected by the "associative object" mechanism.
(2) When defining an association object, you can specify the semantics of memory management to mimic the "owning relationship" and "non-owning relationship" used when defining attributes.
(3) Associate objects should be selected only when other practices are not available because this practice often introduces bugs that are difficult to find.
Access information
After you create an app, you might create categories to extend kernel classes such as NSString, nsmutablestring, and so on. However, the category cannot add attributes and private variables.
typedef objc_enum (uintptr_t, Objc_associationpolicy) {
Objc_association_assign = 0,
Objc_association_retain_nonatomic = 1,
Objc_association_copy_nonatomic = 3,
Objc_association_retain = 01401,
Objc_association_copy = 01403
};
/**
* @description set an associated object for a given object with a given key value and policy.
* @param object for whom to set the associated object
* Key for @param key associated object
* @param value key associated values pass nil after clearing existing associations
* The possible values for @param policy Association policies are shown in the enumeration above.
*/
Objc_export void Objc_setassociatedobject (ID object, const void *key, ID value, Objc_associationpolicy policy)
__osx_available_starting (__mac_10_6, __iphone_3_1);
/**
* Return associated objects based on key
*
* object that sets the associated object @param object.
* Key for @param key associated object
*/
Objc_export ID objc_getassociatedobject (ID object, const void *key)
__osx_available_starting (__mac_10_6, __iphone_3_1);
/**
* Remove all associated objects for this object.
*
* @param object to manage objects associated with objects
*
* @note The main purpose is to easily get the original state of an object, which should not be used in practice, because all associations will be removed, and associations may include other local set-up associations.
You should use Objc_setassociatedobject to pass nil to clear the association.
*/
Objc_export void Objc_removeassociatedobjects (ID object)
__osx_available_starting (__mac_10_6, __iphone_3_1);
The runtime is used to establish an association reference to the category. The next step is to add an example of such a property
@property (nonatomic, copy) NSString * STR;
1. Introduction of header Files
2. Add an attribute to an anonymous category or header file, except that a private attribute is added to the anonymous category that is only available in this class, and is not available in instances of the class. Added in the header file can also be used in instances of the class.
3. Getter Setter method that adds attributes in the implementation.
. h file
@interface nsarray (extension)
@property (nonatomic, copy) NSString * name;
-(void) print;
@end
. m file
#import "Nsarray+extension.h"
#import <objc/runtime.h>
static void * Namekey = & Namekey;
@implementation nsarray (extension)
-(void) Print {
NSLog (@ "%@", self.name);
}
Getter
-(NSString *) name {
Return Objc_getassociatedobject (self, namekey);
}
Setter
-(void) SetName: (NSString *) name {
Objc_setassociatedobject (self, Namekey, name, objc_association_copy);
}
@end
Main file
Nsarray * arr = @[@ "1", @ "2", @ "3", @ "4"];
Arr.name = @ "Array";
[Arr print];
Output "Array".
This article ends here.
Effective-oc 10. Using associated objects to store custom data in existing classes