Using Multiple Delegates

For some XML documents, particularly large and complex documents, having a single delegate for the NSXMLParser object might not be the best approach. The code for handling all of the different parsing events can easily become overly intricate and hard to manage. One technique for making things more manageable is to share the work of handling parsing events among multiple delegates.

Take as an example an application that constructs a DOM-style tree from elements as it encounters them. Starting from the root element, one element creates a child element and passes off control to it by setting it to be the delegate. That child element creates its children (and so on), each time resetting the delegate appropriately. If an element has no children, or if it’s a mixed element, it accumulates the textual content for itself. Finally, when the parser encounters an element’s end tag, the element sets the delegate to be its parent element. Listing 1 shows the pertinent code that accomplishes this processing.

Listing 1  Resetting the delegate for the next element

- (void)parser:(NSXMLParser *)parser
        didStartElement:(NSString *)elementName
        namespaceURI:(NSString *)namespaceURI
        qualifiedName:(NSString *)qualifiedName
        attributes:(NSDictionary *)attributeDict {
    // Element is a custom class for object representing element nodes
    // Creation of element sets child as delegate (see below)
    [self addChild:[Element elementWithName:elementName
        attributes:attributeDict parent:self children:nil parser:parser]];
}
 
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
    [self appendString:string];
}
 
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
    Element *parent = [self parent];
    [parser setDelegate:parent]; // RESET DELEGATE TO PARENT
}
 
+ (id)elementWithName:(NSString *)elementName attributes:(NSDictionary *)attributes parent:(Element *)parent children:(NSArray *)children parser:(NSXMLParser *)parser {
    return [[[[self class] alloc] initWithName:elementName
        attributes:attributes parent:parent children:children
        parser:parser] autorelease];
}
 
- (id)initWithName:(NSString *)elementName attributes:(NSDictionary *)attributes parent:(id)parent children:(NSArray *)children parser:(NSXMLParser *)parser {
    self = [super init];
    if (self) {
         [self setName:elementName];
         if (attributes) {
               [self addAttributes:attributes];
         }
         [self setParent:parent];
         if (children) {
              [self addChildren:children];
         }
         [parser setDelegate:self]; // CHILD SET AS DELEGATE
    }
    return self;
}

Another technique for managing multiple delegates is maintaining a number of delegate objects, each with its specialized role, in a collection such as an NSDictionary object. These objects would know who their child and parent elements are in any given context and so would be able to set the delegate for the next element (using the appropriate dictionary key) after their work with the current element has finished.