Why are my NSManagedObject subclass methods not being recognized? - ios

I've (heavily) subclassed NSManagedObject for a project. It worked in the original project seamlessly and without any effort.
I copied those files over to a new project, manually adding the appropriate CoreData classes to the new data model.
Unfortunately, I'm having 'issues'. For some reason, the methods of the subclass in question are being ignored. The exact same code between the two projects, but I'm suddenly getting an unrecognized selector issue.
NSFetchRequest *blockRequest=[[NSFetchRequest alloc] init];
NSEntityDescription *blockDesc=[NSEntityDescription entityForName:#"AdBlock"
inManagedObjectContext:context];
[blockRequest setEntity:blockDesc];
AdBlock *curBlock=[adBlocks objectAtIndex:blockIndex];
adBlocks=[context executeFetchRequest:blockRequest error:nil];
for (AdBlock *block in adBlocks) {
[block initAdBlock];//Crashes with unrecognized selector
}
I've checked, and the appropriate .m files got added to the compiler build phase. The code was quite literally copy&paste and is identical between the two projects -- source works, destination doesn't.
I've noticed that I don't explicitly tell the context that it should return the subclass type, but that wasn't an issue in the old project, so why should it be an issue in the new one?

When something like this happens to me, there's two things I try. The first thing I do is make sure I properly #imported the NSManagedObject subclass (I forget that way more often than I should), but as you said you copied and pasted, I don't think that is your problem. The second thing I try is to rebuild the NSManagedObjects by going to File>New>File then NSManagedObject Subclass then selecting the NSManagedObjects that I changed something in. I would recommend rebuilding all of them in your case. See if that works. It may not, but it's an easy place to start.

Make sure you check your model and ensure that you've change the class names in the inspector. Otherwise, they'll return as NSManagedObjects no matter what. And use mogenerator so you don't have to worry about regenerating your classes.

I know this question is stale, but maybe someone searching will find this helpful.
I've been using categories to add additional functions to NSManagedObject subclasses. This allows me to use the XCode command to generate the class definition without destroying any of the custom logic. Ron mentions this in a comment on the selected answer - just thought it would be worth calling attention to as I find it's a pretty slick solution.

Related

"Cannot define category for undefined class" error in Xcode 11.3, no access to source file

