I mean to add a property of type NSIndexPath to my NSManagedObject but it doesn't seem to be one of the recognized property list types. I also mean to make it transient since index paths change depending on the UI, hence I have no reason to save it in the managed document.
I'm thinking of creating two separate NSNumber properties called section and row instead but it'll be much easier if I had an NSIndexPath instead. Help?
Just make it a transformable attribute with no value transformer name. The default transformer-- NSKeyedUnarchiveFromDataTransformerName-- takes any class that conforms to the NSCoding protocol and saves it as NSData.
Works for me with NSIndexPath, NSArray, and NSDictionary. The accessors just work... I believe by setting and getting an id type, so it's not as typesafe as an NSIndexSet* property would be, but it's good enough.
[edit...]
Err, actually. I use Mogenerator, which may take care of the issue mentioned in the docs:
In principle, you don’t have to do anything else. In practice, to suppress compiler warnings you should declare a property for the attribute...
... it's Mogenerator that makes a property of type id. The Apple example creates a property of the specific type in the entity class.
Related
I understand how to use NSCoding to convert my objects to archive objects. That's not my question.
What I'm wondering is why there isn't a default implementation of NSCoding that could handle probably 99% of cases.
For instance, every time I write a custom class that I want to archive, I perform the following:
Implement -(void)encodeWithCoder: and -(id)initWithCoder:.
Go down my property list, writing a pair of statements (one encode, one decode) for each property.
If the property is an object, I use the encode/decodeObject method.
If the property is a value, I use the corresponding encode/decode method.
I always use the property's name as my key.
I would suspect that almost every implementation of NSCoding is exactly like mine, with the only changes being the particular properties that need to be manipulated.
It seems to me that this would be a perfect place for a standard implementation, with the option to override if your particular case if funky.
Do I have a misunderstanding of what's going on? If not, could I add a category on NSObject to implement this common method on all objects in my projects?
I suspect that the answer to your question is simply that NSCoding was designed long before Objective-C properties existed. (NSCoding was part of the OpenStep spec in 1994, whereas properties arrived with Objective-C 2.0 in 2007.) Additionally, some classes have properties that are not appropriate to be serialized for later.
However, your proposed solution could be a great time-saver! At least one such solution already exists. Check out AutoCoding.
Since Objective-C 2.0 we have properties, a nice syntax for getting and setting values for instance variables. Since Clang 3.1 all properties which are not dynamic, not readonly with an explicit getter or don't have a custom getter and setter are automatically synthesized to ivars. And since ARC we have weak/strong annotations for properties which are used by ARC to define the memory management logic of automatically synthesized properties.
The properties still can be synthesized manually e.g. for a readonly property backed by an ivar and returning a default value, for instance.
Sometimes, properties are also useful if they are not synthesized at all. I have found a few use cases when I use this sort of behavior:
A custom getter and setter which use a custom ivar for storing the actual value and which perform some additional actions.
A dynamic property, e.g. in subclasses of NSManagedObject.
A readonly property which simply passes through a property of an object stored in another property (e.g. a private one).
The Question: Does it makes sense to annotate these non-synthesized properties with weak/strong according to their actual usage or not? What is the best practice?
(https://twitter.com/kubanekl/status/427142577310408704)
I would say the answer is yes, even if only for documentation sake.
Even if you do not use any of the compiler and framework related default implementations, and implement everything by yourself, someone attempting to use these properties will be in much better position of understanding the API if he is able to get a hint on how the memory management would behave. A person does not really have to know how a setter or a getter is implemented internally, but he would might have to know, for example, if after calling a setter, the value was copied or retained or just assigned, and implement his side of things accordingly.
Yes, it does.
The property definition is a contract specification. Just because the compiler isn't fulfilling the contract doesn't mean you shouldn't respect it when manually implementing the accessor methods.
I'd like to add an ivar to an existing objective-c class in runtime, but documentation states that an ivar cannot be an existing class, so I think property could still solve my issue.
As stated here class_addProperty(...) returns true, but when I try to access the ivar by it's name (or the property name) it always returns nil. What could be the issue causing this to happen?
You won't be able to add an ivar to the class at runtime. You can think of the class, and its ivars, as something like a C struct. It's layout is defined at compile time.
You can add properties at runtime (since these are just methods), and you can implement their getters and setters, but you'll need to come up with a different way to store any data that they represent.
Are you looking for something similar with some other programming language?
it looks like adding properties in AS3, but objc think the best would you use to store NSDictionary objects by keys.
I have a property of type NSArray on my class called "songs". I'm creating a custom getter for it and XCode gives me an option of creating a method:
songsAtIndexes:(NSIndexSet *)indexes
What is this and why is XCode offering this? Is this specific to NSArray properties? What is the purpose of creating a method/getter for this method? If I don't define it manually, will it be automatically created/synthesized?
This is the result of a little-used KVC optimization for indexed collections which can be used on your class. You can read about this here, but to excerpt:
Indexed To-Many Relationship Compliance
For indexed to-many relationships, KVC compliance requires that your class:
Implement a method named -<key> that returns an array.
Or have an array instance variable named <key> or _<key>.
Or implement the method -countOf<Key> and one or both of -objectInAtIndex: or -<key>AtIndexes:.
Optionally, you can also implement -get<Key>:range: to improve performance.
It's only really used with Core Data with KVC (and occasionally NSPredicates), but you can leverage these methods if you'd like to.
It's really not necessary in 99% of cases to implement this, but you can if you'd like.
For attributes of struct types that NSKeyValueCoding can handle, I use the Core Data accessor pattern described in Apple's docs here.
For example, an NSRange struct can be specified in the Core Data model as of type Transformable, then the NSValue rigmarole can be avoided for clients by providing accessors in an NSManagedObject subclass of the form:
Interface:
#property(assign, nonatomic) NSRange range;
Implementation;
- (NSRange) range {
[self willAccessValueForKey:#"range"];
NSRange retVal = range;
[self didAccessValueForKey:#"range"];
return retVal;
}
- (void)setRange:(NSRange)aRange {
[self willChangeValueForKey:#"range"];
range = aRange;
[self didChangeValueForKey:#"range"];
}
Mogenerator's generated NSManagedObject subclasses, however, declare Transformable attributes as NSObject properties, so clients need to get/set NSValues.
What's the best way to handle this situation with mogenerator, whilst (1) keeping with the simple Transformable pattern rather than messing with transient backing attributes, and (2) avoiding any edits of Mogenerator's 'machine' classes?
The ultimate way to deal with this would be, as scc suggested in the previously accepted answer, to change the mogenerator template files. They would need to (a) change the transformable attribute's accessor to be of the appropriate type (NSRange in this instance) and then (b) add the accessors with the appropriate KVO method calls.
As that's more than I have time right now to figure out how to do, my temporary expedient is as follows:
add an attributeValueClassName key to the attribute's userInfo dict (in the Core Data editor), with the value NSValue (just to make sure the generator's accessors will be NSValue rather than NSObject).
in the human-editable mogenerator output, add accessors like those in the question, except with a new name (eg. rangeValue and setRangeValue). The underlying values will still be the persisted NSValues, but my accessors take care of the KVO and boxing/unboxing.
Not ideal, but I do get strongly-typed accessors without having to edit the mogenerator machine files.
Just change the type from NSObject to whatever type you need after the model object generator has finished its job. You should not have any compiler warnings after that.
BTW, when I run the managed object model generator after defining a transformable attribute I do not get NSObject but id. No warnings.
Can you alter the template files that mogenerator uses? (I think) that provided you stay away from scalar values, you can safely use NSObject * instead of id.
For those willing to edit the machine template file, use the following conditional to special case for the transformable NSRange attribute.
<$if Attribute.hasTransformableAttributeType && Attribute.objectAttributeClassName == "NSRange" $>
Here's how I changed my machine template header file. https://gist.github.com/2414047
You are reading the wrong part of the documentation. Please look here:
NSRange doesn't need to be transformed. There are methods for all those basic structures, such as:
NSRangeFromString();
NSStringFromRange();
So you would define the shadow property as something like "rangeAsString" of type NSString.
In your MO subclass you would follow in the docs on how to properly convert and store the value so that core data knows your object became dirty when doing like:
myObject.range = NSMakeRange(0,5);