Implementation of Associatedobject related object principle

Source: Internet
Author: User

Introduced

The Association object (associatedobject) is an attribute of the OBJECTIVE-C 2.0 runtime, allowing the developer to add custom attributes to an existing class in the extension. In the actual production process, the more common way is to add member variables to the category (category).

Example
#import <objc/runtime.h>@interface NSObject (AssociatedObject)@property (nonatomic, strong) id property;@end@implementation NSObject (AssociatedObject)@dynamic property;- (id)property {    return objc_getAssociatedObject(self, _cmd);}- (void)setProperty:(NSString *)property {    objc_setAssociatedObject(self, @selector(property), property, OBJC_ASSOCIATION_RETAIN_NONATOMIC);}@end

By implementing the code can be slightly analyzed, holding the constant objc_getAssociatedObject pointer address (example passed in selector as a parameter, actually void*), from the instance to get the desired object. objc_setAssociatedObjectsaves the specified object according to the parameter protocol passed in.

Parameter protocol
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {    OBJC_ASSOCIATION_ASSIGN = 0,           /**< Specifies a weak reference to the associated object. */    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object. The association is not made atomically. */    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,   /**< Specifies that the associated object is copied. The association is not made atomically. */    OBJC_ASSOCIATION_RETAIN = 01401,       /**< Specifies a strong reference to the associated object. The association is made atomically. */    OBJC_ASSOCIATION_COPY = 01403          /**< Specifies that the associated object is copied. The association is made atomically. */};

In fact, these five protocols are what we usually use when defining attributes, and it is important to note that although Apple says it is equivalent to one in the comments, it OBJC_ASSOCIATION_ASSIGN weak reference actually equals assign/unsafe_unretained .

For weak the difference is not within the scope of this article, the obvious difference is that after the variable is released, the reference will be weak empty, the unsafe_unretained memory address will be preserved, once the gain may be the wild pointer flash back.

Summarize

We know that if a class is to add a variable, only objc_allocateClassPair objc_registerClassPair between and addIvar . Once the class is registered, the variable structure is not allowed to be changed, in order to prevent two instances of the same class having different variables resulting in running confusion.

It is a good practice to add variables to the instance at runtime without changing the structure of the internal variables of the class.

Implementing an external method for an associated object
//Sets an associated value for a given object using a given key and association policy.void objc_setAssociatedObject(id object, const void * key, id value, objc_AssociationPolicy policy);//Returns the value associated with a given object for a given key.id objc_getAssociatedObject(id object, const void * key);//Removes all associations for a given object.void objc_removeAssociatedObjects(id object);

Compared to the usage in the example, one more objc_removeAssociatedObjects , can you use this method to delete the unused associated objects?

Apple's documentation explains that this method is primarily used to restore objects to the initial state of the class, removing all associations, including additions to other modules, and therefore should be objc_setAssociatedObject(..,nil,..) uninstalled in the same way.

Setter implementation

objc_setAssociatedObjectThe actual call is_object_set_associative_reference

