why using NSArray (and other non-display graphic element) into Interface Builder - objective-c

using IB, i found that you could use NSArray not in a programatic way. I was thinking, great, i can make sets of uiviews into an NSArray and then get a proper outlet in xcode and retreiving all my uiviews from the NSArray.
The fact is when i put a NSArray into the nib file, i can't do nothing into it's property list. So the fact is i don't use the NSArray the correct way it should be. So is there anyone who could explain me how can i use my nsArray the way i want to? This will explain me why using non-display class into the nib file, and the purpose of it.
Thanks.

You can instantiate an NSArray instance in a nib file using the "Object" item in the IB library...but you probably don't want to.

Related

List all UIViewControllers of project

For automation of screenshot capture, and for testing purposes, I would like to list all UIViewController classes of my project.
I do not wish to get a list of all UIKit view controllers, I just want the ones I created in my project.
I want to do this in Objective-C code, at runtime, because I then will need to instantiate some of the listed classes.
For example, in my unit tests, I might want a test that assert that all UITableViewCell subclasses return the same height that the height of the item in the xib associated, and this object is not a UIView subclass but a UITableViewCell subclass.
An other intended use is to add to the documentation of the project a screenshot of all my UIViewController classes.
Note that this code will not be shipped to customer. It will only be used in testing and scripting on the developer machine.
I guess I could parse the files included in pbxproj, but that feels wrong and not robust.
A simple ls *ViewController.h on my project works too, but same feeling about it.
Any other idea?
Bonus if I can then extend this way on other classes, to for example get all the UITableViewCells I created, or all UIViews.
Using my NSObject+Subclasses category, you can easily get all subclasses of UIViewController.
To get your view controllers only, filter them like this:
NSSet *myViewControllerClasses = [[UIViewController subclasses_xcd] filteredSetUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
return [[NSBundle bundleForClass:evaluatedObject] isEqual:[NSBundle mainBundle]];
}]];
Then you can do whatever you want with the content of myViewControllerClasses that contains Class objects.
Using the runtime, it's a bit hard. You can use the definitions of class_t and class_rw_t (data-member of class objects) to explore subclass trees efficiently.
To filter your classes, you might need to look at the beginning of the class names (prefix) or maybe a base VC class if have created one for your project.
See this article

Storing NSMutableArray

I am currently storing an instance of a class in an NSMutableArray by using NSValue with "valueWithPointer". The problem is within this class I have another NSMutableArray, whenever I convert the instance of the class back from NSValue the NSMutableArray contained within it is of length 0, all of its elements have been removed. Does anyone know why this happens and how to solve it?
Any help would be much appreciated.
Unless there's a very good reason that you specifically need NSValue, you should not add a class instance to an NSMutableArray that way. Just do:
[myMutableArray addObject:myClassInstance];
This does all the right things with regards to memory management (and avoids ugly pointer value stuff), and should let you get at your class instance's array objects after retrieving the object. See the NSMutableArray docs for a quick starter on how to use the class properly.

NSArrayController + NSTableView : automatically save changes without Core Data

