More of a programming nerd than is strictly healthy. See also {nevyn.nu, thirdcog.eu, twitter}

Projects

Overooped

Filtering a UITableView, and keyboard wonkiness

Hey, another bug that took half a day to fix. That always deserves a blog entry.

So I have a UITableView, and on it I have a tableHeaderView containing a UISearchBar. The UISearchBar filters the contents of the UITableView, and thus at every key press I have to reloadData (or more specifically, I -[UITableView reloadSections:withRowAnimation:]). For some reason, this calls setUserInteractionEnabled:NO on the UITableView, which in turn makes the UISearchBar’s UIFieldEditor resignFirstResponder, which makes the keyboard collapse. After the reload, user interaction is restored, UISearchBar gets focus, and the keyboard comes up.

This is rather embarrassing: I even subclassed UITableView, disabling setUserInteractionEnabled, before I realized that right there, before my eyes, is a UISearchBar delegate method called -[UISearchBarDelegate searchBarShouldEndEditing:]. Just add a bool ivar to the view controller saying whether the search bar may be resign key, set it to false before refreshing, refresh, and set it to true, and in the above delegate, return the ivar bool. Done, no more disappearing and reappearing keyboards.

Oh, and the code:


- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
	_searchBarMayResign = NO;
	[self.tableView reloadData]; // or equivalent
	_searchBarMayResign = YES;
}
- (BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar;
{
	return _searchBarMayResign;
}

NSFileHandle Considered Harmful [Updated]

Update 20090627: This bug has been fixed in 10.6. I’d still only recommend using it for very simple cases, but at least it works now!

NSFileHandle has a bug where the calling thread will lock up indefinitely if a data of size >4096 is requested. Reduced case:


#import 

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    NSFileHandle *fh = [[NSFileHandle alloc] initWithFileDescriptor:fileno(fopen("/dev/random", "r"))];
    
    NSLog(@"Reading 4096 bytes... This will succeed.");
    [fh readDataOfLength:4096];
    NSLog(@"Reading 4097 bytes... This will lock for infinity");
    [fh readDataOfLength:4097];
    NSLog(@"This will never be printed.");
    
    [fh closeFile];
    [fh release];

    [pool drain];
    return 0;
}

(Warning: running this program might lock up your computer; killing the rogue process is difficult as NSFileHandle will read an infinite amount of data from /dev/random, swapping out your OS.)

This is extra dangerous if your read length is dynamic, such as in a protocol where the first incoming bytes defines how long the upcoming chunk to read is.

Solution: Use the C API for working with file descriptors instead, such as read() and write(). A file descriptor can be extracted with [NSFileHandle fileDescriptor] if you receive one from another API.

(This bug has been reported.)