20

Using CoreData I created an entity, then I subclassed it into its own file, where it has the @propertys, then it has the @dynamic parts in the .m file.

When I want something to have a certain value if it's never been set, I always use lazy instantiation, like follows:

- (NSString *)preview {
    if ([self.body length] < 200) {
        _preview = self.body;
    }
    else {
        _preview = [self.body substringWithRange:NSMakeRange(0, 200)];
    }

    return _preview;
}

But how do I do this with @dynamic properties? If I do the same thing, it says _preview is an undeclared property, but it's in the .h file. What do I do different to lazy instantiate it?

user212541
  • 1,878
  • 1
  • 21
  • 30
  • 1
    It says `_preview` is an undeclared _indentifier_, because you don't have a variable with that name. `@synthesize` creates the variable; `@dynamic` does not. – jscs Apr 06 '13 at 17:27

2 Answers2

28

One standard method is to define preview as a transient attribute in the Core Data model (so that the value is not actually stored in the database), and implement a custom getter method. In your case it would look like:

- (NSString *)preview
{
    [self willAccessValueForKey:@"preview"];
    NSString *preview = [self primitiveValueForKey:@"preview"];
    [self didAccessValueForKey:@"preview"];
    if (preview == nil) {
        if ([self.body length] < 200) {
            preview = self.body;
        } else {
            preview = [self.body substringWithRange:NSMakeRange(0, 200)];
        }
        [self setPrimitiveValue:preview forKey:@"preview"];
    }
    return preview;
}

(You can provide custom getter, setter methods for @dynamic properties. However, Core Data properties are not simply backed up by instance variables. That is the reason why you cannot access _preview.)

If you need the preview to be re-calculated if the body attribute changes, then you must also implement a custom setter method for body that sets preview back to nil.

For more information, read Non-Standard Persistent Attributes in the "Core Data Programming Guide".

Update: The current version of the Core Data Programming Guide does not contain that chapter anymore. You can find an archived version from the Way Back Machine. Of course this has to be taken with a grain of salt since it is not part of the official documentation anymore.

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • Is all that is required to do is use that method? Or must I do something in the CoreData file? – user212541 Apr 06 '13 at 17:47
  • @user212541: That should be all. You only have to mark the preview attribute as transient in the Core Data model editor. – Martin R Apr 06 '13 at 17:50
  • How exactly does it duplicate the data? – user212541 Apr 06 '13 at 17:56
  • Sorry, misspoke. I apparently misread the question. I was thinking that the two pieces of data were identical and being accessed from both the `body` property and the `preview` property. @Martin is right. That'll teach me for answering SO questions at 2am. deleting the original comment to avoid confusion. – tapi Apr 10 '13 at 16:42
  • @tapi: No problem, I'll delete my replies. – Martin R Apr 10 '13 at 16:48
  • @Downvoter: Would you care to leave a comment? Please let me know what I did wrong. – Martin R Apr 11 '13 at 10:57
  • you should change :[self setPrimitiveValueForKey:@"preview"]; by [self setPrimitiveValue:preview forKey:@"preview"]; in order to make this code work. – ant_one Aug 16 '14 at 10:07
  • The Non-Standard Persistent Attributes document is a dead link. I have seen countless references to this document, and I think it would be so helpful to have it to help deal with questions such as this. – SAHM Feb 23 '16 at 03:46
  • 1
    @SAHM: Yes, unfortunately Apple removed several chapters from the Core Data Programming Guide with their recent updates (you might want to file a bug report at Apple!). I have added a link to an archived version to my answer. – Martin R Feb 23 '16 at 09:20
6

See documentation on using primitiveValueForKey:

basically:

@dynamic name;

- (NSString *)name
{
    [self willAccessValueForKey:@"name"];
    NSString *myName = [self primitiveName];
    [self didAccessValueForKey:@"name"];
    return myName;
}

- (void)setName:(NSString *)newName
{
    [self willChangeValueForKey:@"name"];
    [self setPrimitiveName:newName];
    [self didChangeValueForKey:@"name"];
}
kelin
  • 11,323
  • 6
  • 67
  • 104
Dan Shelly
  • 5,991
  • 2
  • 22
  • 26