I have a class that I need to extend by a (readonly) property exposing a collection. That collection is not backed by an instance variable. Instead it contains a filtered subset of elements of another collection declared on the same class. I need that subset property to be KVO compliant so I can bind to it. I can't manipulate or subclass my particular class to achieve that, so I need to accomplish my goal in a category.
In the case of a non-collection property depending on a non-collection property it would be very simple to make it KVO compliant even in a category. I could just implement
+ (NSSet<NSString *> *)keyPathsForValuesAffectingValueForDependentProperty {
return [NSSet setWithObject:#"originalProperty"];
}
However where Apple suggests that, the documentation also states: "The keyPathsForValuesAffectingValueForKey: method does not support key-paths that include a to-many relationship." Indeed it doesn't. And how should it? The KVO framework can't know in which way and when a dependent collection will change when the original collection is altered. For that problem Apple than proposes two solutions:
Either registering as observer for the relevant key paths or
Registering for notifications of the responsible NSManagedObjectContext instance in case of using Core Data.
Since I am using a category both options seem to be inapplicable. Where would I register and (safely) unregister? I can't just start replacing methods of the base class like - (instancetype)init; or dealloc, and I can't extend them because I'm not in a subclass, so no calls to super. What is the best way to achieve this? Method swizzling? Mixing in some dirty lowland C code I haven't heard about yet calling to the Obj-C runtime? Something very obvious I've overlooked? To keep things simple: In my case it would suffice if I can send just willChangeValueForKey: and didChangeValueForKey: messages for my dependent property on every change of the original property, no matter the kind of change.
FinalClass.h:
#interface FinalClass : ...
#property NSSet *someCollection;
...
#end
FinalClass+Category.h
#interface FinalClass (Category)
#property (readonly) NSSet *someCollectionSubset;
#end
FinalClass+Category.m
#implementation FinalClass (Category)
+ (NSSet<NSString *> *)keyPathsForValuesAffectingValueForSomeCollectionSubset {
// Not working here because the key path contains a to-many relationship
return [NSSet setWithObject:#"someCollection"];
}
- (NSSet *)someCollectionSubset {
// Change notifications for that subset needed
// No mutations needed, primitive change notifications would do
return [self.someCollection filteredSetUsingPredicate:...];
}
#end
Thanks #TheDreamsWind, I've found a very simple answer:
Simple solution
Even though the signature of the general method for automatic KVO updates for dependent keys is keyPathsForValuesAffectingValueForKey:, the key-specific signature the KVO framework is looking for is keyPathsForValuesAffecting<Key> and not keyPathsForValuesAffectingValueFor<Key>. After I changed my method name accordingly, automatic KVO updates started to work with me.
However that is supposed to work only when the dependency as a whole changes. In my case it also creates simple change messages without set mutations when members of the original collection changes, even though it's not supposed to do so as I understand the documentation.
Complex solution
Another solution I found is swizzling the four change message methods willChangeValueForKey:, didChangeValueForKey:, willChangeValueForKey:withSetMutation:usingObjects: and didChangeValueForKey:withSetMutation:usingObjects: with new implementations, that call the original implementation, as well as generate an additional change message for the dependent key if needed:
- (void)willChangeValueForKeySwizzled:(NSString *)key {
// Call the original implementation
[self willChangeValueForKeySwizzled:key];
// Send additional change messages if applicable and needed
if ([FinalClass.keyPathsForValuesAffectingValueForDependentProperty containsObject:key]) {
[self willChangeValueForKey:NSStringFromSelector(#selector(dependentProperty))];
}
}
...
That approach could be used to generate also set mutation change messages for better efficiency. In my case, that efficiency gain would mean premature (over)optimizing, so I stick with the simple solution proposed.
Related
I have an object that used to be an NSMutableSet but needed some more stuff attached to it. The obvious (and obviously not supported) thing to do is to subclass NSMutableSet and tack on the two additional properties. Since NSMutableSet, like basically all Cocoa data structures, is a class cluster I cannot subclass it in the usual way, since the super class just throws exceptions. This led me down several paths.
The first path was to create sort of a composite object that declared itself as a subclass of NSMutableSet but really just forwarded the invocations to an internal NSMutableSet. I didn't want to have to implement every method on NSMutableSet, so I thought forwardInvocation: would be a good way to accomplish my mission. Unfortunately, the abstract class of NSMutableSet implements all of the methods on the interface and their implementations throw exceptions, so I was never getting to the point where I could forward an invocation.
The second path was to subclass NSProxy and forward the invocation from there. This solution falls short in that I need to copy the interface of NSMutableSet over unless there's a way to declare "this class implements this interface" that I don't know about (this could very well be the solution).
The third path was to create a category on NSMutableSet and import it just for the class that needs to use it but that falls short since you cannot add non-dynamic properties via a category. That led me to using associated objects in a category. I'm willing to admit that that is the correct solution for this use case, but I wish it weren't since it's kind of clunky. It's doubly clunky since the properties I'm adding are primitive so I'll have to wrap and unwrap them when setting and getting the association (unless there's a way to associate primitives which I'm unfamiliar with).
Essentially, what I would like is something that behaves functionally as a subclass of NSMutableSet (and all class clusters) but cannot figure out the best approach. Thanks!
Trying to subclass Cocoa class clusters will just create an awful lot of hurt. It may seem a good idea, but you will forever run into problems.
Just create an NSObject with an NSMutableSet as the first member object.
Subclassing Cocoa class cluster is kind of discouraged. Not without reasons. Please do not enter this crashy world.
Either of your solutions will work. I've successfully used the first path with NSArray and NSDictionary, so I believe it should work fine for NSMutableSet as well. Just remember that you need to override not only forwardInvocation:, but a few of other methods as well. Please consult Surrogate Objects sections of Apple docs:
Although forwarding mimics inheritance, the NSObject class never confuses the two. Methods like respondsToSelector: and isKindOfClass: look only at the inheritance hierarchy, never at the forwarding chain.
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtForwarding.html
In my case, I've overridden:
conformsToProtocol:
isKindOfClass:
isMemberOfClass:
respondsToSelector:
instancesRespondToSelector:
forwardInvocation:
methodSignatureForSelector:
instanceMethodSignatureForSelector:
from which isKindOfClass:, conformsToProtocol: and respondsToSelector: are definitely crucial.
I've also used the third path with good results, but I admit the associated objects API is clunky.
First, gnasher729 is correct. Don't subclass class clusters. Just don't do it. Can you do it? If I tell you that you can't, will it help you convince yourself that you shouldn't? I can lie if it helps you make good choices.
But in all seriousness, it is almost always meaningless as well. Is your subclass really a specific kind of set? Or is it really kind of like a set. Consider NSAttributedString. It isn't a kind of string, it has-a string. This is almost always better.
And also, class clusters happen to be a royal pain to subclass.
That said, adding associated values onto a data structure, as you've already discovered, is generally just fine, because what you really want is "hey, I have some data that needs to go along with this other data." Wrapping has gotten so easy that it shouldn't really slow you down. See https://stackoverflow.com/a/14918158/97337:
objc_setAssociatedObject(self, animatingKey, #(value), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
And with "one weird trick", you can make this really easy:
#interface NSObject (BoolVal)
#property (nonatomic, readwrite, assign) BOOL boolVal;
#end
#implementation NSObject (BoolVal)
- (BOOL)boolVal {
return [objc_getAssociatedObject(self, _cmd) boolValue];
}
- (void)setBoolVal:(BOOL)value {
objc_setAssociatedObject(self, #selector(boolVal), #(value), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#end
But I'd still come back to the question of whether this is really a kind of set (rather than just like a set), and whether it really needs to respond to every message that can be sent to a set. As with NSAttributedString, your real needs are often much smaller than that in practice, and wrapping the handful of methods you need is often worth the simplicity and control.
For completeness, let's look at your first path:
create sort of a composite object that declared itself as a subclass of NSMutableSet but really just forwarded the invocations to an internal NSMutableSet
Can you subclass an NSMutableSet? Yes, but should you? The documentation for NSMutableSet says:
Subclassing Notes
There should be little need of subclassing. If you need to customize behavior, it is often better to consider composition instead of subclassing.
So weigh that up and if you want to subclass refer again to the documentation:
Methods to Override
In a subclass, you must override both of its primitive methods:
addObject:
removeObject:
You must also override the primitive methods of the NSSet class.
And looking at the NSSet class documentation we find its primitive methods are:
Methods to Override
In a subclass, you must override all of its primitive methods:
count
member:
objectEnumerator
That's it, 5 methods.
You can define your own class as a subclass of NSMutableSet, add an instance variable which is an instance of NSMutableSet, implement 5 methods and redirect them to the set instance, add whatever init methods you wish, and then add your additional properties.
If performance is of concern then the tradeoff is between redirecting those five methods and accessing associated objects for your additional properties. You'll need to profile to work that out, but if and only if performance becomes an issue.
I have a subclass of an NSManagedObject, and I'd like to add a couple ivars to keep track of some book-keeping. I don't want these vars to persist, and so that is why I don't include them as part of the data model.
I'm having trouble finding the proper way of doing this.
should I just create ivars in my class, create the corresponding property and then synthesis them in the implementation?
should I not create ivars, and instead just declare the property and then #dynamic the property in the implmentation?
is there some other approach i should be taking?
And should I be doing all the customization in the my NSManagedObject subclass, or should I be creating a subclass of my subclass, so that if I change my data model I won't lose all my customizations when I get xcode to generate my NSManagedObject subclass automatically?
Thanks!
Each attribute for NSManagedObject has a checkbox named transient. This way you'll have dynamic accessors for the object without actually persisting the property value itself. Look for checkbox right under the text field for attribute name entry.
UPDATE If you don't want to create a migration because of new attributes, make standard ivars
#interface MyMO : NSManagedObject {
NSString *_nonPersistentAttribute;
}
#property (nonatomic, retain) NSString *nonPersistentAttribute;
#end
#implementation MyMO
#synthesize nonPersistentAttribute=_nonPersistentAttribute;
#end
The documentation for NSManagedObject has a section called Custom Instance Variables. It discusses both approaches.
Both transient, modeled attributes and ivars are good solutions. It depends on taste and style.
One very important point to remember if you use ivars: You need to clear out ivars in - (void)didTurnIntoFault and set any initial values in - (void)awakeFromFetch and - (void)awakeFromInsert. You need to not touch any modeled attributes or relationships inside -didTurnIntoFault or else you'll get in trouble.
Wise,
To your first question re: ivars, you have two choices standard ivars or transient attributes on your entity. The big difference between the two is that the transient attributes participate in the change/dirty/undo aspects of Core Data. If your ivars don't need that, then don't use the transient attributes. (Yes, use #property and #synthesize for your ivars.)
To your second question re: where to make the changes? I am somewhat of a luddite. The header patterns automatically generated by Xcode are pretty simple. Hence, I use the auto generated files for the first time I create an entity and edit in my changes thereafter. Frankly, you don't change your model data structures very often. Hence, adding a few lines here and there to both .h&.m files isn't a big cost. There are other mechanisms. Wolf Rentzch's mogenerator system is well respected and useful. That said, I'm not convinced that mogenerator solves a problem faced by modern Objective-C v2 Core Data programmers. (Obj-C v2 has made many things easier for Core Data programmers.)
Andrew
Here is a convenient pattern I have used in the past:
Create a category of your class and put it into an additional source file, such as ManagedObject+Support.h. Remember, in the interface declaration you just use brackets like this:
#interface ManagedObject (Support)
// declare your variables and methods
#end
In this way, you can change things around without having to modify your managed object model. (Changing the MOM has many issues with repopulated databases, migration, etc.). When you change the model, you can generate the class files again without loosing the code in the category.
My model objects are lazy-loaded from an SQLite database. Don't ask why, but the code uses QuickLite to populate the objects, which means that some housekeeping has to be performed before an accessor is used the first time.
I thought, naively, that valueForKey: and setValue:forKey: would be called by the #synthesize'd accessors, so that I could simply overload those 2 methods to fill the object from the db, if necessary. Unfortunately, that doesn't work: the #synthesize'd accessors clearly don't use KVC to get/set their represented value.
My question is therefore: Is there a way to call some code before any #property is accessed, without writing all getters/setters myself?
If your model objects were a subclass of NSManagedObject then your accessors would be using KVC (you declare the properties, then use '#dynamic' rather than '#synthesize' in the .m file to indicate that the accessors will be taken care of by other code).
Basically it sounds like you're re-implementing the faulting behaviour in Core Data.
Based on your comment, the only way I can think of doing this would be to have a sort of proxy object which contains your actual object. So, your proxy object would have a single visible property, which is your actual object, and in the accessor for that, you would then check to see if you'd gone to the database for this particular object, if not, do your housekeeping.
So, your calls would be
NSString *someProperty = proxyObject.realObject.someProperty;
Within proxyObject, the accessor for realObject:
if (beenToTheDatabase)
return realObject;
else
{
// Do your business
beenToTheDatabase = YES;
return realObject;
}
Whether this is more or less effort than manually writing your accessors or migrating to core data, I don't know.
I am in a situation where I want to dynamically generate getters and setters for a class at runtime (in a similar manner to what NSManagedObject does behind the scenes). From my understanding, this is possible using resolveInstanceMethod: on a specific class. At this point, you would have to use class_addMethod to dynamically add the method based on the selector. I understand this at a theoretical level, but I haven't delved much into the obj-c runtime, so I was curious if there were any great examples of how to do this. Most of my knowledge comes from this article:
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtDynamicResolution.html
Any thoughts / examples?
The only nice discussion I know is at Mike Ash's blog post. It's not that hard, actually.
I once needed to split a big NSManagedObject subclass into two, but decided to keep the fact an implementation detail so that I don't have to rewrite other parts of my app. So, I needed to synthesize getter and setter which sends [self foo] to [self.data foo], automatically.
To achieve that, I did the following:
Prepare the new method, already in my class.
- (id)_getter_
{
return objc_msgSend(self.data, _cmd);
}
- (void)_setter_:(id)value
{
objc_msgSend(self.data, _cmd,value);
}
Note that _cmd has the selector in it. So, usually, _cmd is either #selector(_getter_) or #selector(_setter_) in these methods, but I'm going to plug the implementation of _getter_ as the implementation of foo. Then, _cmd contains #selector(foo), and thus calls self.data's foo.
Write a generic synthesizing method:
+(void)synthesizeForwarder:(NSString*)getterName
{
NSString*setterName=[NSString stringWithFormat:#"set%#%#:",
[[getterName substringToIndex:1] uppercaseString],[getterName substringFromIndex:1]];
Method getter=class_getInstanceMethod(self, #selector(_getter_));
class_addMethod(self, NSSelectorFromString(getterName),
method_getImplementation(getter), method_getTypeEncoding(getter));
Method setter=class_getInstanceMethod(self, #selector(_setter_:));
class_addMethod(self, NSSelectorFromString(setterName),
method_getImplementation(setter), method_getTypeEncoding(setter));
}
Note that this is a class method. So self stands for the class. Note also that I didn't hardcode type encodings (which tells Objective-C runtime what the arguments of the particular method are). The syntax of type encodings is documented, but constructing by hand is very error-prone; I wasted a few days that way until Mike Ash told me to stop it. Generate it using an existing method.
Generate forwarders at the earliest possible time:
+(void)load
{
for(NSString*selectorName in [NSArray arrayWithObjects:#"foo", #"bar", #"baz",nil]){
[self synthesizeForwarder:selectorName];
}
}
This generates foo, setFoo:, bar, setBar:, and baz, setBaz:.
Hope this helps!
Another example is one I wrote, called DynamicStorage, available here:
https://github.com/davedelong/Demos
The primary impetus behind it was this question, which was asking how to use an NSMutableDictionary as the backing store for any object ivar. I wrote a class that will generate getters and setters for any #property, respecting things like a custom getter/setter name, the object memory management policy, etc. The neat thing about it is that it's using imp_implementationWithBlock() so that it only has to calculate the appropriate property name once (and then captures and saves it as part of the block).
I'm creating a base class that has an isDirty flag. It is set any time one of its properties changes, but since it's a base class, it doesn't know what its properties are. So basically, on every subclass, I have to override every - set: method to something like this:
- (id) setName:(NSString *)value {
if ([name isEqualToString:value]) {
return;
}
[name autorelease];
name = [value retain];
isDirty = YES; //Here's the important bit
}
Almost every line of that is what the automatically-synthesized setter would do. Is there any way I can override what #synthesize actually creates?
There are other options I have come up with, but they all seem like they would be much slower at runtime than this method. I've thought of things like adding an object to observe its own property changes, or creating a generic function to do all that and just pass in the address to the iVar and the new value, but that still requires overriding the setter.
Any ideas? If it makes a difference, it's for an iPhone app.
Several issues here:
(1) If you are concerned about setter performance, you shouldn't be using -isEqualToString: in your setter. Do a pointer compare instead because that is all that matters in this context.
(2) If you have an NSString attribute, you should be copying on set. Copy is free for immutable strings and will save your bacon for mutable strings (by preventing the caller from mutating the string out from under you).
(3) Again with performance; you checked for equality, but then use autorelease. That incurs unnecessary overhead.
(4) * they all seem like they would be much slower at runtime* indicates that you haven't actually tried it, haven't identified a performance problem, and are prematurely optimizing your code. Given (1) and (3), there is likely much more easily addressed performance issues.
My suggestions:
(1) Use #synthesize. It will generate correct and fast code, addressing (1) and (3).
(2) Use KVO or one of the other mechanisms. Until you identify a performance problem through instrumentation and quantification, you don't have a performance problem.
(3) Consider using CoreData (unless, of course, you are targeting OS 2.x). The example code is from something that is obviously a model object. If your code is nicely factored into model/view/controller, using CoreData at the model layer can both simplify your application and CoreData does a wonderful job of change tracking.
There's no way I know of that enables you to override what #synthesize does.
At the end of the day, it's used for creating basic accessor methods - ie. those that don't have specific behaviour.
Maybe you should look into Key Value Coding and Key Value Observing?
There isn't.
What you want to achieve is only possible by digging deep into the Objective-C runtime or by using proxy objects.
Why don't you have a look at KVO again?
If you write your own accessor method(s) #synthesize respects that. #synthesize gives precedence to accessors you write on your own. Just provide the accessor you like and #synthesize will be ignored on that one. For example you could implement an accessor that creates the property only in case it isn't already there.
Example:
#synthesize standardUserDefaults;
- (NSUserDefaults *)standardUserDefaults {
NSLog(#"standardUserDefaults");
if (!standardUserDefaults) {
NSLog(#"standardUserDefaults new");
self.standardUserDefaults = [NSUserDefaults standardUserDefaults];
}
return standardUserDefaults;
}
Here the "setter" is synthesized while the "getter" is not.