I must be missing something. I'm in the chapter titled "Key-Value Observing" in Cocoa Programming by Aaron Hillegass.
I have inserted the code that enables the application to undo/redo adding and subtracting employees from RaiseMan. The application works however what I'm wondering is why is that when I link the "Add Employee" to the NSArrayController to the ADD method (using the .xib file) it calls
- (void)insertObject:(Person *)p inEmployeesAtIndex:(int)index;
According to the Key-Value Coding, shouldn't the add method call?
- (void)addEmployeesObject:newEmployee;
I'm linking the 'add' method not the 'insert' method.
Your array controller is bound to an ordered collection (an array). That's why it uses insertObject:inEmployeesAtIndex: to add a new object at the end of the the collection.
The addEmployeesObject: method would be used if the collection was unordered (i.e. a set).
This is expected behavior when using KVC collection accessor methods. It's more efficient to insert an object at the desired location (even if that location is at the end) than to wonder if "it's at the end" and call -add... directly. All of this is much more efficient than, say, replacing the entire array with an entirely new (-setEmployees:, for example) array when the range of the modification is already known. NSArrayController ultimately uses this method when inserting an object into the array it's controlling.
Related
I would like to check if my NSMutableArray contains my custom object. But if I understand correct contains functions searches for the same object in array (placed at the same memory point)
if(![objectArray containsObject:objToCheck])
{
[objectArray addObject:objToCheck];
}
I know that objectArray has identical object with identical variable values compared to objToCheck, yet such if always returns false. Is there a way to check this without writing custom loop and comparing objects by their parameters?
Override the [NSObject isEqual:] method (actually it's part of the NSObject protocol) of your custom object and check whatever instance variables make sense to you for an object to be considered equal.
Here's an Apple Cocoa Competency article on the subject.
You might try creating a temporary NSSet from your array and testing against that for membership.
I'm using a NSManaged object subclass that was generated by the core data GUI tool in XCode.
NSLog'ing the object reveals that it's properly instantiated and holding values... But if I try to use something like this:
[generatedSubClass committedValuesForKeys:nil]
I get back an empty dictionary.
The docs state that committedValuesForKeys
Returns a dictionary of the last fetched or saved values of the receiver for the properties specified by the given keys.
It follows that the object you are messaging is not the last fetched or saved.
Also, note that committedValuesForKeys is an instance method, not a class method.
Thus, not [managedObjectSubclass committedValuesForKeys:nil]
but [aManagedObject committedValuesForKeys:nil]
I have an NSTableView that binds via an NSArrayController to an NSMutableArray of NSString's.
There's also a TextView in the same window. What I want to do is display a number of occurrences of each NSString in the NSTableView.
I've thought of a way to do this, but that doesn't look elegant way of doing this:
Inherit from NSString and define new method that performs a search in predefined object (NSTextView) and returns the number of occurrences.
I'm guessing there must be a more natural way of achieving the same result?
EDIT:
Sorry, should have clarified. NSSMutableArray is an array of NSObjects that have an NSString property. I suppose I could define an extra method (findAllOccurencesOfString:inString:) which would return a number. But the question is how do I bind to this function and in that binding how to I pass a var (pointer to textField)??
You'll need to have a wordCount (read only) property on whatever objects are in your table data source, this will have to call your new method internally using the object's own string value, as you can't pass parameters in bindings (unless they've changed, I haven't used bindings for a while as I've been concentrating on iOS). Then bind to this property for the column in the table. Presumably you don't need to pass the pointer to the textfield as there is only one?
I have a class and I want my class to confirm to the NSFastEnumeration Protocol. I've read the documentation but it's not really clear. Can someone please tell me what the protocol method should return and how it works?
Apple's FastEnumerationSample shows you what to do, but here's a breakdown.
The sole NSFastEnumeration method, countByEnumeratingWithState:objects:count:, returns chunks of the collection. It's executed whenever more items are needed, until it indicates that there are no more items by returning 0. A chunk is passed as a C array of ids.
Within the method, the state parameter holds most (if not all) of the data you'll be using. You'll need to set state->itemsPtr and update state->state with each separate invocation of countByEnumeratingWithState:objects:count:. Here's a brief description of each field of NSFastEnumerationState:
state: represents the position in the sequence being iterated over. For indexed collections, this would be the index. For linked lists, this could be a node pointer. For other types, this could be a more complex type (e.g. for a tree, state->state could be an NSMutableArray used as a stack to store nodes). When countByEnumeratingWithState:objects:count: is first called, state->state is 0; check for this condition to initialize the state struct.
itemsPtr: the items in the chunk; points to a C array of ids. Cocoa will loop over this array, binding each item in turn to the variable named in the for-in loop.
mutationsPtr: for mutable collections, used to indicate that the collection has changed since the last call to countByEnumeratingWithState:objects:count:. Typically, you'd set this once when initializing the state. Collection mutators increment the value that this points to. Cocoa will compare the value returned by countByEnumeratingWithState:objects:count: to the value from the previous invocation; if they're different, Cocoa will throw an exception.
extra: you can use this to store extra data.
You can set state->state and any element of state->extra to whatever you wish; they're provided solely for your convenience, and do not affect Cocoa. state->itemsPtr, *state->mutationsPtr and the value returned by the method, however, do affect Cocoa.
As for the two other method parameters, stackbuf is an array that Cocoa provides to hold items. Its use is optional, but if you don't use it, you'll have to allocate storage space for state->itemPtr. If you use it, set state->itemsPtr to stackbuf with each invocation. len is the length of stackbuf, the maximum number of items that you'll be able to store in it.
Further reading:
Friday Q&A 2010-04-16: Implementing Fast Enumeration (mikeash.com)
Implementing countByEnumeratingWithState:objects:count: (Cocoa with Love)
NSFastEnumeration Protocol Reference
Implementing NSFastEnumerator on Custom Class (SO)
Just reviving this thread after finding an excellent explanation. The Apple link seems to be broken. You can try here: https://developer.apple.com/library/ios/#samplecode/FastEnumerationSample/Introduction/Intro.html
The best example for implementing fast enumeration that I've found is at: http://mikeash.com/pyblog/friday-qa-2010-04-16-implementing-fast-enumeration.html. It looks much worse than it is.
I've been attempting this for two days, and constantly running into dead ends.
I've been through Aaron Hillegass's Cocoa Programming for MAC OS X, and done all the relevant exercises dealing with NSTableview and mutable arrays, and I have been attempting to modify them to suit my needs.
However none of them seem to be using an array with objects as a data source, it seems to use the tableview as the datasource.
I'm trying to implement Jonas Jongejan's "reworking" of my code here, with a Cocoa front end to display the results.
Any pointers or suggestions I know this should be simple, but I'm lost in the wilderness here.
I can populate the table by setting the array
It's pretty simple really, once you get to understand it (of course!). You can't use an NSArray directly as a table source. You need to either create a custom object that implements NSTableViewDataSource or implement that protocol in some existing class - usually a controller. If you use Xcode to create a standard document based application, the document controller class - (it will be called MyDocument) is a good class to use.
You need to implement at least these two methods:
– numberOfRowsInTableView:
– tableView:objectValueForTableColumn:row:
If you have a mutable array whose values you'd like to use in a table view with one column, something like the following should do as a start:
– numberOfRowsInTableView: (NSTableView*) aTableView
{
return [myMutableArray count];
}
– tableView: (NSTableView*) aTableView objectValueForTableColumn: (NSTableColumn *)aTableColum row: (NSInteger)rowIndex
{
return [myMutableArray objectAtIndex: rowIndex];
}
It has just occurred to me that you could add the above two methods as a category to NSArray replacing myMutableArray with self and then you can use an array as a data source.
Anyway, with a mutable array, it is important that any time you change it, you need to let the table view know it has been changed, so you need to send the table view -reloadData.
If your table view has more than one column and you want to populate it with properties of objects in your array, there's a trick you can do to make it easier for yourself. Let's say the objects in your array are instances of a class called Person with two methods defined:
-(NSString*) givenName;
-(NSString*) familyName;
and you want your table view to have a column for each of those, you can set the identifier property of each column to the name of the property in Person that that column displays and use something like the following:
– tableView: (NSTableView*) aTableView objectValueForTableColumn: (NSTableColumn *)aTableColum row: (NSInteger)rowIndex
{
Person* item = [myMutableArray objectAtIndex: rowIndex];
return [item valueForKey: [tableColumn identifier]];
}
If you replace valueForKey: with valueForKeyPath: and your Person class also has the following methods:
-(Person*) mother;
-(Person*) father;
-(NSString*) fullName; // concatenation of given name and family name
you can add table columns with identifiers like: father.fullName or mother.familyName and the values will be automatically populated.
You could go the datasource route and do all of the heavy lifting yourself, or you could let bindings do all the heavy lifting for you. Add an NSArrayController to the nib file that has the table view in it. Make sure that the File's Owner of the nib is set to the same class that has the mutable array in it. Bind the contentArray of the array controller to File's Owner.myMutableArray. For each column bind Value to the array controller arrangedObjects and add the appropriate key path. This will allow you to get things like user sorting for free if you ever need it.
On the iPhone (I know you're talking about Mac, but maybe this could help) you have to use delegation for loading a tableView. It asks for a cell and you use your array to fill-in the data where needed.
I'm not sure if this works for the Mac, but it'd be worth looking into.
Maybe set dataSource to self and use those delegate methods to access your array based on the row and column #
Apple has a whole guide for Table View Programming so I suggest you start with the Using a Table Data Source section of the that guide.