NSTreeNode mutableChildNodes not working as it should? - objective-c

I'm totally baffled why this is not working. I'm trying to insert a new NSTreeNode into a mutable array of child nodes. Here's the code:
NSTreeNode *newNode = [[NSTreeNode alloc] init];
NSMutableArray *children = [anExistingParentTreeNode mutableChildNodes];
[children addObject:newNode];
Upon execution I get all sorts of errors:
-[NSCFSet initWithObjects:count:]: attempt to insert nil object at objects[0]
-[NSTreeNode _tearDownObserving]: unrecognized selector sent to instance 0x2000bff40
Serious application error. Exception was caught during Core Data change processing: -[NSTreeNode _tearDownObserving]: unrecognized selector sent to instance 0x2000bff40 with userInfo (null)
The errors seem to be dealing with KVO stuff. Has anybody encountered errors like these using mutableChildNodes? Any help is greatly appreciated.
Note: The underlying NSTreeController IS bound to core data via managed object context.

Could it be that you have not initialized the newNodeobject correctly?
The only init method defined for the class is:
- (id)initWithRepresentedObject:(id)modelObject
When you use init, you just use the default implementation inherited from NSObject.
Normally, a class has one or more designated initializers, but in the case of NSTreeNode, I can't see that it is specified in the docs. However, since there is only one initializer defined for the class, and no setter methods to set the represented object at a later stage, I'd conclude that initWithRepresentedObject: is the designated initializer of the class.
About initializers: http://developer.apple.com/library/ios/#documentation/general/conceptual/DevPedia-CocoaCore/MultipleInitializers.html

Refer to my last comment on the original question.

Related

Cannot perform operation because childrenKeyPath is nil

