iPhone - Change entity class (NSManagedObject) to make them initializable - iphone

I would like to use my custom NSManagedObject like a normal object (as well as its regular functions). Is it possible to modify the class in order to be able to initialize it like a normal object?
[[myManagedObject alloc] init];
Thanks
edit: to clarify the question, will it screw everything up if I change the #dynamic with #synthesize in the implementation?

I do this quite often in one of my apps. My approach is to initialize the object with:
-(id)initWithEntity:(NSEntityDescription *)entity insertIntoManagedObjectContext:(NSManagedObjectContext *)context
passing nil for the context. To get the entity description, you do need access to the managedObjectContext. I tend to get the entity description when my app starts and then store it in an instance variable in my app delegate.
Here's an example:
//inside my "Engine" class
self.tweetEntity = [NSEntityDescription entityForName:#"Tweet" inManagedObjectContext:self.moc];
//later on when I want an NSManagedObject but not in a managed object context
Tweet *tweet = [[[Tweet alloc] initWithEntity:self.engine.tweetEntity insertIntoManagedObjectContext:nil] autorelease];
This allows me to use NSManagedObjects without storing them in a database. Later on, if I decide I do want the object inserted into my database, I can do it by inserting it into my managed object context:
[self.moc insertObject:tweet];

The managed object context is a required property of NSManagedObject therefore you can't properly initialize an instance of it without inserting it into a context. It looks at the context to understand it its entity and it notifies the context when any of its properties change.
The #dynamic and #synthesize are just compiler directives. You can switch to #synthesize from #dynamic as long as you provide proper getters and setters yourself. Since NSManagedObject relies heavily on key-value observing to function, you do have to write KVO compliant accessors.
If you need to initialize an NSManagedObject subclass, you override awakeFromInsert which will let you provide customization to the instance when it is created. You can also customized the object every time it is fetched using awakeFromFetch.

Related

Creating instance for NSManagedObject Class using Restkit

how can i create an instance for NSmanagedObject Class which i am created using core data model. I have another class which is the subclass of NSObject. I want to create an object for NSManageObject Class. how can i do that. Is it like the normal way that we create the instance for NSobject class like
ClassB.m //ClassB is NSObject Class
ClassA *obj = [[ClassA alloc]init]; //ClassA is NSmanagedObject Class
or is there any way to do that???
i am not using this code(Apple Docs) for creating instance
NSManagedObject *newEmployee = [[NSManagedObject alloc]
initWithEntity:employeeEntity
insertIntoManagedObjectContext:context];
i would like to know, is there any way to create an object similar to the above code using restkit
Note: i am using Restkit for creating object instance and mapping.
Thanks
Using Restkit you can use the static method object on the class you want to create. In your example
[ClassA object]
would return you an instantiated object.
Just be sure to import the correct headers:
#import <RestKit/CoreData.h>
instead of
#import <CoreData/CoreData.h>
No, you don't want to instantiate an NSManagedObject via alloc/init. You should use NSEntityDescription's insertNewObjectForEntityForName:inManagedObjectContext:. Something like:
ClassA *obj = [NSEntityDescription
insertNewObjectForEntityForName:#"ClassA"
inManagedObjectContext:context]; // ClassA is NSManagedObject Class
In order to have a NSManagedObjectContext, you also need an NSPersistentStoreCoordinator, and an NSManagedObjectModel, etc. Yeah, it's complex. This book really helped me get my head around Core Data; I recommend it wholeheartedly.
See the Core Data Programming Guide section on creating and deleting managed objects. Actually, while you're there, I'd recommend reading the entire Core Data Programming Guide. Core Data is amazing and powerful, but it is complex; you really want to know what you're doing.
Update: The fact that you're using RestKit might change what I said above. It may be that RestKit has it's own API for doing Core Data stuff, I'm not sure. Maybe check the docs.

Mutable sets in NSManagedObjects?

The standard declaration for an autogenerated NSManagedObject property declares to-many relationships like this:
#property (nonatomic, retain) NSSet *somethings;
...
#dynamic somethings;
Would it be safe to change that declaration to an NSMutableSet? Would the CoreData component still function correctly?
Judging by the #dynamic, it probaby doesn't care if I use an NSSet subclass rather than an NSSet, but I don't want to rewrite a lot of code only to find out it doesn't work.
According to Apple's Core Data Programming Guide, this should always be declared as a NSSet.
If you want a mutable proxy (which is basically the mutable version of the core data set and works exactly the same) then you would use the function mutableSetValueForKey like this:
// myManagedObject is the managed object that has the somethings property.
NSMutableSet *mutableSomethings = [myManagedObject mutableSetValueForKey:#"somethings"];
mutableSomethings can then be modified as a standard NSMutableSet andsomethings will be updated and KVO methods will be appropriately called.
Note however, that many features of the mutable set (such as addSomethingsObject and removeSomethingsObject) are already provided in the core data generated accessors so in many cases you don't need to use the proxy.
You should keep it as an NSSet and do one of the following:
Use key value coding
Add the core data generated accessors
For key value coding, you'll access your collection like so:
NSMutableSet *somethings = [object mutableSetValueForKey:#"somethings"];
[somethings addObject:newObject];
For core data generated accessors, you'd add the following to your header:
#interface MyManagedObject (CoreDataGenerated)
- (void)addSomethingsObject:(MySomething *)object;
- (void)removeSomethingsObject:(MySomething *)object;
- (void)addSomethings:(NSSet *)somethings;
- (void)removeSomethings:(NSSet *)somethings;
#end
You do not need to implement these methods (Core Data will take care of it for you), and you can call them easily. This is my preferred way of handling collections because you get better type checking from the compiler.
The reason you should not change it to an NSMutableSet is because there is a good chance that you will cause crashes and/or that your changes will not be persisted into your Core Data store.
You may also want to look into mogenerator to help you out with creating all of your Cocoa classes.

How do I reference a pointer from a different class?

First off, I despise singletons with a passion. Though I should probably be trying to use one, I just don't want to. I want to create a data class (that is instantiated only once by a view controller on loading), and then using a different class, message the crap out of that data instance until it is brimming with so much data, it smiles.
So, how do I do that? I made a pointer to the instance of the data class when I instantiated it. I'm now over in a separate view controller, action occurs, and I want to update the initial data object. I think I need to reference that object by way of pointer, but I have no idea how to do that. Yes, I've set properties and getters and setters, which seem to work, but only in the initial view controller class.
There's no need to use a singleton if you don't like the pattern or if it doesn't fit. Assuming you are creating your second view controller in the first one, just declare an ivar and property for your model object in your second view controller and when you instantiate it, assign the model object to this property.
Make a global variable for your object and store it there on creation. You can wire that up in the init method (probably bad style), or from the caller or via interface builder. Just make your variable known in the files that use it.
Or - well - use some kind of singleton pattern and get the instance directly from that class. Looks much cleaner.
Seriously use a singleton. In case you don't like them cause you don't know the code:
#interface Order : NSObject {
NSMutableArray *order;
}
#property (nonatomic, retain) NSMutableArray *order;
+ (Order *)sharedInstance;
#end
#import "Order.h"
#implementation Order
#synthesize order;
+(Order *)sharedInstance {
static Order *myInstance = nil;
#synchronized(self) {
if(!myInstance) {
myInstance = [[Order alloc] init];
}
}
return myInstance;
}
-(void)dealloc {
[order release];
[super dealloc];
}
#end
Um. Hello. Isn't Core Data a good enough framework for you? It allows you to have a single persistent store and multiple contexts to manage updates and merging of changes in response to notifications. I may be out of line here, but seeing how you start the question with a strong opinion about a well accepted pattern in your first question indicates that you have not spent much time discovering the ways in which the objective c runtime and Foundation classes in iOS can collaborate to achieve a task. In any software, one object and only one object owns a specific resource. You should embrace singletons. I suggest you spend some time reading http://www.cocoadesignpatterns.com/. Oh yeah, check out the meaning of KVO.
Why not make it a property of your app delegate? That way you don't have to use the singleton pattern but you are taking advantage of Apple's already existing usage of the singleton pattern.
Don't forget that Objective-C is a superset of C.
Basically, a data class is a plain C struct.
If you want to access a variable of that class from another class, make it global.
mydata.h:
struct MyData {
// Whatever data fields that you need, e.g.:
NSInteger value;
};
extern struct MyData mydata;
mydata.c:
struct MyData mydata = {
// Whatever initial value that you find relevant, e.g.:
.value = 42;
};

Can I encode a subclass of NSManagedObject?

This is for an iPhone App, but I don't think that really matters. I need to send a custom object (which is managed by Core Data) over bluetooth using the iPhone's GameKit. Normally, I would just use an NSKeyedArchiver to package up the object as a data object and ship it over the line, then unarchive the object and I'm done. Of course, I would need to implement the initWithCoder: and encodeWithCoder: methods in my custom object as well.
I'm not sure if this can be done with an NSManagedObject class, which is managed by Core Data or not. Will they play nice together? I'm guessing once I ship the encoded managed object over to the other device and unencode it, I would just add this received object to the other device's context. Is this correct? Am I missing any steps?
An NSManagedObject instance can't meaningfully exist outside of an NSManagedObjectContext instance, so I wouldn't bother trying to do the NSCoding dances required to directly serialize and deserialize an NSManagedObject between two contexts (you can do this; see below). Instead I would create a dictionary with the appropriate attribute key/values (you can get the attribute names via the managed object instance's attribute names via instance.entity.attributesByName.allKeys (you can use [instance dictionaryWithValuesForKeys:keys] to get the dictionary of attribute:value pairs) . I would send relationship information as NSURL-encoded NSManagedObjectIDs. Don't forget to include the instance managedObjectID (as an NSURL) in the dictionary so that you can reconnect any relationships to the object on the other end. You'll have to recursively create these dictionaries for any targets of relationships for the instance you're encoding.
Then send the dict(s) across the wire and reconstitute them on the other end as instances in a new managed object context (you can use setValuesForKeysWithDictionary:).
You may notice that this is exactly what the NSCoder system would do for you, except you would have to use the classForCoder, replacementObjectForCoder: and awakeAfterUsingCoder: along with a custom NSDictionary subclass to handle all the NSManageObject-to-NSDictionary mapping and visa versa. This code is more trouble than it's worth, in my experience, unless you have a complex/deep object graph that you're trying to serialize. For a single NSManagedObject instance with no relationships, it's definitely easier to just do the conversion to a dict and back yourself.
This sounds like a job for TPAutoArchiver.
I suggest the dictionary solution for simpler options. However, here is how I solved the issue. My model was already sizable and robust, with custom classes and a single root class above NSManagedObject.
All that I needed was for that single class to call the appropriate designated initializer of NSManagedObject: [super initWithEntity:insertIntoManagedObjectContext:]. This method, and the metadata in an NSEntityDescription is what sets up the implementations of all the dynamic accessors.
- (id)initWithCoder:(NSCoder *)aDecoder {
CoreDataStack *cds = [LibraryDiscoverer unarchivingCoreDataStack];
NSEntityDescription *entity = [cds entityDescriptionForName:[[self class] entityName]];
NSManagedObjectContext *moc = [cds managedObjectContext];
self = [super initWithEntity:entity insertIntoManagedObjectContext:moc];
self.lastEditDate = [aDecoder decodeObjectForKey:#"lastEditDate"];
return self;
}
The CoreDataStack is my abstraction around CoreData. The LibraryDiscoverer is a global access hook to get hold of the core data information. The entityName is a method defined to provide the entity name from the class name; if you follow a naming convention (i.e. class name = entity name) it can be implemented generically.
All the other initWithCoder: methods in my class hierarchy are standard NSCoder, with the note that you don't need to encode both directions of a relationship, CoreData reconnects that for you. (As it always does, including with the dictionary solution.)

Why does Core Data create NSManagedObject's with properties but no accompanying instance variable?

Exactly as the title says, why does Core Data create instances of NSManagedObject with properties for each entity's attribute, but no accompanying instance variable? The problem is, I would like to use some of my 'Entities' in a typical alloc/init style fashion in some parts of my code. Not using Core Data's fetching/context to create/store.
I suppose I could stay without the instance variables, but would it hurt to add them? Can I also change the #dynamic to #synthesize so I can use KVC on my properties/ivars?
Perhaps I'm completely wrong in trying to use one of my Core Data entities in some parts of my code without using the core data APIs. Still rather new to it to understand when I can go my own route.
The reason it doesn't use ivars is that that data isn't there. When you load an object it may not fault in all of its data. ivars are just variables, if you and write to them NSManagedObject doesn't have a chance to fault in the value if it has not been loaded yet. Via accessors NSManagedObject has a choke point that allows it to read the data off the disk if it has not been faulted in yet, which means your object graph can be brought in lazily. Otherwise you would need to bring in every connected object.
Because of that, you can't just add ivars, they won't have the values you want. Likewise you can't change from #dynamic to #synthesized, things will not behave correctly. The dynamic property implementations provided by CoreData are completely KVC and KVO compliant anyway.
If you want to access the values without tripping KVO or KVC you can access the "primitive" values. You do that in one of two ways. You use primitiveValueForKey: and setPrimitive:valueForKey: or you can just declare the primitive and let CD provide dynamic implementations (from the documentation)
#interface Department : NSManagedObject
{
}
#property(nonatomic, retain) NSString *name;
#end
#interface Department (PrimitiveAccessors)
- (NSString *)primitiveName;
- (void)setPrimitiveName:(NSString *)newName;
#end
You don't need instance variables with Core Data. A managed object stores these values elsewhere.
I've never gotten very far with Core Data, but if I remember correctly, you're supposed to use primitiveValueForKey: and setPrimitiveValue:forKey: to perform these accesses from your accessor methods. (Outside of your accessors, you should be using either the accessors themselves or valueForKey:/setValue:forKey: instead.)