I'm trying to make a category for a class that gets defined in a source file I don't have access to, namely RunnerViewController.
The two important files here are iPad_RunnerAppDelegate.h and FilesBrowser.mm. I do not have access to the header file's corresponding source file.
iPad_RunnerAppDelegate.h contains a forward declaration to the RunnerViewController class, and can reference that class with no difficulties.
However, if I try to #include "iPad_RunnerAppDelegate.h" in FilesBrowser.mm and then try to create a category in the same file, it doesn't recognise the class name.
Despite this error appearing, I can still use the RunnerViewController class inside FilesBrowser.mm, I just can't make categories for it.
What's happening here, and how would I fix it?
I've had to do this same thing but it was a long time ago. The problem is that without some indication of where to apply the category, your code cannot work alone. What you need is info to the compiler to let it know where it's going to insert the category.
In FilesBrowser.mm, you will tell it by adding this BEFORE "iPad_RunnerAppDelegate.h":
#interface RunnerViewController: UIViewController // or whatever it actually subclasses
#end
Now the compiler knows that its going to insert the category against an Objective C class named RunnerViewController.
But - you're not completely done. Now you need to insure that the runtime / loader do the class loading and category insertions at the proper time. This is where my knowledge gets a bit fuzzy, so it may not turn out to be a problem at all.
What I believe needs to occur is for the RunnerViewController class to get loaded first, then later at some point the category you wrote applied to it (but this may not be necessary). So if RunnerViewController is statically linked into your app (using a .a archive), then you're good for sure. But if that class is in a dylib loaded dynamically, and your category is statically linked - well that might be a problem. I just don't know how long Apple waits to try and deal with the categories (which by the way the -ObjC link flag is for.
In any case try the code above first and just see what happens. If RunnerViewController is dynamically loaded, and you get some hard runtime error, there is a workaround where you would make your category go into a module/framework that is also dynamically linked, but gets linked after RunnerViewController.
In the end I believe you have a good chance of making this happen.

What is the use of "filename+CoreDataProperties.swift" and "filename.swift" NSmanagedObject subclasses?

I recently noticed while creating the NSManagedObject subclass two classes are getting created.
One is filename+CoreDataProperties.swift and other is filename.swift.
Is filename+CoreDataProperties.swift similar to filename.h and filename.swift to filename.m in Objective-c? If so then i can put my implement my logic inside the filename.swift using the +CoreDataProperties.swift class.
I seriously couldnt find out the differences between two these files and whats their use?
Any help is appreciated
I think the filename+CoreDataProperties.swift version gets overwritten every time you export your model from Editor > Create NSManagedObject Subclass (e.g., every time you update your model and need to re-export the class files).
If you have some other, non-Core Data properties (or methods) in your custom subclass, they would be lost every time.
By separating the Core Data-specific code and your custom code, you can export from the Core Data editor as many times as you want without losing your non-Core Data additions.

When to use alloc

Ive tried digging around in old posts, but they were kind of obsolete and years old, so I was concerned things might have changed due to ARC being introduced etc.
Mainly I was looking at some fairly new code that used alloc on an array, which was declared as a property in the header. I was thinking, I don't normally do this and everything is working smoothly. But then came to my mind UIButton and UILabel for example, which has to have alloc invoked, or they won't be added to the view when you want that. Are there any rough guidelines for when its actually needed. I would think any object needs this, but the array vs. button/label thing as I just described made me wonder what is the reasoning behind this.
Thanks in advance
This is 100% dependent on the actual objects being used from the framework. The really great news, though, is that Apple is extremely consistent. If you send a message that contains an "Alloc" in it anywhere, you are responsible for making sure that it has been released.
Here's a super document on it from Apple.
You use alloc always if you create the object on your own.
Which means that sending alloc is normally followed by an init method.
It might sometimes look like it's not always the case, as many classes have convenient constructors that return an already allocated and initialized object to you.
But there is still alloc and init called to create the object.
It's just done for you by the system.
But the difference between convenient constructors and manually creating objects isn't new to ARC, it has always been around.
Example for creating an NSArray on your own:
[[NSArray alloc]initWithObjects:#"Test", nil];
and as NSArray has an convenient constructor here the example for that:
[NSArray arrayWithObjects:#"Test", nil];
If you use ARC, there is no real difference.
Without ARC, the object returned by the convenient constructor would still be autoreleased, while the manually allocated object has to be released by you at a later point (or you have to add an autorelease after the init).
So the main difference is the owner ship:
In the manually created example the object belongs to you, so you are responsible to clean up after you don't need it anymore.
If something is declared in .xib then it is automatically allocated for you. Just use it. While If you are creating a view in code u must alloc it first. And if you have a property NSArray/NSMutableArray etc, u might be doing one of the following:
_array = [NSArray new];
_array = [[NSArray alloc] init];
_array = [NSArray arrayWithObjects: values count:2];
_array = # ["ABC", "xyz"];
etc,
so you are actually allocating it if you are using any of these methods.
I do not really understand, what
they won't be added to the view when you want that.
means, but here is the story:
Every object is allocated using +alloc. To be honest, it is +allocWithZone:, but this does not have any meaning at this place.
The very first message you have to send to the newly created instance object (remember: you sent +alloc to the class object) is an initialization message, something like init…. To put this together you will find code as:
… [[TheClass alloc] init…] …;
Because it is boring typing there are new allocators. They put this messages into one:
… [TheClass new…] …;
(This has some advantages for the class implementor, too, but this is transparent to you. Think of it as sending +alloc, -init….)
In earlier times it has been a good idea to send an autorelease message to it as the next step for some reasons:
… [[[TheClass alloc] init…] autorelease] …;
This has been put together to convenience allocators:
… [TheClass theClass…] …
This is what you find most of the time when reading old code. Using ARC you do not need convenience allocators anymore: There is no autorelease. So there is no reason for convenience allocators anymore. But we had MRR for years and there are still many convenience allocators. (Now called factory methods, but CA's are only a subset of these. A subset you do not have to care about. It's all gone with ARC.) Because there are that many CA's, one still uses them. (And there is a performance issue with them. In nowadays I only write new allocators, which has the advantages of a CA, but not the disadvantage of autorelease.)
To make a long story short: You simply do not see sometimes that +alloc is used.

Swift - CoreData’s NSManagedObject subclass

When creating NSManagedObject subclasses in Swift, I get an error:
2015-02-12 00:12:57.662 MyApp[1934:272844] CoreData: warning: Unable to load class named ‘ClassName' for entity 'ClassName'. Class not found, using default NSManagedObject instead.
I can fix this by manually adding #objc(ClassName) in the Swift subclass, but not only does this defeat the purpose of automatic subclassing, but I don’t want to waste my development time adding this to all two dozen NSManagedObject subclasses.
Originally I did this and just got over it. But this time I needed to change my model and generate all subclasses again and have a deadline coming up, so what would be the correct way to overcome this error so regenerating my model’s subclasses doesn’t mean wasting so much time/patience.
Thanks as ever
You are out of luck. Some things in Swift just do not (yet) work the way they used to in Objective-C.
Consider writing a script (in Perl, Python - or Swift!) to insert the missing line. For example, the first line of the class declaration is always on the same line, so it should be trivial to extract the class name and insert the corresponding #objC call.

"Header File "Only able to be read in one class and other init methods won't work in other files

I have been stumped on this for awhile. I have asked multiple developers I know and they think I have forgotten to "#import the .h file". But I know I have, I have tested the class in more than one file in my project. It only works in the "VNDecalLevelListViewController.h" ( which I will post its implementation if a picture). When I try and call my "initForNewDecal" method for my "VNDecalCreatorViewController.h" class in my "VNAdminViewController.h" class I received the error that this method has not been declared in "VNDecalCreatorViewController.h". But when I call it in my "VNDecalLevelListViewController.h" class it works.
I am able to allocate and use "init" to create the object and it loads with a work around I made. But I am new to programming and I can tell there is definitely a better solution.
As you will soon see as i got to allocate the VNDecalCreatorViewController in the " VNAdminViewController" the option to initialize VNDecalCreatorViewController with the proper initializer "initForNewDecal" isn't even a option.
Anyone know why this is happening ? I want to write the code right I am trying to figure out why my header file is only being read in one class.
I guess this is because you mutually imported between the two class Creator and Level. I mean you may have #include "VNDecalCreatorViewController.h" in VNDecalLevelListViewController.h and vice versa. The solution is to use #class to forward declare any classes you may need to reference instead of #import'ing the header.
Make sure that the method is declared in the header file and implemented in the .m file.

Resources