void _object_set_associative_reference (ID object, void *key, id value, uintptr_t policy) {//Retain the new value (if    Any) outside the lock.    Objcassociation old_association (0, nil); ID new_value = value?    Acquirevalue (value, policy): nil;        {Associationsmanager Manager;        Associationshashmap &associations (Manager.associations ());        disguised_ptr_t Disguised_object = Disguise (object);            if (new_value) {//Break any existing association.            Associationshashmap::iterator i = Associations.find (disguised_object); if (i! = Associations.end ()) {//secondary table exists objectassociationmap *refs = i->                Second                Objectassociationmap::iterator j = refs->find (key);                    if (J! = Refs->end ()) {old_association = j->second;                J->second = objcassociation (policy, new_value); } else {(*refs) [KeY] = objcassociation (policy, new_value);                }} else {//Create the new association (first time).                Objectassociationmap *refs = new Objectassociationmap;                Associations[disguised_object] = refs; (*refs)                [Key] = objcassociation (policy, new_value);            Object->sethasassociatedobjects ();            }} else {//Setting the association to nil breaks the association.            Associationshashmap::iterator i = Associations.find (disguised_object);                if (i! = Associations.end ()) {Objectassociationmap *refs = i->second;                Objectassociationmap::iterator j = refs->find (key);                    if (J! = Refs->end ()) {old_association = j->second;                Refs->erase (j);    }}}}//Release the old value (outside of the lock). if (Old_association.hasvalue ()) ReleasevAlue () (old_association);} 
Memory management
static id acquireValue(id value, uintptr_t policy) {    switch (policy & 0xFF) {    case OBJC_ASSOCIATION_SETTER_RETAIN:        return objc_retain(value);    case OBJC_ASSOCIATION_SETTER_COPY:        return ((id(*)(id, SEL))objc_msgSend)(value, SEL_copy);    }    return value;}static void releaseValue(id value, uintptr_t policy) {    if (policy & OBJC_ASSOCIATION_SETTER_RETAIN) {        return objc_release(value);    }}ObjcAssociation old_association(0, nil);id new_value = value ? acquireValue(value, policy) : nil;{    old_association = ...}if (old_association.hasValue()) ReleaseValue()(old_association);

We pick out the code that is related to the object memory carefully, first put the new incoming object, according to the protocol retain/copy , in the process of assigning the value of the old value, before the method ends release .

Assign value
  Associationsmanager Manager; Associationshashmap &associations (Manager.associations ());d isguised_ptr_t Disguised_object = DISGUISE (object);    if (new_value) {//requires assignment associationshashmap::iterator i = Associations.find (disguised_object);        if (i! = Associations.end ()) {//found the associated table for this object objectassociationmap *refs = i->second;        Objectassociationmap::iterator j = refs->find (key);            if (J! = Refs->end ()) {//found the associated object of this key old_association = j->second;        J->second = objcassociation (policy, new_value);        } else {//not found, add an association (*REFS) [Key] = objcassociation (policy, new_value);        }} else {//not found, create a new association table Objectassociationmap *refs = new Objectassociationmap;        Associations[disguised_object] = refs; (*refs)        [Key] = objcassociation (policy, new_value);    Object->sethasassociatedobjects (); }}

First Look at AssociationsManager theAssociationsHashMap

class AssociationsManager {    static AssociationsHashMap *_map;public:    AssociationsHashMap &associations() {        if (_map == NULL)            _map = new AssociationsHashMap();        return *_map;    }};class AssociationsHashMap : public unordered_map<disguised_ptr_t, ObjectAssociationMap *, DisguisedPointerHash, DisguisedPointerEqual, AssociationsHashMapAllocator>;class ObjectAssociationMap : public std::map<void *, ObjcAssociation, ObjectPointerLess, ObjectAssociationMapAllocator>;

AssociationsManagerManages all the associated objects within an application through a hash table with a pointer address as the primary key and a value of the associated tables.

First, the object's pointer address to find the association table, and then through the specified key value to find the association, thereby obtaining the associated object.

Delete
AssociationsHashMap::iterator i = associations.find(disguised_object);if (i !=  associations.end()) {    ObjectAssociationMap *refs = i->second;    ObjectAssociationMap::iterator j = refs->find(key);    if (j != refs->end()) {        old_association = j->second;        refs->erase(j);    }}

Similar to the modification method, the method of executing the hash table is removed after the association relationship is found erase .

Getter implementation

objc_getAssociatedObjectThe actual call is_object_get_associative_reference

ID _object_get_associative_reference (ID object, void *key) {id value = nil;    uintptr_t policy = objc_association_assign;        {Associationsmanager Manager;        Associationshashmap &associations (Manager.associations ());        disguised_ptr_t Disguised_object = Disguise (object);        Associationshashmap::iterator i = Associations.find (disguised_object);            if (i! = Associations.end ()) {Objectassociationmap *refs = i->second;            Objectassociationmap::iterator j = refs->find (key);                if (J! = Refs->end ()) {Objcassociation &entry = j->second;                Value = Entry.value ();                Policy = Entry.policy ();                if (Policy & Objc_association_getter_retain) {Objc_retain (value); }}}} if (Value && policy & objc_association_getter_autorelease) {Objc_aut    Orelease (value); } return value;

The way to find a hash table is the same as a setter, except that if you need retain and autorelease in your strategy, you need to deal with it. So how do we agree to these strategies?

enum {     OBJC_ASSOCIATION_SETTER_ASSIGN      = 0,    OBJC_ASSOCIATION_SETTER_RETAIN      = 1,    OBJC_ASSOCIATION_SETTER_COPY        = 3,            // NOTE:  both bits are set, so we can simply test 1 bit in releaseValue below.    OBJC_ASSOCIATION_GETTER_READ        = (0 << 8),     OBJC_ASSOCIATION_GETTER_RETAIN      = (1 << 8),     OBJC_ASSOCIATION_GETTER_AUTORELEASE = (2 << 8)}; 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};

OBJC_ASSOCIATION_RETAIN = 01401, where the 01401 beginning is 0 , so is the octal number, translated into binary is 0000 0011 0000 0001 , take a bit to judge is OBJC_ASSOCIATION_SETTER_RETAIN OBJC_ASSOCIATION_GETTER_RETAIN OBJC_ASSOCIATION_GETTER_AUTORELEASE .

When you save, you need to retain increase the reference count first, and retain then autorelease wait for the release to achieve atomicity when you get it.

Remove implementation

objc_removeAssociatedObjectsDetermines whether an object has an association, and then executes the_object_set_associative_reference

void _object_remove_assocations (ID object) {vector< objcassociation,objcallocator<objcassociation> >    Elements        {Associationsmanager Manager;        Associationshashmap &associations (Manager.associations ());        if (associations.size () = = 0) return;        disguised_ptr_t Disguised_object = Disguise (object);        Associationshashmap::iterator i = Associations.find (disguised_object);            if (i! = Associations.end ()) {//Copy all of the associations, need to be removed.            Objectassociationmap *refs = i->second; for (Objectassociationmap::iterator j = refs->begin (), end = Refs->end (); J! = end; ++j) {ELEMENTS.P            Ush_back (J->second);            }//Remove the secondary table.            Delete refs;        Associations.erase (i);    }}//The calls to Releasevalue () happen outside of the lock. For_each (Elements.begin (), Elements.end (), Releasevalue ());

The implementation also shows why it is not recommended in the introduction, because it iterates through all the associated objects and releases them all, which can cause other module functional defects.

Judging associated objects

It is interesting to determine whether an object has an associated object implementation.

inline bool objc_object::hasAssociatedObjects(){    if (isTaggedPointer()) return true;    if (isa.nonpointer) return isa.has_assoc;    return true;}
inline void objc_object::setHasAssociatedObjects(){    if (isTaggedPointer()) return; retry:    isa_t oldisa = LoadExclusive(&isa.bits);    isa_t newisa = oldisa;    if (!newisa.nonpointer  ||  newisa.has_assoc) {        ClearExclusive(&isa.bits);        return;    }    newisa.has_assoc = true;    if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;}

By default, the result is true that only one token bit is saved under a 64-bit system. So I'm guessing that to speed up the release cycle, when the destructor is being refactored, the object is determined whether it needs to be freed. Imagine that if you query the hash table every time, the execution efficiency will be reduced, it is better to pass first, then do the processing.

In the nonpointer context of this article, it is briefly described as a 64-bit system where the pointer address is stored not only as a memory address, but also contains other token information, including the HAS_ASSOC involved in this article.

taggedPointeris an optimization strategy that saves simple numeric or string information directly in the pointer address, thus not requesting additional memory to speed up the operation.

Summarize

The implementation of the associated object is not complex, it is saved in a global hash table, and access is performed by querying the table to find the association. The hash table is characterized by the sacrifice of space for time, so execution speed can also be guaranteed.

What is the application of question-and-answer related objects?

An association object can bind a lifetime variable to a specified object at run time.

1. Because it does not change the implementation of the original class, it can be extended to native or packaged libraries, generally with the category to achieve complete functionality.

Variables defined by the 2.ObjC class are exposed externally due to runtime characteristics, and the associated object can be used to hide key variables and ensure security.

3. Can be used for KVO, using the associated object as the observer, you can avoid observing itself causing the loop.

How does the system manage associated objects?

The system obtains the associated object through the management of a global hash table through the object pointer address and the fixed parameter address passed. settermanage the life cycle of an object based on the parameter protocol passed in.

Is it necessary to manually empty the pointer when it is released?

When the object is disposed, if the protocol is set OBJC_ASSOCIATION_ASSIGN , then his associated object does not reduce the reference count, and the other protocols are reduced to release the associated object.

unsafe_unretainIt is generally considered that the external object is controlled, so the object is not processed, so no matter what the protocol, the object frees without having to manually tell the associated object to empty.

Associatedobject Associative object principle implementation

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.