Dirty flags on Realm objects - objective-c

Can anyone suggest a good pattern for implementing a dirty flag on Realm objects? Specifically, I would like every subclass of Realm Object to expose an isDirty flag that gets set whenever an instance of the class is modified and is cleared whenever the instance is written to the cloud (not the Realm). I'm working in Objective-C.
Possible solutions I can think of include the following:
Write a custom setter for every property of every objects. Set isDirty within each of those setters. Not very desirable.
Use KVO in some way. Two problems with this approach: (a) I don't fully understand how to implement this approach, and (b) Realm doesn't support KVO for managed objects (which are exactly the objects I need it for).
Use Realm notifications. Again, I don't have experience with these, and I'm not sure how to use them for this purpose.

Short of simply having a non-managed isDirty property that you manually set after performing each write transaction, KVO would be the best way to go.
Setting custom setters would indeed be incredibly messy. You'd have to have a separate one for each property you wanted to track.
Realm notifications would only work if you were tracking a set of objects and wanted to be alerted if any were changed (using collection notifications) or if anything in the Realm changed.
With KVO, you could potentially get your object subclass itself to add observers to all of its properties, which are then channeled to one method whenever any of them change, this could then be used to mark the isDirty property.
#interface MyObject: RLMObject
#property NSString *name;
#property NSInteger age;
#property BOOL isDirty;
- (void)startObserving;
- (void)stopObserving;
#end
#implementation MyObject
- (void)startObserving
{
NSArray *properties = self.objectSchema.properties;
for (RLMProperty *property in properties) {
[self addObserver:self forKeyPath:property.name options:NSKeyValueObservingOptionNew context:nil];
}
}
- (void)stopObserving
{
NSArray *properties = self.objectSchema.properties;
for (RLMProperty *property in properties) {
[self removeObserver:self forKeyPath:property.name];
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey,id> *)change
context:(void *)context
{
self.isDirty = YES;
}
+ (NSArray *)ignoredProperties {
return #[#"isDirty"];
}
#end
Obviously you'd want to do more checking in here than I've done (to make sure isDirty truly needs to be set), but this should give you an idea.
There's no real way to automatically know when a managed Realm object has been created, so it would be best for you to manually start and stop observing as you need it.

Related

Objective-C: Proper handling of shared properties between subclassed NSControl and NSActionCell?

One of the things I've always been unsure of is how to properly handle communication between a custom NSControl subclass and an NSCell subclass. Throughout my introduction to Cocoa, I've seen it mentioned several times how the parent control provides many of the same methods, accessors, and mutators as the child cell's/cells' implementation. For example, the NSControl class and NSCell class both have -isEnabled and -setEnabled: in their header files:
NSControl.h
- (BOOL)isEnabled;
- (void)setEnabled:(BOOL)flag;
NSCell.h
- (BOOL)isEnabled;
- (void)setEnabled:(BOOL)flag;
I understand that the NSControl class provides "cover" methods for most of the properties found in NSCell. What I'm more interested in knowing is: how are they implemented? Or even better, how should one implement his/her own subclasses' shared properties? Obviously, only Apple's engineers really know what's happening on the inside of their frameworks, but I thought maybe somebody could shed some light on the best way to mimic Apple's cover method approach in a nice clean way.
I'm horrible at explaining stuff, so I'll provide an example of what I'm talking about. Say I've subclassed NSControl like so:
BSDToggleSwitch.h
#import "BSDToggleSwitchCell.h"
#interface BSDToggleSwitch : NSControl
#property (nonatomic, strong) BSDToggleSwitchCell *cell;
#property (nonatomic, assign) BOOL sharedProp;
#end
And I've subclassed NSActionCell:
BSDToggleSwitchCell.h
#import "BSDToggleSwitch.h"
#interface BSDToggleSwitchCell : NSActionCell
#property (nonatomic, weak) BSDToggleSwitch *controlView;
#property (nonatomic, assign) BOOL sharedProp;
#end
As you can see, they both share a property called sharedProp.
My question is this: What's the standard way to effectively keep the shared property synchronized between the control and the cell? This may seem like a subjective question, and I suppose it is, but I'd like to think that there is a most-of-the-time "best way" to do this.
I've used all sorts of different methods in the past, but I'd like to narrow down the ways in which I handle it, and only use the techniques which provide the best data integrity with the lowest overhead. Should I use bindings? What about implementing custom mutators which call their counterparts' matching method? KVO? Am I a lost cause?
Here are some of the things I've done in the past (some or all of which could be totally whacky or straight-up wrong):
Cocoa Bindings — I'd just bind the control's property to the cell's property (or vice versa):
[self bind:#"sharedProp" toObject:self.cell withKeyPath:#"sharedProp" options:nil];
This seems like a pretty good approach, but which object would you bind to/from? I've read all of the KVO/KVC/Bindings documentation, but I've never really picked up on the importance of the binding's directionality when the property should be the same in either case. Is there a general rule?
Send Messages from Mutators — I'd send the cell a message from the control's mutator:
- (void)setSharedProp:(BOOL)sharedProp
{
if ( sharedProp == _sharedProp )
return;
_sharedProp = sharedProp;
[self.cell setSharedProp:sharedProp];
}
Then I'd do the same thing in the cell's implementation:
- (void)setSharedProp:(BOOL)sharedProp
{
if ( sharedProp == _sharedProp )
return;
_sharedProp = sharedProp;
[self.controlView setSharedProp:sharedProp];
}
This also seems fairly reasonable, but it also seems more prone to errors. If one object sends a message to the other without a value check, an infinite loop could happen pretty easily, right? In the examples above, I added checks for this reason, but I'm sure there's a better way.
Observe and Report — I would observe property changes in each object's implementation:
static void * const BSDPropertySyncContext = #"BSDPropertySyncContext";
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
if ( context == BSDPropertySyncContext && [keyPath isEqualToString:#"sharedProp"] ) {
BOOL newValue = [[change objectForKey:NSKeyValueChangeNewKey] boolValue];
if ( newValue != self.sharedProp ) {
[self setSharedProp:newValue];
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
Once again, this seems doable, but I don't like having to write an if statement for every single shared property. Along the lines of observation, I've also sent notifications, but since the control-cell relationship is about as "one-to-one" as one can get (sweet pun intended), that just seems silly.
Again, I know this is a bit subjective, but I'd really appreciate some guidance. I've been learning Cocoa/Objective-C for awhile now, and this has bothered me since the beginning. Knowing how others handle property syncing between controls and cells could really help me out!
Thanks!
First, note that NSCell mostly exists because of performance issues from the NeXT days. There has been a slow migration away from NSCell, and you generally shouldn't create new ones unless you need to interact with something that demands them (such as working with an NSMatrix, or if you're doing something that looks a lot like an NSMatrix). Note the changes to NSTableView since 10.7 to downplay cells, and how NSCollectionViewItem is a full view controller. We now have the processing power to just use views most of the time without needing NSCell.
That said, typically the control just forwards messages to the cell. For instance:
- (BOOL)sharedProp {
return self.cell.sharedProp;
}
- (void)setSharedProp:(BOOL)sharedProp {
[self.cell setSharedProp:sharedProp];
}
If KVO is a concern, you can still hook it up with keyPathsForValuesAffectingValueForKey: (and its relatives). For instance, you can do something like:
- (NSSet *)keyPathsForValuesAffectingSharedProp {
return [NSSet setWithObject:#"cell.sharedProp"];
}
That should let you observe sharedProp and be transparently forwarded changes to cell.sharedProp.

Objective-C KVO doesn't work with C unions

I need to observe union-typed properties on an Objective-C class using KVO, but it seems I have no luck with this. I did some experiments: everything works fine as long as I am using a C struct. As soon as I replace the struct with a union, automatic KVO doesn't work anymore (observeValueForKeyPath is not being called).
Here's my small test class:
AppDelegate.h:
#import <Cocoa/Cocoa.h>
typedef union {
float data[3];
struct {
float x,y,z;
};
} vec3union;
typedef struct {
float x,y,z;
} vec3struct;
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property (assign) IBOutlet NSWindow *window;
#property (assign) vec3struct vectorStructValue;
#property (assign) vec3union vectorUnionValue;
#end
AppDelegate.m:
#implementation AppDelegate
#synthesize vectorStructValue = _vectorStructValue;
#synthesize vectorUnionValue = _vectorUnionValue;
- (void)dealloc
{
[super dealloc];
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[self addObserver:self forKeyPath:#"vectorStructValue" options:NSKeyValueObservingOptionNew context:nil];
[self addObserver:self forKeyPath:#"vectorUnionValue" options:NSKeyValueObservingOptionNew context:nil];
self.vectorStructValue = (vec3struct){1,2,3};
self.vectorUnionValue = (vec3union){4,5,6};
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
NSLog(#"keyPath %# did change, object: %#", keyPath, [object description]);
}
#end
Output:
2013-01-12 17:38:26.447 KVOTest[57522:303] keyPath vectorStructValue did change, object: <AppDelegate: 0x100614200>
Am I doing something wrong or is this a bug or missing feature in the Objective-C runtime/KVO implementation?
Note: I know I can implement this manually, by overriding the property setter, but this is not the point of this question. The answer should give me an idea of why the automatic KVO doesn't work in this case.
Update: Just to make this clear, this is a simple test case comparing the KVO observer on a struct property to that on a union property. These properties are not interrelated. They have independent ivars with independent memory backing stores. You can remove the struct property and run the test, still the same result – no KVO observer event for the union property.
The properties aren't related in OP's question. I misread that in a fever induced hallucination.
Unions are just flat out busted in KVO/KVC. Leaving the text below because it is still interesting.
KVO doesn't work by watching memory or playing any such tricky shenanigans like that. It works by dynamically creating a subclass on the fly, overriding the setter method, and invoking the willChange.../didChange... methods automatically when the setter is called.
Thus, you effectively have 2 properties with 1 backing store. As far as KVO is concerned, though, they are in total isolation from each other.
What you want is dependent keys. You can use +keyPathsForValuesAffectingValueForKey: to create a dependency between the two keys such that calling either setter will trigger a change for the other property.
I don't know if it supports co-dependnence; if it supports what would effectively be a circular dependency.
Alternatively, you ought to be able to override the setter to call willChange/didChange for the other property (as well as the property being changed).
The related keys would be used if you want willChange/didChange to fire for both keys if either property changes. I.e. if you muck with the struct, the union effectively changes and observers of the union property should see a will/did change in response to setting the struct version.
I just tested it. You're right. Something is odd with unions. It is flat out broken. All of the above still remains true, but it does no good.
Radar filed: rdar://problem/13003794
Oooh... neat. KVO w/unions simply doesn't work. It appears that the runtime simply does not even recognize that the class has a key called vectorUnionValue at all.
I added:
+ (NSSet *)keyPathsForValuesAffectingVectorStructValue
{
return [NSSet setWithObject:#"vectorUnionValue"];
}
+ (NSSet *)keyPathsForValuesAffectingVectorUnionValue
{
return [NSSet setWithObject:#"vectorStructValue"];
}
Which caused a runtime exception:
2013-01-12 12:05:11.877 djkdfjkdfjkdf[51598:303] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<AppDelegate 0x10010a520> valueForUndefinedKey:]: this class is not key value coding-compliant for the key vectorUnionValue.'

Test type of NSNotification

I need to check whether an object is an NSNotification. It is not enough to know if it is a subclass, as I want to differentiate between whether it is an NSNotification or a subclass of NSNotification.
So to elaborate I need to differentiate between the following:
An NSConcreteNotification
A Subclass of NSNotification (But not NSConcreteNotification)
The problem is that NSNotifications are actually NSConcreteNotifications and NSConcreteNotification is a private class so I can't use it to test against.
[object isMemberOfClass: [NSNotification class]] // returns NO in both cases
[object isKindOfClass: [NSNotification class]] // returns YES in both cases
There is no reason to subclass NSNotification the way you're describing. First, NSNotification already carries a userInfo dictionary. You can put any data you want in there. You can use category methods to read and write into that dictionary if you like (I do this all the time). For example, a very common thing I want to do is pass along some object, say the RNMessage. So I create a category that looks like this:
#interface NSNotificationCenter (RNMessage)
- (void)postNotificationName:(NSString *)aName object:(id)anObject message:(RNMessage *)message;
#end
#interface NSNotification (RNMessage)
- (RNMessage *)message;
#end
static NSString * const RNMessageKey = #"message";
#implementation NSNotificationCenter (RNMessage)
- (void)postNotificationName:(NSString *)aName object:(id)anObject message:(RNMessage *)message {
[self postNotificationName:aName object:anObject userInfo:[NSDictionary dictionaryWithObject:message forKey:RNMessageKey];
}
#end
#implementation NSNotification (RNMessage)
- (RNMessage *)message {
return [[self userInfo] objectForKey:RNMessageKey];
}
As #hypercrypt notes, you can also use associated references to attach data to any arbitrary object without creating an ivar, but with NSNotification it's much simpler to use the userInfo dictionary. It's much easier to print notification using NSLog. Easier to serialize them. Easier to copy them. Etc. Associated references are great, but they do add lots of little corner cases that you should avoid if you can get away with it.
That sounds like a really bad idea. When you first receive the notification, you already know what type it is, because it's passed as an explicit argument to a notification callback method. Consider storing the notification as a strongly typed property of another object, or inserting in a dictionary under an appropriate key if you're adding it to a collection, or passing it to other methods that don't preserve the type information to make it easier to identify later.
Creating dependencies on private API (including the names of private classes) will make your code more fragile, and much more likely to break in a future release. Obviously, one of the reasons these classes are private is to make it easier for Apple's engineers to change them as they see fit. For example, the concrete subclasses used by NSArray and NSMutableArray just changed in a recent release of the SDK.
To test id object is an NSNotification use:
[object isMemberOfClass:[NSNotification class]];`
To test if it is a NSConcreteNotifications use
[object isMemberOfClass:NSClassFromString(#"NSConcreteNotifications")];
Change the string to the name of a different class as needed...
You can then combine the two check for 'A Subclass of NSNotification (But not NSConcreteNotification'.
Either:
if ([object isMemberOfClass:NSClassFromString(#"NSConcreteNotifications")])
{
// It's a NSConcreteNotifications...
}
else if ([object isKindOfClass:[NSNotification class]])
{
// It's an NSNotification (or subclass) but not an NSConcreteNotifications
}
Or
if ([object isKindOfClass:[NSNotification class]] && ![object isMemberOfClass:NSClassFromString(#"NSConcreteNotifications")])
{ /* ... */ }
If you want to add properties to NSNotifications you should look into Associative References.
The basic idea is:
static const char objectKey;
- (id)object
{
return objc_getAssociatedObject(self, &objectKey);
}
- (void)setObject:(id)object
{
objc_setAssociatedObject(self, &objectKey, object, OBJC_ASSOCIATION_RETAIN);
}
As others have pointed out, it is a bad idea to rely on the name of a private class. If you are looking for one specific subclass, you could just explicitly check for that class.
[notification isMemberOfClass:[MyNotificationSubclass class]];
You could use multiple statements to check for multiple subclasses, but that would be a little cluttered. This method also requires changes every time you add a new class to look for. It may be better to define a readonly property which indicates whether a notification supports the feature you are looking for, so you aren't relying on the class so much as the ability of the class. You could use a category on NSNotification which simply returns NO for this property, and any subclasses which have the feature would override the method to return YES.
#interface NSNotification (MyFeature)
#property (readonly) BOOL hasMyFeature;
#end
#implementation NSNotification (MyFeature)
- (BOOL)hasMyFeature {
return NO;
}
#end
In the subclasses which support it:
- (BOOL)hasMyFeature {
return YES;
}
- (void)performMyFeature {
...
}
This would also allow you to change whether or not a notification has the feature enabled by changing a flag which is returned for hasMyFeature, and your checking code would simply be:
if(notification.hasMyFeature) [notification performMyFeature];

Responding to setters

What is the best way to respond to data changes when property setters are called. For example, if I have a property called data, how can I react when [object setData:newData] is called and still use the synthesised setter. Instinctively, I would override the synthesised setter like so:
- (void)setData:(DataObject *)newData {
// defer to synthesised setter
[super setData:newData];
// react to new data
...
}
...but of course this doesn't make sense - I can't use super like this. So what is the best way to handle this situation? Should I be using KVO? Or something else?
There are a few different ways to do this, depending on how much control you want. One way to do it is to observe your own property:
[self addObserver:self forKeyPath:#"data" options:0 context:nil];
- (void)observeValueForKeyPath:(NSString *)path ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if(object == self && [path isEqualToString:#"data"]) {
//handle change here
} else [super observeValueForKeyPath:path ofObject:object change:change context:context];
}
Make sure you remove yourself as an observer in your dealloc or finalize method, if not before.
Another way would be to override -didChangeValueForKey:. However, this method may not be called if there are no observers on the object.
- (void)didChangeValueForKey:(NSString *)key {
[super didChangeValueForKey:key];
if([key isEqualToString:#"data"]) {
//handle change here
}
}
#synthesize creates default accessors for easy use. In case some special action is needed then it is always possible to write own accessors instead of using #synthesize. The setter and getter are not inherited from base class, they are created by the #synthesize directive. So you don't need to (neither you can) call super setData: (unless you really have created super class that support that).
Just ensure that you are managing memory correctly. Memory Management Programming Guide contains examples on how to manage memory for different types of memory policy (retain or assign or copy).
From this SO answer.
You can define a synthesized "private" property, (put this in your .m file)
#interface ClassName ()
// Declared properties in order to use compiler-generated getters and setters
#property (nonatomic, strong <or whatever>) NSObject *privateSomeObject;
#end
and then manually define a getter and setter in the "public" part of ClassName (.h and #implementation part) like this,
- (void) setSomeObject:(NSObject *)someObject {
self.privateSomeObject = someObject;
// ... Additional custom code ...
}
- (NSArray *) someObject {
return self.privateSomeObject;
}
You can now access the someObject "property" as usual, e.g. object.someObject. You also get the advantage of automatically generated retain/release/copy, compatibility with ARC and almost lose no thread-safety.

Using one setter for all model iVars

I have a series of models for my application. Across all these models there are (will be) some 200 or 300 instance variables. The application stores its persistent data on a web-based server (MySQL - but I guess that part doesn't matter). Whenever a model iVar is updated I need to make a call to the server to update the appropriate value for that iVar.
My current model strategy is (header file):
#interface MyModel : NSObject {
NSString * firstName;
NSString * lastName;
}
#property (readwrite, copy) NSString * firstName;
#property (readwrite, copy) NSString * lastName;
#end
(implementation file):
#implementation MyModel
#synthesize firstName;
#synthesize lastName;
-(id)init {
[super init]
[self setFirstName:#"George"];
[self setLastName:#"Kastanza"];
return self;
}
-(void)setFirstName:(NSString *)aName {
// call method to update server with new value here
firstName = aName;
}
-(void)setLastName:(NSString *)aName {
// call method to update server with new value here
lastName = aName;
}
#end
The problem is that if I have 200 or 300 iVar's all needing to go through the same update call to the server that means writing a lot of setters. Moreover, if I need to make a change to the method call, I'd have to update each and every method in every setter i the entire application.
Is there a process by which I could run every set of an iVar through a method first, before setting?
I thought of having just a NSMutableDictionary per model object to store all of the iVar's, but that abstracts the setters and getters and may introduce a big memory footprint for so many dictionaries. However, doing it this way means that every time the dictionary is set I could pass it through one method.
As I understand it dynamically adding iVar's at runtime to an object model is considered a bad thing because of the pointer referencing for any subclasses that may be dependent upon the model (the subclass pointer doesn't get offset unless a complete recompile is done).
Any ideas and suggestions much appreciated.
Update
Based upon Ole's recommendation here is the solution (although it uses a little more code than a few lines unfortunately)...
In the model I added a method that I can set when I need to. I didn't call the method directly from the init, because adding a whole bunch of results returned from the server would trigger the observers for every object added. So I call the method after I have initialized and updated the first grab from the server.
Here's the code...
-(void)registerObservers {
[self addObserver:self
forKeyPath:#"firstName"
options:NSKeyValueObservingOptionNew
context:NULL];
[self addObserver:self
forKeyPath:#"lastName"
options:NSKeyValueObservingOptionNew
context:NULL];
}
Then I add the observer to the model:
-(void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context {
if ([keyPath isEqual:#"firstName"]) {
// Do whatever I need to do
}
if ([keyPath isEqual:#"lastName"]) {
// Do whatever I need to do
}
}
In my real implementation I also happen to post a notification of the object set to self so that I can update anything that should be listening but isn't paying attention (like stuff in NSArrayControllers).
Use Key-Value Observing. You have to manually register yourself as an observer for every property, though.