I read the apple developer document of Customizing Existing Classes and Objective-C Runtime Reference.
I am wondering whether the objc_getAssociatedObject and objc_setAssociatedObject methods must be used with CATEGORY or not.
Is it mean category is used for customing method and objc_getAssociatedObject and objc_setAssociatedObject is used for customing (ususally add) ivar?
Is that make senses to use methods above to add another instance variable individually?
If yes, what's the condition to add instance variable ?
Thanks.
Latest edit:
ViewController.m
[XXView showView:[UIColor greenColor]];
XXView.m
+ (void)showView: (UIColor *)bgcolor {
XXViewController *vc = [[XXViewController alloc] init];
vc.backgroundColor = [self BackgroundColor];
}
+ (void)setBackgroundColor:(UIColor *)BackgroundColor {
objc_setAssociatedObject(self, #selector(backgroundColor), BackgroundColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
+ (UIColor *)BackgroundColor {
if (!objc_getAssociatedObject(self, _cmd)) {
[self setBackgroundColor:[UIColor redColor]];
}
return objc_getAssociatedObject(self, _cmd);
}
The associated object functions can be used in any code; the code just needs to have a reference to the object to which you which to associate another object, a reference to the object you wish to associated, and access to the unique key value.
So yes, you can use them in category methods and this is a way to achieve something similar to an instance variable created and maintained by category methods.
For the unique key value use the address of a static variable – just declared such a variable, any type will do as you are only going to use its address, in the same file as you define the category methods. An address is used as every address in a program is unique.
For the policy argument you probably want OBJC_ASSOCIATION_RETAIN, which means the associated object reference will be retained and released along with the associating object - this mimics the default behaviour of instance variables under ARC.
Not exactly sure what you are asking with "condition to add instance variable", the only conditions are the key must be unique and you can only associate object references - no primitive values, but that is easily addressed if needed by associating an object which contains a primitive valued property. If you wish to associate multiple objects ("add multiple instance variables") then associating a single object with multiple properties is worth consideration.
HTH
Related
I have read through many materials online which all explain when people should use "copy" instead of "strong".
"The copy attribute is an alternative to strong. Instead of taking ownership of the existing object, it creates a copy of whatever you assign to the property, then takes ownership of that. Only objects that conform to the NSCopying protocol can use this attribute..."
And there are plenty of example codes showing when using "copy", the original value stays the same.
However, I'm new to Objective-C. I really want to know how to use the newly assigned value. Where is the "new instance(copy)" with the "new value"? Do I need any additional methods to change the original value if I want to?
It will be great if someone can share an example for this part not the one proving the original value is not changed, which is everywhere.
What the copy attribute does behind the scenes is to create a setter like this:
- (void)setMyCopiedProperty:(MyClass *)newValue {
_myCopiedProperty = [newValue copy];
}
this means that whenever someone does something like this object.myCopiedProperty = someOtherValue;, the someOtherValue is sent a copy message telling it to duplicate itself. The receiver gets then a new pointer (assuming copy is correctly implemented), to which no-one excepting the receiver object has access to.
You can look at copy as being exclusive in some kind of way:
the clients that set the property don't have access to the actual set value
the receiver doesn't have access to the original passed value.
Beware of the caveats, though:
a copied NSArray doesn't copy its objects, so you might end up thinking that a #property(copy) NSArray<MyClass *> *myProperty is safe, however while the array itself is safe from being modified, the objects held by the array share the same reference. Same is true for any collection class (NSDictionary, NSSet, etc)
if the property matches to a custom class you need to make sure the copy method does its job - i.e. creating a new object. This happens for all Cocoa/CocoaTouch classes that conform to NSCopying, however for other classes this might or not be true, depending on implementation (myself I didn't saw yet a class that lies about its copy method, however you never know)
Try this:
Model.h
#interface Model: NSObject
#property (nonatomic,strong)NSString *firstName;
#property (nonatomic,copy) NSString *lastName;
#end
ViewController.m
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
Model *model = [[Model alloc]init];
NSMutableString *str = [[NSMutableString alloc]initWithString:#"test"];
model.firstName = str;
model.lastName = str;
NSLog(#"%#, %#", model.firstName, model.lastName);
[str appendString:#"string"];
NSLog(#"%#, %# ", model.firstName, model.lastName);}
Output :
1st Nslog = "test", "test"
2nd Nslog = "teststring", "test"
An instance of a class is a discrete copy. When you assign an instance of a class to be the value of a property with the copy attribute, a clone of that instance is made and that clone becomes the value of the property. There is no relationship between the original and its clone, so the property does not have access to the original instance at all. Changing an attribute of the property's value is changing the clone.
Note:
If you implement the setter for a copy property, it is your responsibility to ensure it actually creates a copy. As is true with all the attributes for a property, they only have meaning when the compiler is generating (synthesizing) the setter and/or getter for you.
Whenever a property on my object is changed or updated I want to change a variable (nonce variable). This nonce is time-based. So everytime a property is updated this nonce gets updated to the current time.
Is there any way to automatically listen for all key changes on my object? Or will I have to manually maintain all keyvalue observers for each property separately?
Many thanks
Did you take a look at the Obj-C runtime functions? See here in the docs. For example, this gives you a list of all the properties in a class Lender. (BTW: I'm not at my Mac, so this is just straight out of the docs):
#interface Lender : NSObject {
float alone;
}
#property float alone;
#end
you can get the list of properties using:
id LenderClass = objc_getClass("Lender");
unsigned int outCount;
objc_property_t *properties = class_copyPropertyList(LenderClass, &outCount);
you can then get the name of a property:
const char *property_getName(objc_property_t property)
If you pipe those names back into addObserver:forKeyPath:options:context you should be golden.
Some ideas:
1) you can ask the runtime for the properties and ivars, and their types, and use that information to create and take down observers. Obviously a lot of work if you are doing this for one object.
2) if your properties are "regular", meaning all strong objects, then you can use #dynamic (to prevent setter/getter creation), then use resolveInstanceMethod: or other funky methods from NSObject to catch setMyObject: and myObject calls. You could in essence do what the system does for 'set...' calls, and dynamically get the string of the variable. You could then update/get an ivar, maybe one that has a prefix of "-" or something, and you'd be able to do what your observers would be doing.
3) You could put all the ivars in a "Helper" class, and direct all the setters to it (which could of course message you back), using forwardingTargetForSelector:. I'm using this technique (sort of) in one of my github projects
I am reading Apple Doc for understanding property instance variable but bit confused
From Apple Doc:
Most Properties Are Backed by Instance Variables By default, a
readwrite property will be backed by an instance variable, which will
again be synthesized automatically by the compiler.
An instance variable is a variable that exists and holds its value for
the life of the object. The memory used for instance variables is
allocated when the object is first created (through alloc), and freed
when the object is deallocated.
Unless you specify otherwise, the synthesized instance variable has
the same name as the property, but with an underscore prefix. For a
property called firstName, for example, the synthesized instance
variable will be called _firstName.
Although it’s best practice for an object to access its own properties
using accessor methods or dot syntax, it’s possible to access the
instance variable directly from any of the instance methods in a class
implementation. The underscore prefix makes it clear that you’re
accessing an instance variable rather than, for example, a local
variable:
If using accessor methods or dot syntax is best practice then why user _ivarPropertyName?
Why use ivar for presenting properties? what are its benefits? when apple says "using accessor methods or dot syntax is best practice"
#property declares the existence of a property (describing its interface), but doesn't specify the implementation of that property. But properties need to store their contents somewhere. By default, the compiler synthesizes an ivar for that (and matching setters and getters). So normally you can ignore the existence of the ivar and just use dot syntax.
I follow Apple's advice and try to avoid using ivars directly. But somtimes you want to access a property without invoking its getter. The most common exception in my code is lazily-initialized read-only properties:
#interface MyObject : NSObject
#property ( nonatomic, readonly ) id someProperty ;
#end
#implementation MyObject
#synthesize someProperty = _someProperty ; // required; compiler will not auto-synthesize ivars for readonly properties
-(id)someProperty
{
if ( !_someProperty )
{
_someProperty = ... create property here
}
return _someProperty ;
}
#end
Also, you may not want to invoke the getter for a property in your -dealloc method... for example, a timer property. To avoid creating a timer in -dealloc, access the ivar directly:
-(void)dealloc
{
[ _myTimer invalidate ] ; // don't use self.myTimer here, that would create a timer even though we're going away...
}
There are probably more use cases. For most properties you don't even need to use the ivar, just use <value> = self.property and self.property = <new value>.
edit:
Also, there will be some additional overhead for accessing the property via message dispatch (using dot-accessor syntax or the getter) vs directly accessing the ivar, but it will make no difference in almost all cases.
I have placed all of my NSManagedObject's custom logic in a category, so that I can regenerate the standard classes from my model if an when it changes.
One such piece of logic I require is a custom setter on one of the object's properties:
- (void) setName:(NSString *)name
{
[self willChangeValueForKey:#"name"];
[self setPrimitiveValue:name forKey:#"name"];
[self didChangeValueForKey:#"name"];
NSLog(#"name was changed");//for example
}
I have placed this in the category, which in this case is Item+Custom.m
My question:
Why is it that, whenever I set the name of an Item, it is not necessary to import Item+Custom.m? The log statement above still fires.
Just curious how the class sending the message doesn't need to know about the category for the logic to still fire?
And (perhaps a separate issue) what would happen if I added the same custom setter, with a different logging statement, to a second category on the same object?
When a program is loaded, all category methods are made known to the runtime. So if you declare a -[Item setName:] method, then Core Data will not create this method at runtime anymore.
You need not import anything because name is already declared as a #dynamic property in the Xcode generated managed object subclass files.
If two categories declare the same method, or if the name of a method declared in a category is the same as a method in the original class, the behavior is undefined, see Avoid Category Method Name Clashes in "Programming with Objective-C".
Can someone explain to me the significance of creating (what seems to be) an extra variable and why put an underscore before a new variable with the same name as the last.
I have read that it is to do with creating instance variables but why would you want to create a UI element as an instance variable? Can you please provide a use case?
This objective c code for use in iOS development.
Thanks.
When you #synthesize a property and you do not provide your own getter and setter methods (in which case there is no need to #synthesize) then there is always a new instance variable created. By default it gets the same name as the property. So #synthesize slider; makes an instance variable named slider behind the scenes.
The problem here is that you might mistakenly type slider = xxx when you really meant to use self.slider = xxx. When you make something a property, best practice says you should always access it through self.propertyName (except in your init and dealloc methods and any custom getter and setter methods).
So in order to avoid this, the #synthesize statement is used to rename the backing ivar into something that is harder to confuse with the property. If you now use slider instead of self.slider the compiler will give an error message because slider is no longer the name of an instance variable.
The reason for doing that is to make the instance variable clearly stand out from the property dotting syntax. It also has the practical effect of avoiding shadowing of instance variables from argument names, which also occur in some situations.
The reason for using an instance variable at all is in most cases to avoid KVO triggering in dealloc. If you do this, you risk triggering KVO in such a way that your observers gets a deallocated object passed to them, causing an EXC_BAD_ACCESS.
- (void)dealloc
{
self.slider = nil;
[super dealloc];
}
So it's common to do this instead, which will not trigger KVO since you don't do property access.
- (void)dealloc
{
[_slider release];
[super dealloc];
}
This is commonly used to synthesize the property to a private prefixed or suffixed ivar. It tries to prevent you from accidentally accessing the ivar and not the property or overriding the ivar with a method argument.
Consider this:
#implementation MYClass
#synthesize flag = flag_;
- (void)doSomethingWithFlag:(BOOL)flag {
if (flag) {
// You do not need to worry about confusing the ivar
// flag and the param flag because it is synthesized to flag_
}
}
- (void)doSomething {
if (flag) { // Doesn't work -> use accessor self.flag
...
}
}
#end