OK, so I'm implementing a classic scenario :
A NSPopupButton with some items in it
When the selected value changes, my itemsArray is updated
The itemsArray is linked to an NSArrayController
Each item in the itemsArray is an NSMutableDictionary (with keys : title,content)
An NSTableView displays the titles of the arrangedObjects (binding)
An NSTextView displays the content of the selected item.
Now, what I want is to automatically save any changes to the itemsArray (or itemsArray's item title/content), but without using Core Data (which I suspect might have been the best way to go about it).
I imagine it's quite a basic question this one, but honestly I've never really like Cocoa's auto-magical way of doing things... so, I need your help...
How should I go about that?
You can write an array to a file very easily:
[yourArray writeToURL:someFileURL atomically:YES];
This will work if all the contents of the array are property list objects (i.e. they are NSNumber, NSString, NSDictionary, NSArray or NSData objects). This is the case in your example.
You can then recreate the array using either the arrayWithContentsOfURL: or initWithContentsOfURL: methods when you load from disk.
If your model object is more complex than just an array, then you should make your model object conform to the NSCoding protocol. This means you need to implement the initWithCoder: and encodeWithCoder: methods. You can then use the NSKeyedArchiver and NSKeyedUnarchiver classes to convert your object to and from an NSData representation that you can write to disk.
You should read the Archives and Serialization Programming Guide for more detailed information.
Another solution might be to add a Shared User Defaults Controller and bind the Controller Content Array from the Array Controller to the Shared User Defaults Controller

How to access NSArray instance belonging to MyDocument in another xib?

In my document application I subclassed an NSArrayController which I referenced in MyDocument.xib bounding its content to File's Owner.entries.
entries is an NSArray I expose as an attribute of MyDocument class this way:
#interface MyDocument : NSDocument {
NSArray *entries;
}
-(NSArray *)entries;
-(void)setEntries:(NSArray *)newEntries;
#end
This works perfectly fine. Good.
Now I have designed another .xib, CSVEntries.xib which I open via a NSMenuItem through a NSWindowController and again, this works smoothly.
I can't figure out how to make this new window display the content of the MyDocument.entries instance in an NSTableView.
I have tried many things, but the problem basically is that any NSArrayController I put on CSVEntries.xib can not be bound to MyDocument.entries: CVSEntries.xib File's Owner is referring to something else not MyDocument (which is logical, I guess).
I also tried to add a copy of the NSArray instance to the NSWindowController which opens CVSEntries.xib, but since the window get's instantiated only when the user clicks on the menu, I ended up having an empty array.
I am searched around the Internet but could not find an appropriate answer, I just would like to figure out the proper way to approach the issue, I am sure two windows can communicate between eachother, maybe accessing their common parent (NSApplication instance)?
All newbie questions, I know :)
Update
I think I figured out where to look but still not how to. I think I have to make sure second window File's Owner is MyDocument but in the NSWindowController initWithWindowNibName:owner: I can't understand how to specify the MyDocument instance as the owner. Specifying it only in the xib file File's Owner seems not enough.
Update 2
Uhm...I am starting to think that I need to have two NSWindowController instances both instantiated by my NSDocument subclass (default MyDocument). With those in place maybe both the NSWindowControllers will have access to the MyDocument.entries NSArray and the two windows xib files will be able to be bound to File's Owner.entries.
Can someone confirm this? Thanks.
I reply to my own question here, basically stating that I have realized what I was asking is very much dependent on what the File's Owner is bound to. The design I implemented was not correct and I am therefore redesigning it to achieve my purpose.
Thanks everyone anyway, even no answers are an answer :)

Am I updating my NSArray that's bound to an NSTableView right?

I have a pretty standard setup where I have an array in my controller that I'm binding to an NSTableView. When I add an object to this array with the UI, I add elements by copying the array to a mutable array, adding the contents, then copying it back. I saw this idiom somewhere, but I'm not really sure where, or whether this is actually a sane thing to do. I t works fine on Snow Leopard, but it crashes really badly on Leopard. GDB tells me it happens right at the marked line, where I copy the new array back.
#interface MyAppDelegate : NSObject {
NSArray * urls;
//other declarations
}
#property (nonatomic, retain) NSArray *urls;
#implementation MyAppDelegate
#synthesize urls;
- (void)addThing:(id)sender {
NSMutableArray *newUrls = [NSMutableArray arrayWithArray: [self urls]];
[newUrls addObject: newurlimadding];
[self setUrls: [NSArray arrayWithArray: newUrl]];
}
I'm pretty sure I must be doing something boneheaded here. Is there a better way to do this? Why am I crashing?
NSMutableArray *newUrls = [NSMutableArray arrayWithArray: [self urls]];
[newUrls addObject: newurlimadding];
[self setUrls: [NSArray arrayWithArray: newUrl]];
What did you create newUrls for if not to set it as the new value of urls?
Besides that, there are a couple of things you're doing wrong:
No model objects. Bindings hates this. Unless your table view exists solely to display the different parts of the URL (scheme, host, path, etc.), each in one column, you're not giving Bindings what it wants.
Pay attention to the fields in the Bindings Inspector. Note that there are two: Controller Key and Model Key Path. They're exactly what they say on their tins: The Controller Key is the key for a property of the controller, which should provide model objects. The Model Key Path is the key path for one or more properties in the model, which usually should provide value objects, such as strings and numbers.
You probably need to create a model class and make the URL a property of that class. I'd guess that you have other objects, perhaps in parallel arrays, that you can move to properties of the model class. Do that, so that you're working with Bindings instead of against it.
Not using array accessors. You're just setting the whole array at once, which is inefficient and may cause display problems (such as selection highlights disappearing). Implement and use array accessors for this array property. Accessorizer can help you here.
I actually have no idea whether this will fix your crash because you haven't told us anything about it. You should edit your question to include any exception messages or other crash-describing output, which you'll find in the Run Log/Debugger Console.
Also, since the type of urls can be mutable, you should set it to copy, not retain.
NSArray * things; - since this can be modified you better represent using NSMutableArray instead NSArray.
When ever you need to add some new element to the list just use 'addObject' and insert element to the list.
Your binding will make sure that UI is updated using KVO and KVC.
It looks like the problem was that I had NSURLs as my object type. Once I changed the object in my array to a custom-made KVC compliant object, I wasn't getting any more crashes.
Maybe NSURL isn't KVC-compliant on 10.5, but it is on 10.6?