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".
Related
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
This might be a silly question. I'm learning objective C (iOS) by studying the code and I came across the expression
[InstanceName class];
What does it do?
I tried to search for class method but It just pops up difference between class method and instance method etc. I guess it might give some sort of class object but I have no idea what is the purpose of the statement.
the original code is Sample Facebook App (scrumptious) using FB SDK....
If you see something like this as a standalone expression....
[InstanceName class];
... then the code is most likely forcing the execution of the +initialize method on said class. The first time any method is invoked on a class, the +initialize method will be invoked prior by the runtime. So, have a look at InstanceName and see if it has a +initialize method.
Note that forcing +initialize to execute in this fashion is a sure sign of bad design. +initialize should never need to be forced like this and should not have execution order dependencies.
There is a legitimate additional reason why this line of code might exist. By referring to InstanceName with a hard reference, it'll force the linker to link in all symbols in the library. (If you don't have a hard reference to at least one symbol in a library -- a .a -- some linkers will simply drop the library from the link unit entirely.)
It gets the class of the object.
So for instance if InstanceName is an instance of class Foo
[InstanceName class]; will return Foo, in a variable of type Class
You can use class_getClassName to get an NSString from this class to log it.
class is a method inherited from NSObject. It lets you get the instance of the class object representing the class of the instance on which the method is called.
It can be used to examine the metadata of the current object. For example, you can use class method to determine if a given object is of a particular class:
if ([sender isKindOfClass:[UIButton class]]) {
...
}
It returns the class of the object. Suppose you have an array of UIView subclasses you created and you want to perform some action only to those who belong to a certain class. You could loop through the array and check for each object's class:
for (id view in myViews) {
if ([view isKindOfClass:[MyUIViewSubclass class]]) {
// Do something
}
}
I've implemented +resolveInstanceMethod on a class that subclasses NSDictionary. I will dynamically add methods to my class for certain cases, but I want standard NSDictionary methods to 'just work'.
I thought this would be the case if I just call [super resolveInstanceMethod:sel]; at the end of my method but it doesn't work.
+ (BOOL) resolveInstanceMethod:(SEL)sel
{
BOOL value = [super resolveInstanceMethod:sel]; // this is always NO!?
return value;
}
Why is this? How do I get my class to behave 'normally' for existing methods on the superclass?
What leads you to expect that -resolveInstanceMethod: would return YES for already existing methods? The release notes which introduced the new method say that the default implementation simply returns NO:
The NSObject class implements these methods with a default
implementation that returns NO today, though you should still invoke
it even if you're just directly subclassing NSObject.
In any case, it should never even be called for methods which are already present on the class. It's only called when you message an object with a selector which isn't already "resolved" to an implementation.
Perfect KVO here includes two parts: add observer correctly and remove observer correctly.
The story:
I use one UITableViewCell(cell) to display one NSManagedObject(object).
Each object has some dynamic properties that need observing by its cell.
Not all objects have the same set of observed properties. I add key path observers selectively like this:
if (object.thumbnail_pic_url)
[object addObserver:cell forKeyPath:#"thumbnail_picture" options:0 context:NULL];
Object could be deleted. I must remove observers when object is deleted. The database is very large and complex so I definitely don't want to register all cells to receive moc notifications like NSManagedObjectContextObjectsDidChangeNotification. But I can accept to add a cell ivar in object if I have to, even though it goes agains good Modle-View-Controller design pattern.
The problem: How can I correctly remove the observer(cell) for all the registered key paths from an object when it is deleted?
In fact, it is a big problem that can be divided into two small problems:
Where is the best place to put the observer removing code?
How do I determine which key paths to unregister? I can't query its properties after an object is deleted — it will cause unfulfillable faults, so I can't write code like this:
if (object.thumbnail_pic_url)
[object removeObserver:cell forKeyPath:#"thumbnail_picture"];
and I can't either blindly remove observer for unregistered key path — exceptions(Cannot remove an observer for the key path "thumbnail_picture" from because it is not registered as an observer.) will be thrown up.
an0,
There is an NSManagedObject method just for doing deletion timed functions: -prepareForDeletion.
Its documentation claims: "You can implement this method to perform any operations required before the object is deleted, such as custom propagation before relationships are torn down, or reconfiguration of objects using key-value observing."
You could also look at using: -willTurnIntoFault and -didTurnIntoFault. But I think you'll be happier using -prepareForDeletion.
Andrew
P.S. This method is documented in the class reference. I respectfully suggest that you save time by reading the documentation.
The main problem implementing KVO here is that you don't know when the object is getting deleted , at least not outside the NSManagedObject subclass, What you could really do is create a generic delegate on a subclass of NSManagedObject and override its didChangeValueForKey: method
// DataObservingManagedObject.h
#import <Foundation/Foundation.h>
#import <MMRecord/MMRecord.h>
#protocol DataObservingDelegate <NSObject>
-(void)valueChangedForKey:(NSString*)key andValue:(id)value;
#end
#interface DataObservingManagedObject : NSManagedObject
#property(nonatomic,weak)id<UserStatusDelegate> changeDelegate;
#end
//DateObservingManagedObject.m
#import "DateObservingManagedObject.h"
#implementation DateObservingManagedObject
#synthesize changeDelegate=_changeDelegate;
-(void)didChangeValueForKey:(NSString *)key{
[self.changeDelegate valueChangedForKey:key andValue:[self valueForKey:key]];
}
#end
I believe the mistake is in step 1. Cells should be designed to display different objects. Cells are simply views with labels that can show anything. Tables are optimised to reuse the same cells for different data objects. In your VC create a configureCellWithEvent, configureCellWithVenue methods etc. then you can dequeue a cell of generic identifier and pass it to these methods. Then you likely won’t even have the issue of when to add remove observers because cells shouldn't have object observers.
The current status
I have a superclass named "GeneralCard" that is the super of many "CardsControllers".
Every class that inherits from "GeneralCard" has a different subclass of UIViewController that holds different NSManagedObjects subclasses -
(WhiteCardsController -->WhiteCard,
RedCardsController --> RedCard ...).
I have a lot of functions that are actualy the same for all the managedObjects. so i want to group them in the general card.
The problem
I have created a "General" NSManagedObject called :
NSManagedObject *currentCard.
Now on each View controller I am trying to cast :
self.currentCard = (WhiteCard*)self.currentCard;
So i will be able to use "WhiteCard" properties.
that dosent work as i keep getting errors like -
...Cards View
Controllers/WhiteCardViewController.m:226:
error: request for member 'letter' in
something not a structure or union
As letter is not structured in NSManagedObject but it does in his WhiteCard subclass.
To the question
*How can i share the same variable from the super but cast it differently on each vew controller ?*
Thanks a lot
shani
I would write an accessor method for each child class that returns the typecast object. Something like:
- (WhiteCard *)myTypeCurrentCard {
return (WhiteCard *)self.currentCard;
}
The parent class would have something like:
- (GenericCard *)myTypeCurrentCard {
return (GenericCard *)self.currentCard;
}
Within each controller class with these methods, the message [self myTypeCurrentCard] would return a object cast to the current type. This method does not guarantee the resulting object is actually that type of object, so care must be taken to insure no runtime errors occur by sending non WhiteCard objects white card specific messages.