Think of how you can add instance variables to all of your objects? We know that using category makes it easy to add methods to existing classes, but it is not possible to increase the instance variables directly. However, starting with Mac OS X v10.6, the system provides associative References, which is an easy problem to solve. This method is called Association (Association), we can dynamically add any number of properties during runtime, and read at any time. The two important runtime APIs used are:
1 |
objc_export void objc_setassociatedobject ( id object, const void *KEY,  id value, Objc_associationpolicy Policy) |
12 |
OBJC_EXPORT id objc_getAssociatedObject( id object, const void *key) __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1); |
Now let's combine a practical example to illustrate their usage. Let's say we're now going to add the Flashcolor property by using the Category property supplement to Uilabel. In General, we have a principle: you can use category extension without inheritance, because with the increase in the depth of inheritance, code maintainability will increase a lot. use category to do this:
12345678 |
#import <UIKit/UIKit.h> #import <objc/runtime.h> @interface UILabel (Associate) //单单从头文件看是不是很像一个类?再看看.m文件你就知道这些都是假象了 - ( nonatomic , retain) UIColor *FlashColor; @end |
12345678910111213 |
#import "UILabel+Associate.h"
@implementation
UILabel (Associate)<br><br>
@dynamic
FlashColor;
static
char
flashColorKey;
- (
void
) setFlashColor:(UIColor *) flashColor{
objc_setAssociatedObject(
self
, &flashColorKey, flashColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIColor *) getFlashColor{
return
objc_getAssociatedObject(
self
, &flashColorKey);
}
@end
|
The above example has several points to note:
1. Key: We notice that the type of key in the function signature is const void *, which means that key is just an address, not the contents of the string, which is also the reason why the Flashcolorkey is not initialized, because we don't care what the specific point is, All we need is the address! If you pass in Setassocaitedobject with Flashcolorkey, the Get method will be nil. The correct one should be the incoming address &flashcolorkey.
2. Policy: The policy here is the same as the retain, assign, and copy in the attribute declaration, no longer repeat
3. @dynamic declaration at the beginning of implement. In general @dynamic and @synthesize can be used to declare attributes, @synthesize is the default declaration, which means that the compiler automatically generates setters and getters for your properties during the compilation phase, while @dynamic tells the compiler, don't panic, boy, The compile phase does not generate a setter and getter for me, I have already implemented the setter and getter myself at runtime. Here we choose @dynamic. In fact, the two have aroused a strong controversy in StackOverflow: please click here.
Let's look at another example from Apple Guide:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465 66676869707172737475767778798081828384858687 |
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
int
main (
int
argc,
const
char
* argv[]) {
@autoreleasepool
{
/*Seciton 0. 关联数据的Key和Value*/
static
char
overviewKey;
static
const
char
*myOwnKey =
"VideoProperty\0"
;
static
const
char
intValueKey =
‘i‘
;
NSArray
*array = [[
NSArray
alloc]
initWithObjects:@
"One"
, @
"Two"
, @
"Three"
,
nil
];
// For the purposes of illustration, use initWithFormat: to ensure
// we get a deallocatable string
NSString
*overview = [[
NSString
alloc]
initWithFormat:@
"%@"
, @
"First three numbers"
];
NSString
*videoKeyValue = @
"This is a video"
;
NSNumber
*intValue = [[
NSNumber
alloc]initWithInt:5];
/*Section 1. 关联数据设置部分*/
objc_setAssociatedObject (
array,
&overviewKey,
overview,
OBJC_ASSOCIATION_RETAIN
);
[overview release];
objc_setAssociatedObject (
array,
myOwnKey,
videoKeyValue,
OBJC_ASSOCIATION_RETAIN
);
objc_setAssociatedObject (
array,
&intValueKey,
intValue,
OBJC_ASSOCIATION_RETAIN
);
/*Section 3. 关联数据查询部分*/
NSString
*associatedObject = (
NSString
*) objc_getAssociatedObject (array, &overviewKey);
NSLog
(@
"associatedObject: %@"
, associatedObject);
NSString *associatedObject2 = (
NSString
*) objc_getAssociatedObject(array, myOwnKey);
NSLog
(@
"Video Key value is %@"
, associatedObject2);
NSString
*assObject3 = (
NSString
*) objc_getAssociatedObject(array, &myOwnKey);
if
( assObject3 )
{
NSLog
(@
"不会进入这里! assObject3 应当为nil!"
);
}
else
{
NSLog
(@
"OK. 通过myOwnKey的地址是得不到数据的!"
);
}
NSNumber
*assKeyValue = (
NSNumber
*) objc_getAssociatedObject(array, &intValueKey);
NSLog
(@
"Int value is %d"
,[assKeyValue intValue]);
/*Section 3. 关联数据清理部分*/
objc_setAssociatedObject (
array,
&overviewKey,
nil
,
OBJC_ASSOCIATION_ASSIGN
);
objc_setAssociatedObject (
array,
myOwnKey,
nil
,
OBJC_ASSOCIATION_ASSIGN
);
objc_setAssociatedObject (
array,
&intValueKey,
nil
,
OBJC_ASSOCIATION_ASSIGN
);
[array release];
}
return
0;
}
|
Programming Little Weng @ Blog Park, thank you for your predecessor's information.
iOS Dynamics: How to add properties dynamically--association (e.g. adding attributes to category)