Understanding NSManagedObject - objective-c

In an existing project I have tried to introduce Core Data long after the project was created, so its model is already in place.
I have created the xcdatamodel and added my only class to it.
That class should act as a global storage for objects in my application.
The class properly implement NSManagedObject and I have verified it gets created and saved in context, also retrieved with a fetch result.
The way of saving data in this class is by means of NSMutableArray. But this is just not working. Here's a fragment of this class:
#interface WZMPersistentStore : NSManagedObject<NSCoding> {
NSMutableArray *persistentStorage;
}
#property(nonatomic,retain) NSMutableArray *persistentStorage;
-(void)add:(id)element;
-(void)remove:(id)element;
-(id)objectAtIndex:(NSUInteger)index;
-(NSUInteger)num;
#end
In the implementation I also override the initWithEntity like this:
- (id)initWithEntity:(NSEntityDescription*)entity insertIntoManagedObjectContext:(NSManagedObjectContext*)context {
NSLog(#"init with entity");
[super initWithEntity:entity insertIntoManagedObjectContext:context];
return [self init];
}
The init method only initialize the mutable array, and I can see from the log that it gets properly called by the app delegate when creating entity.
The add method just send message insertObject to persistentStorage.
The questions that come from this:
Am I doing "conceptually" right ? I
mean, is it correct to have instance
variable in managed object and
initialize like I did ?
when ns logging the size of the
persistentStorage I always get 0
even when logging a moment after the
addObject message (edit: that's not
true, I have verified again and I
correctly got 1 added).
The object stored in managed object
class trough persistentStorage are
normal class with attribute. Is
there something I need to do with
them ? I suppose not because I am
not getting any error at runtime.

No, that is not the "right" approach. You can perform initialization of instance variables in awakeFromFetch. Apple guidelines for NSManagedObject subclasses include the following:
You are also discouraged from
overriding
initWithEntity:insertIntoManagedObjectContext:,
dealloc, or finalize. Changing values
in the
initWithEntity:insertIntoManagedObjectContext:
method will not be noticed by the
context and if you are not careful,
those changes may not be saved. Most
initialization customization should be
performed in one of the awake…
methods. If you do override
initWithEntity:insertIntoManagedObjectContext:,
you must make sure you adhere to the
requirements set out in the method
description [...] (NSManagedObject Class Reference)
To really help, I'd need a deeper understanding of what you're trying to accomplish. Regardless, I strongly suggest combing through Apple's Core Data Programming Guide and sample code before proceeding.

I finally manage to solve this issue. Even if I am a newbie in objective-c, I think that introducing core data after the project is done, is not a good idea. Even if many claim it's easy. Unfortunately, all the people saying so, are showing as proof some really simple tutorial of one entity with one string attribute to change.
Instead for my project I ended up writing much code in addition to the existing one, plus some subclassing (NSManagedObject for example) which break the original model. This added code has also to be written carefully. Derived problem can be as simple as an attribute not saved, or dangerous as deleting wrong entities.
Infact, my problem was due to a wrong configuration in decode and encode method in the classes involved in the process of serialization.
For my questions:
-Point one still remain unanswered, because I am not yet confident in objective-c
-Point two, as I said the related object had some problem with encode/code.
-Point three, I was wrong, there's a lot of code to write, depending how complex is the relevant class.

Related

NSManagedObject subclass generated +CoreDataProperties.m files not actually required?

When generating an NSManagedObject subclass xcode creates two extra files MyObject+CoreDataProperties.h & MyObject+CoreDataProperties.m to keep the CoreData stuff away from our own code.
This is nice but I noticed in a WWDC clip that they didn't have the +CoreDataProperties.m file in their example. So I tried removing these in my code and everything compiles and runs fine without them, they don't appear to be necessary at all.
Are they required in some way that I've overlooked, or if not, then why does xcode generate them at all?
The short answer:
No, it is not necessary.
The long answer:
Objective-C is a dynamic typing, late binding programming language. In a short form that means, that every type decision can be made at runtime instead of compile time and you can access properties of an and send messages to an object without knowing its type (class).
But it is not necessary for the Core Data framework and you as the user of Core Data and your model to know the type of an managed object for a entity type. It is even not necessary to have a specific type for an entity type. (I do not generate the classes very often and if I do, I do it manually.) Therefore in contrast to other programming languages these generated classes are not necessary to give the compiler a type.
However, the compiler wants to see at least every method at least one time to get the signature (parameter typing). Otherwise it would warn. Even it is possible to have working code like this …
NSManagedObject *person = …
NSString *firstName = [person firstName];
… for an entity type Person with a property firstName, the compiler will warn you, that he does not know anything about a method –firstName.
Instead you can type something like this:
NSManagedObject *person = …
NSString *firstName = [person valueForKey:#"firstName"];
(The compiler knows -valueForKey:, since this is a method declared in NSObject.)
Beside this you get benefits like code completion, check for typing errors and so on. But you do not need to use the code generation tool of Xcode. Simply declare such a class and at the properties at the interface. The accessors can be dynamically generated using #dynamic. (Personally I nearly never use the static code generation.)
Edit: Added the result of the discussion in the comments.
So having the interface (".h-file") of the category, the compiler knows enough to compile the whole code without warnings. This will work at runtime, if it is guaranteed – or checked at runtime – that a corresponding message can be dispatched. This makes sense in many situations from the very beginning of Objective-C, i. e. for forwarding and informal protocols. In the context of Core Data it is used to dynamically generate standard accessor methods. Everything works fine without any implementation.
However, one wants to have an implementation for some reasons, i. e. housekeeping on value changes. In such a case it is useful to have a stub implementation you can edit. But for standard behavior it is not necessary.

How to set a breakpoint on "objectAtIndex:" method of a specific property in a specific class in XCode 4?

I would like to set a symbolic breakpoint on "objectAtIndex:" method of a specific property in a specific class.
See the following code :
#interface Foo
...
#property (strong,nonatomic) NSMutableArray *fooArray;
...
#end
I 've tried the following things:
-[[Foo fooArray] objectAtIndex:]
-[Foo::fooArray objectAtIndex:]
-[Foo::fooArray objectAtIndex]
Foo::fooArray::objectAtIndex:
Foo::fooArray::objectAtIndex
Foo::fooArray::objectAtIndex()
None of theses solutions work.
Any ideas to do the trick ?
Unfortunately, while this would be useful, it cannot work, for multiple reasons.
The first involves how methods are specified. A method signature for identifying a method in a breakpoint has three parts:
-¹[NSDictionary² objectForKey:³]
+¹[NSString² stringWithContentsOfURL:encoding:error:³]
Is this an instance method (-) or a class method (+)?
Which class's implementation of this method?
What's the selector of this method?
So your first problem is that what you have written for #2 is not a class.
What you do have is a class, followed in some fashion by a property name. This cannot work, because the debugger has no way to know whether that is a pure accessor—it cannot be sure that you, or whoever implemented that property, didn't write a custom accessor that does something else. This means that the debugger has no good, reliable way to obtain that value, or to know when that value changes, without potentially incurring side effects.
Moreover, the role of a class in a method signature is to identify which class provides the implementation you're setting a breakpoint on. That goes out the window as soon as you start trying to refer to a property that holds an object instead, because the debugger needs a class, and will have to get it from the object—and see the previous paragraph for some of the difficulties of knowing which object that is at all times.
(To be fair, it would indeed be possible for the debugger to watch the value of an instance variable—IIRC, both debuggers can already do this in a watchpoint, though reliability of watchpoints was flaky the last time I tried one. If the debugger could translate the property into its backing ivar, if it has one, and watch that, it would be a decent 90% solution for the majority of properties, which aren't backed by imaginative storage implementations and custom accessors. But the debuggers cannot do this today.)
The second reason is that NSArray is a class cluster.
You probably already know the first part of this (I suspect it's why you're trying to specify a single object by a property of another):
NSArray and NSMutableArray are both abstract classes, which in turn means that neither one implements the business of being an array; each one implements a bunch of convenience methods, while leaving a select set of core methods unimplemented, for subclasses to implement.
So, when you create an NSArray, you do not create an NSArray. The object you get back will be an instance of some private subclass of NSArray, with its own implementation of all of the details of how it manages an ordered list of objects.
So you could set a breakpoint on, say, -[NSArray objectAtIndex:], but it would never get hit, because nothing uses NSArray's implementation of objectAtIndex:—it would not make sense to use that implementation, because that implementation raises an exception (intended to catch subclasses that forget to implement it).
The part that breaks your question is:
While NSArray's implementations of various non-essential methods are defined ultimately in terms of the core methods, such as objectAtIndex:, that does not mean that subclasses are bound to use those implementations. A subclass could very well have its own implementations that don't use objectAtIndex:, if objectAtIndex: is not the most efficient way to do what they do (e.g., if the array is backed by a linked list rather than a C array).
So, to summarize this long answer:
It is not possible for the debugger to reliably watch the value of a property.
As such, it is not possible for the debugger to break when a method in the class of the object that is the value of that property is called, because the correct method to set the breakpoint on may change at any time, and the debugger cannot know when that happens.
Even if you could break on objectAtIndex: of some object identified by property, the array may validly never use objectAtIndex:, in which case your breakpoint would never get hit anyway.
You probably should ask another question about whatever you're trying to do by breaking on objectAtIndex:. I assume you're trying to investigate a bug in your app; that bug is probably another interesting question.
After some digging, I found a way to work this out. That's kinda ugly.
It involves creating a conditional breakpoint dynamically, in a command triggered by a first breakpoint.
First, break whenever your fooArray is ready. I settled on the fooArray accessor, but it could be done earlier :
breakpoint set --name "-[Foo fooArray]"
Then, what you want is break when objectAtIndex: is called on this specific array object. First let's put its pointer in a variable :
expr id $watch = self->_fooArray
and then create a new breakpoint, using this variable in the condition :
breakpoint set --name "-[__NSArrayI objectAtIndex:]" --condition "$rdi == $watch"
$rdi contains self, at least on x86_64. Use $r0 on ARM. (See Clark Cox's great post on the topic.)
-[NSArray objectAtIndex:] is never called. As Peter mentioned, NSArray is a class cluster, and your array is actually an __NSArrayI.
Or, in Xcode :
(Don't forget to check the "continue" box.)
It's not really beautiful, but it seems to work !
I am not at my Mac, so I cannot try this myself, but how about:
breakpoint set -n "-[[Foo fooArray] objectAtIndex:]"

Override a method in a single object instance

Am not sure how to put this, and I couldn't find the answer because of my inability to find the words to express what am looking for. (!)
In Java, I used to do something like this (I don't remember):
JPanel myButton = new JPanel("Press me"){
public void add(JComponent component){
//override add method
}
};
But, i couldn't find how to do this in Objective-C .. What I found in my search was categories and weird ^{} symbols ...
So, how can I override method(s) in a newly created object?
(For example, override -(BOOL)isEqual; in a newly created NSString* ?)
Am sorry if the question is a bit vague..
EDIT:
Obviously, without subclassing :)
EDIT:
Might as well post my problem in case someone has a better idea:
I have a few CCTransitions in COCOS2D, and I want to be notified when the transition ends .. The thing is, as soon as the transition ends, the -(void)finish; method is invoked (which is part of the CCTransition class structure)
I would really want to avoid subclassing the CCTransition class, and override the finish method to do my logic when the transition ends :)
EDIT:
-(void)onEnterTransitionDidFinish; ... I can't believe something as awesome as that existed and I haven't came across it while searching......
Which means, instead of subclassing CCTransition, override this method in my CCNode subclass :D!
It's still not going to be very clean, but assuming you're willing to concentrate the ugliness, you could do something like (untested):
Method methodToReplace =
[targetClass instanceMethodSignatureForSelector:#selector(methodToReplace)];
IMP implementationToSet =
[someProxyClass instanceMethodForSelector:#selector(implementationYouWant)];
method_setImplementation(methodToReplace, implementationToSet);
Relevant reference documentation is the Objective-C Runtime Reference and, optionally, the NSObject Class Reference (because it makes a few things slightly neater, though e.g. you could use class_getInstanceMethod from the runtime rather than instanceMethodSigntureForSelector:).
Note that you'll have no way to call the original implementation if you use exactly that recipe. method_setImplementation returns the old implementation, it's generally wise to add that to the class under a brand new selector and call that instead.
For reference, I've had a legitimate reason to do this sort of thing only exactly once: when we implemented printing support in an iOS application with which needed to be compatible with both OS 3.2 and 4.0. You need to subclass a particular class, but the class isn't available in 3.2. So you sort of have to subclass at runtime (though the conceptually neater way would be to use a normal subclass, put that into a framework and weak link, but Apple's iOS SDK terms allow static libraries only, so...).
Following Daniel's suggestion, you can implement a method in an NSObject category of the form
[anObject overrideMethod:#selector(foo:)
byBlock:^(id self,id super,id originalArg){
...
}];
What you need to do is to
objc_allocateClassPair against self's own class, to create a new temporary class
Turn a block into a function pointer, using e.g. this or this
method_setImplementation to set the new implementation to the temporary class
use object_setClass to self to set the class to the new temporary class
I haven't figured out how to provide super to the block :p
It's believed this is basically how the KVO is done by Apple, see e.g. this discussion.
Read Runtime reference.
What you have there in Java is an anonymous subclass. This is not possible in Objective-C (well, it sort of is but you would have to do some pretty involved contortions with the Obj-C runtime library).
But Objective-C as of iOS 4 or OS X 10.6 has "blocks", which is what the ^{} syntax is for. This is Objective-C's notion of a closure. This isn't going to help you directly if the APIs that you're calling don't support block callbacks, but you may be able to create wrapper classes that use blocks instead of subclassed methods to handle callbacks.
There are many resources for learning about blocks in Objective-C.

Adding multiple entries to a set while preserving the object-link

I have a question regarding the addition of multiple CoreData entries in multiple tables.
I have the following code:
while((item = [enumerator nextObject]))
{
DOCategory *cat;
cat = [[self categoryController] getNewCategory];
[cat setName:[item objectForKey: #"name"]];
[cat setDesc:[item objectForKey: #"description"]];
[cat setLastUpdate:updateTime];
//[[self categoryController] commitChanges];
}
I used to call the commitChanges after each enumeration, and that works, but takes a long time, especially on older devices as the initial load is quite substantial. Therefore I just want to save at the end of the whole operation.
What is the best way to add all those objectors to a NSSet (or NSArray) while preserving the link to the ManagedContext. Normally I would 'copy' them into the set, but that doesn't work here. A simple question, just have a block to seeing the best solution.
(I assume that I don't have to 'commit/save' after every new object I created, so the results are not written to the database yet, but are available for searches as there are different relational objects in the procedure)
Update:
After the suggestion below and more testing, it appears to me that when I haven't saved/committed the context it is not included NSFetchResultController. Is this right and supposed to be the way? Is there a way to do all the operations (including searches to create relations) in 'cache' and then commit once all is done?
Update 2:
In order to get the Managed Object I have a procedure in the Controller-class:
- (DOCategory *) getNewCategory{
return (DOCategory *)[NSEntityDescription insertNewObjectForEntityForName:#"Category" inManagedObjectContext:managedObjectContext];
}
It appears that the code runs fine, including cross references, until I come to adding the final object which uses all the other managed objects. There must be some problem in that code.
Your code is old school Objective-C 1.0 so it would be best to update it. You seldom use enumerators directly like that anymore.
The best way to handle this is to create the new managed objects by inserting them into the context using +[NSEntityDescription insertNewObjectForEntityForName:inManagedObjectContext:] this both creates the managed object and makes the context aware of it. The context will retain the managed object itself. That's were the "managed" of "managed object" comes from.
The normal practice when making a lot of changes is to change all the objects and then save the context only when you are done making all the changes.
Update:
It appears that the code runs fine,
including cross references, until I
come to adding the final object which
uses all the other managed objects.
There must be some problem in that
code.
That sounds like you might have a problem with the relationship being set properly in the data model itself. I can't say for certain without knowing what your data model looks like.
I discourage the use of cast with insertNewObjectForEntityForName: e.g.
(DOCategory *)[NSEntityDescription insertNewObjectForEntityForName:#"Category" inManagedObjectContext:managedObjectContext];
... because if you have a data model problem, such as not providing the name of the DOCategory class for the DOCatergory entity, the cast will obscure the problem. If there is no class defined the method will return an instance of the generic NSManagedObject class or if you accidentally define the wrong class for the entity e.g. MadeUpClassName instead of DOCategory then the cast will force the runtime to treat the returned object as a DOCategory object. That might work fine until way down in the code.
...it appears to me that when I haven't
saved/committed the context it is not
included NSFetchResultController. Is
this right and supposed to be the way?
Is there a way to do all the
operations (including searches to
create relations) in 'cache' and then
commit once all is done?
Not exactly sure what problem you are seeing. It is best practice to make all changes to all objects and then to save the context instead of saving after every change to any single object. Disk operations are expensive.
If your changes to the context are not reflected in the NSFetchResultsController (FRC) then you most likely did not implement the FRC delegate methods that update the tableview (see FRC docs.) Another possible error is that you've created two context accidentally. If you are using threads, each with a separate context (required practice) then you have to merge the background context with the front context before the changes made in the background are made known to the foreground.

Sending messages to objects?

I have done this successfully many times before, but this time it just won't work.
The code in my .h file:
#interface Images : NSView {
}
- (void) draw;
#end
The .m file (of another object):
- (IBAction) go: (id) sender; {
[Images draw:nil];
}
When I try to compile this, it says the following:
'*' may not respond to '*
Images may not respond to '+draw'
This has me quite confused. Shouldn't this be working?
Please Note: Yes, I have seen the other questions about messages, but none of those answers was even partially helpful for me. After reading them, I was even more confused.
Your draw method is an instance method: it can only be called on instances of the Images class. In your go: method you're trying to call it as a class method—if this is what you want, change it to:
#interface Images : NSView
+ (void)draw;
#end
I think a review of some of the basic concepts of object-oriented programming is in order; namely the difference between a class and an object or instance.
A class, in the general sense, is a collection of data and the functions which act upon it. A class defines the interface that one uses to access and manipulate data that is logically grouped together, and serves as a blueprint for creating objects or instances. See http://en.wikipedia.org/wiki/Class_(computer_programming)
Instances of a class (objects) are the typical things you manipulate in an object-oriented program, and they are created from the class "blueprint" and follow the behavior as specified by the class.
A typical example would be a fruit- take apples for example. An imaginary Apple class would represent all apples in the general sense and would model properties such as color and size and actions such as wash and eat. An instance would represent one, single physical apple- a Granny Smith or Pippin or whatever variety.
Just as it doesn't make sense to wash or eat apples in the general sense of the word (the concept of apples, not the agglomeration), typically it doesn't make sense to tell a class what to do. You tell objects (individual apples) what to do.
The code you present above defines the class Images. The "-" in front of -(void)draw indicates that the draw method only exists for specific objects. It is an instance method in typical OO parlance.
Of course, in Obj-C it is also possible to send a message to a class without requiring an object. This is denoted by a "+" in front of the method name, as other answers indicate. This is called a static method and it typically used to control some shared behavior or aspect of all objects of that particular class.
The problem with your code is that you are declaring -(void)draw as an instance method but calling it as a static method. Which way you want to do things is up to you, and it's difficult to determine from your code what the intent of the Images class is.