Inheritance Core Data: Do I need to specify a Subclass in my xcdatamodel? - objective-c

I have a base class called Mail that will essentially act as an abstract class which will have concrete subclasses that determine things like the mail's subject, body, sender etc.
Let's call one such concrete subclass NewsMail.
I've set up polymorphic relationships in Obj C before but never in CoreData.
It seems as though the following line is expecting a class thats declared in the xcdatamodel. The following line:
[NewsMail MR_createInContext:[NSManagedObjectContext MR_context]];
produces this error:
"NSInternalInconsistencyException", "+entityForName: could not locate an entity named 'NewsMail' in this model.
Now I could get around this by adding an entity in my xcdatamodel for every concrete subclass I have but this will grow unwieldy after a time.
Advice?
UPDATE
So I have a category 'Mail+Types' where I intend to configure the concrete types:
#import "Mail.h"
#import "MailProtocol.h"
#interface Mail (Types)
+ (instancetype)newInstanceInContext:(NSManagedObjectContext*)context;
#end
#interface WelcomeMail : Mail<MailProtocol> #end
MailProtocol will define the additional methods that a concrete Mail should conform to:
#import <Foundation/Foundation.h>
#protocol MailProtocol <NSObject>
- (NSString*)subjectKey;
- (NSString*)bodyKey;
- (void)build;
#end
The (instancetype)newInstanceInContext:(NSManagedObjectContext*)context; method should return a concrete class but built from the parent Mail class, as pointed out in an answer below:
+ (instancetype)newInstanceInContext:(NSManagedObjectContext*)context
{
return [NSEntityDescription insertNewObjectForEntityForName:#"Mail" inManagedObjectContext:context];
}
I can confirm that the correct concrete class implementation of newInstanceInContext is executed.
Unfortunately, if I try to run build on a WelcomeMail I get an 'unrecognized selector' error because it tries to run it on the Parent 'Mail' class.

You can do it but I suspect not the way you want to. I don't use MR but it seems obvious it has a factory method to create your managed object based on the class name - and because you don't have an Entity with that name it fails.
What I have done is created both Entities in the MOM editor and marked, for example Mail, as abstract then added each subclass as entities which are derived from Mail. Not sure what you want to do with each derived class where this is that much of a burden. After all, each derived class only then needs the additional attributes that class needs which they don't inherit from Mail. Including for example transient attributes which can be used to add business logic by adding behavior that's not part of the stored model.
The other thing I often do which does work is inherit from the managed object in code - model not stored in the MOM - to add additional logic. But in that case I usually add a factory method myself to instantiate the actual MOM Entity. I suspect in this case MR may be trying to help you by creating a MO with the wrong Entity.
Just to elaborate a bit. I usually create a 'base' model object that's not stored in Core Data that looks like:
#interface BaseModel : NSManagedObject
+ (instancetype)newInstanceInContext:(NSManagedObjectContext*)context;
#end
#implementation BaseModel
+ (instancetype)newInstanceInContext:(NSManagedObjectContext*)context
{
return [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([self class]) inManagedObjectContext:context];
}
#end
My actual model object, say Mail (which is defined in the MOM), inherits BaseModel.
I then further derive another class from Mail. Say ExpressMail. ExpressMail inherits Mail. But overrides newInstanceInContext with:
+ (instancetype)newInstanceInContext:(NSManagedObjectContext*)context
{
return [NSEntityDescription insertNewObjectForEntityForName:#"Mail" inManagedObjectContext:context];
}
Basically it creates a Mail MO. I can then add additional logic to ExpressMail. With the obvious understanding that I can't add any properties here that will be persisted outside of what's in Mail.
Not sure if this is what you want but its a pattern I use frequently to do something like what you are trying to do.
UPDATE
Updating after several comment threads. I think we both realized down casting is not going to work. If you really only want to add behavior - i.e. add methods that can decorate or work on your base class - Mail - then categories should definitely work. If you really only need your POMO (plain old mail object) then you can use categories to decorate it / configure it in different ways -- but If you really need to work with a different subclass then you will have to set those up in your MOMD for core data to be able to instantiate them properly for you.
One last, last suggestion. Not sure if you are familiar with a tool called MoGenerator? It generates a matching set of classes -- one private that represents your actual MOM class and another public that you can extend with additional logic. Anyway, it looks like it has a pretty clever way to wrap your entity with the inner private version -- which creates as _Mail -- and then exposes a public version Mail - that you can then modify. All the plumbing is in the _Mail class. (It also takes care of the drudgery of doing the wrapping). I think you might be able to use it to do what you want. Ie generate your models -- and then just create another ExpressMail subclass that inherits from _Mail. Again, I can't be certain and not sure it eliminates the downcasting issue - but maybe worth a try?
Anyway, check it out:
https://github.com/rentzsch/mogenerator
PostScript
I just tried this myself using Mogenerator. And you still can't add anything to a different subclass of your _baseclass that doesn't exist on the base. That is, I can't subclass the _Mail class it would generate (which maps to a Mail entity in the MOMD) -- to create eg ExpressMail -- and add any behavior that's not on Mail. Same problem as before. So see prior conclusions. Doesn't seem possible for some of the reasons already given.

Related

What is the benefit of using associated objects vs static object defined in the category implementation file?

The problem is I don't see the benefit of using associated objects vs static objects defined in the category implementation file with getter/setter methods.
I was thinking about defining the getters and setters in the header file of the category. Like this:
#interface NSObject (test_static)
- (id)getStaticObject;
- (void)setStaticObject:(id)a_static;
#end
and then to declare a static variable in the implementation file and implement getter/setter methods, like this:
static id test;
#implementation NSObject (test_static)
- (id)getStaticObject
{
return test;
}
- (void)setStaticObject:(id)a_static
{
test = a_static;
}
Why I shouldn't use this approach and use associated objects instead ?
Well, I guess I didn't get how properties work and how they've solved the fragile base class problem. Maybe it's related...
There is a huge difference. Associated objects is a way to simulate properties in a category.
Using a single static variable means you have a single, shared value used across all instances.
The choice is which to use depends on your goal. If you want an instance specific result from your two category methods, do not use a static variable - use associated objects. If you want the same object back from the two category methods regardless of the object instance, then use the static variable (and probably change your category methods to class methods instead of instance methods).

Init a class with one argument using objc_msgSend [duplicate]

This question already has answers here:
Create a subclass of a class using parent's init - from another class
(2 answers)
Closed 8 years ago.
EDIT: Yes, I did it wrong. It's well possibly knowing the init method by using a protocol on class level. This is something I rarely do, so that didn't come to my mind at first (see linked question about my answer to it using a protocol). So yes, this question is broken. As bbum said, there should be absolutely no reason to do this.
Background to my question in [1].
For a design reason (data mapper pattern) I need to initialize classes which I know are subclasses of a certain base class (ManagedEntity). I assert for this once - then later I want to create as many instances, and as fast as possible (I'm programming for iOS). However, since the class where I need to create the concrete instances in doesn't know any of the model classes, the meta class stored and used to create entity instances of is just known to be of type Class.
Long story short: I can't simply use [[[_EntityClass] alloc] initWithBlah:something], since EntityClass is unknown, just known as type Class there, hence the init method initWithBlah is unknown of course - but I know it must exist (it must be by design a subclass of the base class, which is asserted once when the mapper is initialized).
So in order to create instances of the unknown class with the init method that I know it exists, I need to construct a method invocation. This should call the initWith:something selector on the unknown class and create an instance of it.
I think I should use objc_msgSend rather than NSInvocation, because the latter is supposed to be an order of magnitude slower [2]. The init method is supposed to not change, and requires one argument.
So... What would be the equivalent to:
ManagedEntity *newEntity = [[ManagedEntity] alloc] initWithEntityDescription:_entityDescription];
with objc_msgSend?
[1] Create a subclass of a class using parent's init - from another class
[2] http://www.mikeash.com/pyblog/performance-comparisons-of-common-operations-leopard-edition.html
Better:
Class klass = NSClassFromString(className);
id newEntity = [[klass alloc] initWithEntity:entity insertIntoManagedObjectContext:ctx];
There is no reason to use objc_msgSend() directly when you have a fixed selector. You can always call the selector directly using the normal syntax. Worst case, you might have to type-cast the return value of one of the calls.
The only requirement is that the compiler has seen the declaration of initWithEntity:insertIntoManagedObjectContext: sometime prior to compiling the above call site.
Example:
#interface NSObject(BobsYourUncle)
- (void)bob:sender;
#end
...
Class klass = NSClassFromString(#"NSManagedObject");
[[klass alloc] bob:nil];
The above compiles just fine. Not that I'd recommend hanging random definitions off of NSObject. Instead, #import the abstract superclass's declaration (which should contain the selector declaration).
id cls = NSClassFromString(className);
id alloced_cls = objc_msgSend(cls, #selector(alloc));
id newEntity = objc_msgSend(alloced_cls, #selector(initWithEntity:insertIntoManagedObjectContext:), entity, ctx);
return newEntity;

Class design plus object literals in Cocoa Obj-C

Let us say that application has a concept of cars in it. A car is an instance of Car. There are a small number of possible cars and most, but not all of the data about a car is known at design time. Each kind of car is a singleton; there is at most one of each kind of Car per app.*
Since they are singletons, and since they are named, that suggests some sugar. The most important piece of sugar is this: when I import "Car.h", into a file, I want the symbols "MAFerrari", "MAMercedes", and "MAMclauren" to start showing up in my autocomplete where ever code completion thinks I am trying to provide an instance of a Car*.
My next greedy desire is that I want to be able to send instance methods to my MAFerrari literal, e.g. [MAFerrari topSpeed] means "get the singleton instance of Car that corresponds to the Ferrari and send topSpeed to it", but I acknowledge the utter triviality of this. Even pragmatists should know what their Utopia looks like.
Finally, if possible, I want clean way to declare the constant properties of the three cars as literal data at the top of my Car.m file.
Now, again, I don't actually expect all of that to be possible in Objective C. I just want to know how close we can get to that all that.
The closest idea I've had is to subclass Car for each type of car and provide a +sharedInstance method for each one. sharedInstance can implement the singleton pattern: if it's initialized, return it, otherwise initialize it, store it (where? it should be private to the Car class hierarchy), then return it. Each subclass can implement its own initializer which contains the defaults for that subclass.
Here's what I don't like about this:
I think I have to import all the header files whenever I work with these subclasses. This absolutely sucks. Is there another way?
I have to create .m/.h file pair for every one of these subclasses. That seems like a lot of boilerplate, since they have no unique behavior.
[[MAFerrari sharedInstance] topSpeed] isn't quite as good as [MAFerrari topSpeed], but I acknowledge that this is trivial.
Anyway, how would you do it?
*In reality, it's not cars, but in-app purchase assets, for the curious.
It sounds like what you want is just a global variable. You can create a global variable named whatever you want (say, MAFerrari) and stick whatever you want in it — whether the contents of the variables are instances of multiple singleton classes or multiple instances of the same class doesn't really matter from the point of view of having global names to refer to the objects.
Easy-peasy.
Note that these aren't singletons; they're just long-lived instances of a class stored in global variables.
// Bolt.h
#import <Foundation/Foundation.h>
#interface Bolt : NSObject
// Properties
- (instancetype)initWithLength:(NSUInteger)length
diameter:(NSUInteger)diam
thread:(NSUInteger)thread
grade:(NSUInteger)grade;
// etc.
#end
extern Bolt * twentyFiveByTwelveCoarseThreadGradeEightBolt;
extern Bolt * fiftyByTenFineThreadGradeFiveBolt;
//
// Bolt.m
#import "Bolt.h"
Bolt * twentyFiveByTwelveCoarseThreadClassEightBolt;
Bolt * fiftyByTenFineThreadGradeFiveBolt;
// This will be run before main() but after classes are loaded
__attribute__((constructor))
static void initialize_global_Bolts(void)
{
twentyFiveByTwelveCoarseThreadClassEightBolt = [[Bolt alloc] initWithLength:25
diameter:12
thread:175
grade:8];
fiftyByTenFineThreadGradeFiveBolt = [[Bolt alloc] initWithLength:50
diameter:10
thread:1
grade:5];
}
#implementation Bolt
- (instancetype)initWithLength:(NSUInteger)length
diameter:(NSUInteger)diam
thread:(NSUInteger)thread
grade:(NSUInteger)grade
{
// Do initialization
}
#end
Now you can do things like [fiftyByTenFineThreadGradeFiveBolt maximumTorque]; wherever Bolt.h is imported.
You can't put dictionary or other literals at top level, because they resolve into method calls, which can't be used outside of other methods.

"Decorate" several classes with a specific method in Obj-C

I'm not yet that into design patterns so "Sorry!" to bother you with such a question, that might be obvious.
The thing is, I have several classes: Show, Segment, Command. These three classes are totally different, except the one thing: They all have an NSArray called schedules, which contains ScheduleItem classes.
In my workflow I need to check, if the current time matches a scheduleItem to set the Show,Segment or Command active. So, I'd like to have a method on all these three classes called isActive(). Since this method does the same for all current and future classes, I'm looking for a way to implement the isActive method just once, and reuse it in those classes.
Is there a nice way doing this?
To remember, those classes have absolutely nothing in common, except the schedules array. So, I'd like to avoid subclassing. But you can convince me otherwise.
You can create smth like this
#interface ScheduleCollection : NSObject {
NSArray* schedules;
}
#property NSArray* schedules;
/**
Return true if matches.
*/
-(BOOL) match:(ScheduleSclass); //or call it isActive or whatever you like
#end
Then replace schedules array in Show, Segment, Command with ivar of this class. If you need to compare time just get the property and call match:
Show* show = ...;
BOOL m = [show.schedules match: my_time];
There's really no design pattern for this except generic inheritance (shared base class with the method). You can't add it as a category for all three, as they don't share a base class.
If you want to avoid introducing a base class, you can use the fact that type id is a typeless object, and you can invoke any method on it at runtime. Only it will fail if the actual objec doesn't have the method...
On each of the objects, create a method called getSchedule like this:
- (NSArray*) getSchedule {
return schedule;
}
Then just create this method somewhere else
-(BOOL) isActive:(id)anyObjectWithGetScheduleAnyOtherWillFailWithSelectorNotImplemented
{
// You could do an explicit check to determine if the object passed in implements
// getSchedule, but I skipped it here.
NSArray* schedule = [anyObjectWithGetScheduleAnyOtherWillFailWithSelectorNotImplemented getSchedule];
<your implementation here>
}
In my opinion, you would be better off just introducing a shared base class, as it's a lot clearer and won't really take that much more work. But if you have good reasons not to, this will also do the job.

cannot respond to warning in Objective C

I am getting a warning:
RS232Msg cannot respond to
"-initWithRS232MsgRawEncoded"
Code is
-(void)createMessage
{
RS232Msg* pMsg;
//pMsg = new RS232MsgRawEncoded(static_cast<int>nMessageNumber); in cpp
pMsg = [pMsg initWithRS232MsgRawEncoded:(int)nMessageNumber];
}
initWithRS232MsgRawEncoded is a derived class of RS232Msg.
and pMsg is a pointer to RS232Msg. The createMessage is a method that is declared in RS232Msg How to make it to access ?
If you defined initWithRS232MsgRawEncoded in a class derived from RS232Msg you cannot use that selector with RS232Msg*.
If I understand correctly what you are trying to do, you would like to add one more possibility of creating RS232Msg objects by initializing them with raw encoding.
You can do that in different ways. One is creating a sort of "factory" class (it would not be an orthodox factory as per GoF patterns, but that does not matter). This class can have a static function that is exactly your initWithRS232MsgRawEncoded.
Another option you have is define a category for RS232 and then add the initWithRS232MsgRawEncoded into it. Categories are a way to extend classes without the need of subclassing them. This is a skeleton of how you would go about it in your case:
#interface RS232 (MyRS232Extension)
(id)initWithRS232MsgRawEncoded:....;
#end
#implementationRS232 (MyRS232Extension)
....
#end