OBJECTIVE-C Runtime Two: member variables and properties

Source: Internet
Author: User
Type encoding (Type Encoding)
As a supplement to Runtime, the compiler encodes the return value and parameter type of each method into a string and associates it with the method selector. This encoding scheme is also very useful in other situations, so we can use the @encode compiler directive to get it. When a type is given, @encode returns the string encoding of this type. These types can be basic types such as int, pointers, or types such as structures and classes. In fact, any type that can be used as a parameter of sizeof () can be used for @encode ().

In the Type Encoding section of the Objective-C Runtime Programming Guide, all type encodings in Objective-C are listed. It should be noted that many of these types are the same as the encoding types we use for archiving and distribution. But some cannot be used when archiving.

Note: Objective-C does not support long double type. @encode (long double) returns d, which is the same as double.

The type code of an array is in square brackets; it contains the number of array elements and the element type. As the following example:

float a [] = {1.0, 2.0, 3.0};
1
NSLog (@ "array encoding type:% s", @encode (typeof (a)));
The output is:

2014-10-28 11: 44: 54.731 RuntimeTest [942: 50791] array encoding type: [3f]
Other types can refer to Type Encoding, not elaborate here.

In addition, there are some encoding types, although @encode will not return them directly, but they can be used as the type qualifier of the method declared in the protocol. You can refer to Type Encoding.

For properties, there will be some special type codes to indicate that the property is read-only, copy, retain, etc. For details, please refer to Property Type String.

Member variables, attributes
There are not many related data structures about member variables and attributes in Runtime, only three, and they are very simple. However, there is also a very practical but often overlooked feature, namely, associated objects, which we will discuss in detail in this section.

Basic data type Ivar
Ivar is the type of instance variable, which is actually a pointer to objc_ivar structure, which is defined as follows:

typedef struct objc_ivar * Ivar;

struct objc_ivar {

    char * ivar_name OBJC2_UNAVAILABLE; // variable name

    char * ivar_type OBJC2_UNAVAILABLE; // variable type

    int ivar_offset OBJC2_UNAVAILABLE; // Base address offset byte

#ifdef __LP64__

    int space OBJC2_UNAVAILABLE;

#endif

}
objc_property_t
objc_property_t is the type of property declared by Objective-C. It is actually a pointer to objc_property structure, which is defined as follows:

typedef struct objc_property * objc_property_t;
objc_property_attribute_t
objc_property_attribute_t defines the attribute of the attribute (attribute), it is a structure, defined as follows:

typedef struct {

    const char * name; // feature name

    const char * value; // characteristic value

} objc_property_attribute_t;
Associated Object
Associated objects are a very useful feature in Runtime, but may be easily overlooked.

Associated objects are similar to member variables, but they are added at runtime. We usually put member variables (Ivar) in the header file of the class declaration, or behind @implementation of the class implementation. But this has a disadvantage, we can not add member variables in the classification. If we try to add new member variables to the classification, the compiler will report an error.

We may hope to solve this problem by using (or even abusing) global variables. But these are not Ivar, because they will not be connected to a separate instance. Therefore, this method is rarely used.

Objective-C provides a solution to this problem: Associated Object.

We can think of the associated object as an Objective-C object (such as a dictionary), which is connected to an instance of the class with the given key. However, because the C interface is used, the key is a void pointer (const void *). We also need to specify a memory management strategy to tell Runtime how to manage the memory of this object. This memory management strategy can be specified by the following values:

OBJC_ASSOCIATION_ASSIGN

OBJC_ASSOCIATION_RETAIN_NONATOMIC

OBJC_ASSOCIATION_COPY_NONATOMIC

OBJC_ASSOCIATION_RETAIN

OBJC_ASSOCIATION_COPY
When the host object is released, the associated object is processed according to the specified memory management strategy. If the specified strategy is assign, the associated object will not be released when the host is released; if the specified is retain or copy, the associated object will be released when the host is released. We can even choose whether to automatically retain / copy. This is very useful when we need to process multithreaded code that accesses related objects in multiple threads.

All we need to do to connect an object to other objects is the following two lines of code:

static char myKey;

objc_setAssociatedObject (self, & myKey, anObject, OBJC_ASSOCIATION_RETAIN);
In this case, the self object will acquire a new associated object anObject, and the memory management strategy is to automatically retain the associated object. When the self object is released, the associated object will be automatically released. In addition, if we use the same key to associate another object, the previously associated object will also be automatically released. In this case, the previous associated object will be properly disposed of, and the new object will use its memory.

id anObject = objc_getAssociatedObject (self, & myKey);
We can use the objc_removeAssociatedObjects function to remove an associated object, or use the objc_setAssociatedObject function to set the associated object specified by key to nil.

Let's use an example to demonstrate how to use related objects.

Suppose we want to dynamically connect a Tap gesture operation to any UIView, and specify the actual operation after clicking as needed. At this time, we can associate a gesture object and operation block object to our UIView object. This task is divided into two parts. First, if necessary, we need to create a gesture recognition object and use it and block as the associated objects. As shown in the following code:

-(void) setTapActionWithBlock: (void (^) (void)) block

{

    UITapGestureRecognizer * gesture = objc_getAssociatedObject (self, & kDTActionHandlerTapGestureKey);



    if (! gesture)

    {

        gesture = [[UITapGestureRecognizer alloc] initWithTarget: self action: @selector (__ handleActionForTapGesture :)];

        [self addGestureRecognizer: gesture];

        objc_setAssociatedObject (self, & kDTActionHandlerTapGestureKey, gesture, OBJC_ASSOCIATION_RETAIN);

    }



    objc_setAssociatedObject (self, & kDTActionHandlerTapBlockKey, block, OBJC_ASSOCIATION_COPY);

}
This code detects the associated objects for gesture recognition. If not, create and establish an association relationship. At the same time, connect the incoming block object to the specified key. Note the associated memory management strategy of the block object.

The gesture recognition object requires a target and action, so next we define the processing method:

-(void) __ handleActionForTapGesture: (UITapGestureRecognizer *) gesture

{

    if (gesture.state == UIGestureRecognizerStateRecognized)

    {

        void (^ action) (void) = objc_getAssociatedObject (self, & kDTActionHandlerTapBlockKey);



        if (action)

        {

            action ();

        }

    }

}
We need to detect the state of the gesture recognition object, because we only need to perform the operation when the tap gesture is recognized.

From the above example, we can see that the associated objects are not complicated to use. It allows us to dynamically enhance the existing functions of the class. We can use this feature flexibly in actual coding.

Member variable and attribute operation method Member variable
Member variable operations include the following functions:

// Get member variable name

const char * ivar_getName (Ivar v);



// Get member variable type code

const char * ivar_getTypeEncoding (Ivar v);



// Get the offset of the member variable

ptrdiff_t ivar_getOffset (Ivar v);
● ivar_getOffset function, for instance variables of type id or other object types, you can call object_getIvar and object_setIvar to directly access member variables without using offset.

Related objects
Related object operation functions include the following:

// Set the associated object

void objc_setAssociatedObject (id object, const void * key, id value, objc_AssociationPolicy policy);



// Get the associated object

id objc_getAssociatedObject (id object, const void * key);



// remove the associated object

void objc_removeAssociatedObjects (id object);
Related objects and related examples have been discussed earlier, and will not be repeated here.

Attributes
Attribute operation related functions include the following:

// Get the attribute name

const char * property_getName (objc_property_t property);



// Get the attribute description string

const char * property_getAttributes (objc_property_t property);



// Get the characteristics specified in the attribute

char * property_copyAttributeValue (objc_property_t property, const char * attributeName);



// Get the attributes of the attribute Sex list

objc_property_attribute_t * property_copyAttributeList (objc_property_t property, unsigned int * outCount);
● The property_copyAttributeValue function, the returned char * needs to be released by calling free () after use.

● The property_copyAttributeList function, the return value needs to be released by calling free () after use.

Examples
Assuming such a scenario, we obtain the same dictionary data from two different interfaces on the server side, but these two interfaces are written by two people, and the same information is represented by different fields. When we receive the data, we can save the data in the same object. The object class is defined as follows:

@interface MyObject: NSObject



@property (nonatomic, copy) NSString * name;

@property (nonatomic, copy) NSString * status;



@end
The dictionary data returned by interfaces A and B are as follows:

@ {@ "name1": "张三", @ "status1": @ "start"}



@ {@ "name2": "张三", @ "status2": @ "end"}
The usual method is to write two methods to do the conversion separately, but if you can use Runtime flexibly, you can only implement one conversion method.

static NSMutableDictionary * map = nil;



@implementation MyObject



+ (void) load

{

    map = [NSMutableDictionary dictionary];



    map [@ "name1"] = @ "name";

    map [@ "status1"] = @ "status";

    map [@ "name2"] = @ "name";

    map [@ "status2"] = @ "status";

}



@end
The above code maps the different fields in the two dictionaries to the same properties in MyObject, so that the conversion method can be handled as follows:

-(void) setDataWithDic: (NSDictionary *) dic

{

    [dic enumerateKeysAndObjectsUsingBlock: ^ (NSString * key, id obj, BOOL * stop) {



        NSString * propertyKey = [self propertyForKey: key];



        if (propertyKey)

        {

            objc_property_t property = class_getProperty ([self class], [propertyKey UTF8String]);



            // TODO: deal with special data types

            NSString * attributeString = [NSString stringWithCString: property_getAttributes (property) encoding: NSUTF8StringEncoding];



            ...



            [self setValue: obj forKey: propertyKey];

        }

    }];

}
Of course, the premise of whether an attribute can be processed in the above way is that it supports KVC.

summary
In this chapter we discussed the content related to member variables and attributes in Runtime. Member variables and attributes are the data foundation of the class. Reasonable use of related operations in Runtime allows us to handle the work related to the class data more flexibly.

Objective-C Runtime Runtime 2: Member variables and attributes


Related Article

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.