DEVELOPERS

Hints and Tips

by Andrew Stone

Twilight zone
NeXTstep 2.0 introduced a new memory-handling capability called zone allocation. By telling NeXTstep that you want certain objects to be allocated from the same hunk of memory, you can get significant performance improvements.

For example, you might allocate a Window and all the objects in it from the same zone. That way you reduce the chance of paging when clicking different objects in the window. The system pages instead when you click a different window.

You can easily create a new memory zone. Just use:

NXZone *myZone = NXCreateZone(vm_page_size, vm_page_size, NO); 
Then, instead of using alloc, you use allocFromZone:, passing in the zone you just created, like this:

id anObject = [[MyClass allocFromZone:myZone] init];
When you're done with the Window, you don't need to individually free all its objects (which chews cycles) Ð just throw away the zone:

NXDestroyZone(myZone);
All this is really just a warm-up for my tip: Freeing memory from a destroyed zone is a guaranteed show stopper. Be careful!

Unfortunately, it turns out that the only way to get a window off NXApp's windowList is to free it. To free a Window that's in a different zone, you've got to follow these steps:

1. Set the Window object's contentView to nil.

2. Free the Window object (removing it from the windowList).

3. Destroy the zone.

Otherwise, the next time you access the windowList, adios amigo!

Another type of window that might get on the windowList (and may need explicit freeing) is pop-up list. Remember, they're real Window objects, and cause your newly optimized app to die.

Popping with pop-up lists
Ever want to have a pop-up list that was small on the screen when collapsed but as wide as needed when popped up?

"You just can't do it," said NeXT. We had a need, however, for just such a pop-up when storing users' Find requests in DataPhile, because Find requests can be as arbitrarily complex as the user wants. But if two requests have field structures that are similar at the beginning but different at the end, the pop-up won't reveal any difference. Luckily, I bemoaned this sad shortcoming of the AppKit to Charles Perkins, who had already solved the problem for Marble Associates's Teleconnect.

NeXTstep pop-up list buttons are really buttons that send the popUp: message to a PopUpList object, which in turn displays the pop-up list on the screen. The button that you click is called a cover. The trick to displaying the popped-up selection at a different width from the button on the screen is to insert a custom object between the two.

Such an object is WidePopupManager, which sits between the button and the pop-up list and intercepts the popUp: message. NeXT's PopUpList object is fooled into thinking that the manager is the button; this lets you resize the pop-up list itself without changing the size of the cover. The list not popped-up (the cover) looks like this:

But when it pops up, you get the full size:

Initialize it like this, where "theButton" is the id of a PopUp List you created with Interface Builder:

id   popUp = [[PopUpList allocFromZone:[self zone]] init];
     NXAttachPopUpList(theButton,popUp);
[[WidePopupManager allocFromZone:[theButton zone]]
     initForButton:theButton andList:popUp];

When adding or removing titles to the pop-up, be sure to send the pop-up the sizeToFit message.

Here's the class interface and implementation:

/* WidePopupManager.h */
#import < objc/Object.h> 
@interface WidePopupManager:Object
{   id button, popupList;
}
- initForButton:aButton andList:aPopupList;
- popUp:sender;
@end
/* WidePopupManager.m */
#import < appkit/appkit.h> 
#import "WidePopupManager.h"
@implementation WidePopupManager
- initForButton:aButton andList:aPopupList
{
   button = aButton;
   popupList = aPopupList;
   [popupList sizeToFit];
   button setAction:@selector(popUp:)];
   button setTarget:self]:
   return self;
}
- getBounds:(NXRect *)theRect
{
   NXRect popupFrame;
   [popupList getFrame: &popupFrame];
   [button getBounds: theRect];
   if (theRect-> size.width <  popupFrame.size.width - 1)
      theRect-> size.width = popupFrame.size.width - 1;
   return self;
}
- setTitle:(const char *)aString 
{
   [button setTitle: aString];
   return self;
}
- convertPoint:(NXPoint *)aPoint toView:aView
{
   return [button convertPoint:aPoint toView:aView];  
}
- popUp:sender   
{
   return [popupList popUp: self];
}
- window   
{
   return [button window];
}
- display   
{
   return [button display];  
}
- (BOOL) needsDisplay   
{  
   return [button needsDisplay]; 
}
- (const char *) title   
{  
   return [button title]; 
}
@end