Why do I get this crash error when I try to insert a child into my NSTreeController?
NSTreeController *tree = [NSTreeController new];
NSTreeNode *node1 = [NSTreeNode treeNodeWithRepresentedObject:#{ #"type": #"shirt" }];
// The below method causes mysterious crash
[tree insertObject:node1 atArrangedObjectIndexPath:[NSIndexPath indexPathWithIndex:0]];
Xcode says:
* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '[object class: NSMutableDictionary] Cannot perform operation because childrenKeyPath is nil'
*
Why is causing this error? What is childrenKeyPath and why do I need it (I'm not using interface builder)?
I've made several mistakes here. You should not insert instances of type NSTreeNode into an NSTreeController with insertObject. You use insertObject to insert a model object, at which point an NSTreeNode will be automatically created for it.
Also, in your model object, you need to have a property or key which is set as an NSMutableArray. Then, before you insert any model objects, you set the childrenKeyPath property of NSTreeController to equal the name of that property or key.
This is because NSTreeController and NSTreeNode are not designed to hold data or child objects themselves, but act as a simple but helpful "map" for the models you've created and retained.
I hope this information helps others because the rest of StackOverflow was oddly silent about it...

How to properly use makeObjectsPerformSelector: Getting error unrecognized selector

Let me start off by saying I am new to Objective C.
I am getting the error
atusMenuApp[24288:303] -[__NSCFConstantString createListItem]: unrecognized selector sent to instance 0x100002450
Here is my code:
selector = [NSMutableArray arrayWithObjects: #"nvda", #"aapl", #"goog", nil];
[selector makeObjectsPerformSelector:#selector(createListItem:) withObject:self];
- (void)createListItem:(NSString *)title {
//do some stuff
}
Now I have done plenty of looking around and it seems like the biggest reason for this issue is the addition of or lack of the :however I do believe I properly have that in place. Maybe I do not understand the use of makeObjectsPerformSelector very well as after look up the doc on it I found:
Sends to each object in the array the message identified by a given selector, starting with the first object and continuing through the array to the last object.
Any help would be great, Thanks!
[Only if you read the documentation (or thought a bit about why a method is named this way and not that), or even made the effort trying to understand the error message...]
The makeObjectsPerformSelector:withObject: method of NSArray does what it suggests it does: it makes the objects of the array perform the selector, that can have an optional argument. So
[selector makeObjectsPerformSelector:#selector(createListItem:) withObject:self];
will send the createListItem: message to every single NSString object in the selector array and pass in self as its argument. It won't perform the selector on self passing in the object. I. e., what you have is equivalent to
for (NSString *obj in selector) {
[obj createListItem:self];
}
Obviously, instead of this, you want the following:
for (NSString *obj in selector) {
[self createListItem:obj];
}
You don't even need that nasty method for this. A nice fast enumeration for loop will do it.
First you make an array of NSStrings. Then, you send them all the message createListItem. That's all fine and dandy, but NSString doesn't have any method called createListItem; just because you've defined an instance method called createListItem doesn't mean every instance of every class can use it. Only the class who's implementation file has the definition will be able to handle the message. For instance, I can't make a list of Car instances, then define the method fly in another class called Helicopter's implementation and expect to be able to call fly on an instance of Car; only Helicopter can use it. I recommend you read a good book on Objective-C and further familiarize yourself with classes, instances and instance methods.
You misunderstood the method.
It will call the method createListItem: with argument self over every object of the NSArray.
So the resulting call would be something like:
[#"nvda" createListItem:self];
...
Clearly that method doesn't exist for a NSString and there goes your exception.
If you need to apply a method of self to every object inside your array, simply loop through it.

Why does calling a property on NSObject pointer gives build errors?

I have an NSMutableArray which returns me some object.
The object which I added had properties name,age.
now when I use these properties on the Object returned (obj.name or obj.age ),
Compiler says, no such member, use (->) instead of (.)
I understand that NSObject wont have these members and hence it wont understand the property.
But If i use setters, and getters as method ([obj name] or [obj age]) syntax instead of this properties, I dont get any errors.
But using property means calling a setter or getter only ?
ad Objective C is suppose to be dynamic language, right ?
Do you cast the returned object to your object type (MyObject)?
You should do something like:
((MyObject*)[mutableArray objectAtIndex:0]).age = 20;
The reason you're not getting any errors when using [[mutableArray objectAtIndex:0] name] syntax is that you're calling a method on the returned object (which is of type id), and id s tend to not choke in the compile-time if you call a (yet) non-existant method on them. At the run-time, [mutableArray objectAtIndex:0] might resolve to type MyObject an in that case, the message [obj name] has a proper implementation (IMP). If it doesn't resolve to MyObject, your app will crash.
And note that the reason you're not even getting a compile-time warning is that Xcode knows that there is at least 1 class in your codebase that implements the method name, and it trusts you with calling this method only on instances of that class. if you do something like ((MyObject*)[mutableArray objectAtIndex:0]).ageeeeee = 20;, it'll give you a warning as there's a very good chance that it'll crash (no class in your app implements the method ageeeeee statically).
The type id does not have a property name, and that's why you can't use dot syntax.
Actually, this incident shows perfectly why ObjC is called a dynamic language!
That's right - dot syntax is not supported in such case.
You need to cast a pointer to the actual class:
((MyObject*)[array objectAtIndex: 0]).name = #"Bill";

Problem assigning value obtained from [array objectAtIndex:]

In my Piano class, I have a property (Keys is another custom class)
#property (nonatomic, retain) Keys *lastPlayed;
In one of my Piano methods, I set the value of lastPlayed using an object from an array of Key objects.
self.lastPlayed = [allKeys objectAtIndex:variable];
The above line of code causes the program to crash.
I've noticed that if I hardcode a specific Key object from the allKeys array, then it works fine. Like so:
self.lastPlayed = keyC;
Interestingly, it doesn't crash if I put the crashing code into a different method.
How can I prevent the crash?
EDIT:
I call this method in the Keys class, where my piano is the delegate
[delegate deliverTagwithNameTag:self.tag]
the piano then responds
- (void) deliverTagwithNameTag:(int)nameTag {
self.lastPlayed = [allKeys objectAtIndex:nameTag];
}
You're probably not getting a plain unexplained crash, you're probably raising an exception. You can watch the console to find out which exception you raised. Normally in this sort of situation it'll be something useful to tell you either that you're asking the array for an out-of-bounds value (which could happen if variable were collecting an incorrect value somehow), that the array itself is invalid (which could be a memory allocation problem) or that the thing returned can't be stored as lastPlayed (which would normally indicate you're doing something custom in the setter and getting an unexpected type of class).
So to prevent the crash, check your console and look for one of those problems.

Why do some objects not need to be initialized before use in objective-c?

Why do some objects not need to be initialized before use in objective-c?
For example why is this NSDate *today = [NSDate date]; legal?
They are initialized within the date method. This is a common way to create autoreleased objects in Objective-C. Allocators of that form are called convenience allocators.
To learn more about that, read the "Factory Methods" paragraph in Apple's Cocoa Core Competencies document about Object Creation: http://developer.apple.com/library/mac/#documentation/General/Conceptual/DevPedia-CocoaCore/ObjectCreation.html
To create convenience allocator for you own classes, implement a class method, named after your class (without prefix). e.g.:
#implementation MYThing
...
+ (id)thing
{
return [[[MYThing alloc] init] autorelease];
}
...
#end
today is initialized (and autoreleased) inside the static date call.
You only need to called an init… method on objects you have allocated by calling alloc. alloc only reserves space needed for the object, creating a an unitialized object.
An uninitialized object have all instance variables set to zero, nil, or equivalent for the type. Except for the retain count that is set to 1.
All other methods that return an object are guaranteed to return a fully initialized object. alloc is the exception.
You must never call an init… method on an object that is already initialized. Simple rule on thumb is to use a 1-to-1 relation between alloc-init…, thats it.
Two parts.
First, as others have mentioned, a method can initialise and then autorelease an object before returning it. That's part of what's happening here.
The other part is how it's defined. Note how most Objective C definitions begin with a -? The one you mention does not. The signature looks like this:
+ (NSDate*) date;
That is, it's a class method and applies to the class as a whole rather than to an instance of that class.