On Jul 12, 2014, at 3:50 PM, Bill Cheeseman <wjcheese...@gmail.com> wrote:

> The interesting method is -outlineView:itemForPersistentObject:. It 
> unarchives the archived UUID string. Then it uses recursive blocks to search 
> the data source array through all nested levels until it finds the item with 
> the same UUID identifier and returns it. Here is the method:
> 
> - (id)outlineView:(NSOutlineView *)outlineView 
> itemForPersistentObject:(id)object {
>    // Datasource method per NSOutlineViewDataSource formal protocol. Required 
> if using autosaveExpandedItems. Called when application launches, once for 
> each item in the autosaved array in the user defaults preferences file. 
> AppKit calls this before -awakeFromNib, so the source list data source must 
> be populated in the designated initializer.
> 
>    NSString *identifier = [NSKeyedUnarchiver unarchiveObjectWithData:object];
> 
>    __block id returnItem = nil;
>    __block void (^findItemForIdentifierInArray)(NSArray *) = ^(NSArray 
> *contents) {
>        [contents enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL 
> *stop) {
>            if ([[obj objectForKey:ID_KEY] isEqualToString:identifier]) {
>                returnItem = obj;
>                *stop = YES;
>            }
>            id subarray = [obj objectForKey:CONTENTS_KEY];
>            if (subarray) {
>                findItemForIdentifierInArray(subarray); // recursive
>            }
>        }];
>    };
> 
>    findItemForIdentifierInArray([self sourceListContents]);
>    return returnItem;
> }
> 
> The only remaining problem is that the recursive call to 
> findItemForIdentifierInArray() is flagged with a warning that it is likely to 
> lead to retain cycles, hence leaks. I don't know how to fix this, despite 
> some hours of online searching. Can anybody with greater knowledge of blocks 
> help me on this?

You could do it this way:


   __block void (^__unsafe_unretained 
innerFindItemForIdentifierInArray)(NSArray *);
   __block void (^findItemForIdentifierInArray)(NSArray *) = ^(NSArray 
*contents) {
       [contents enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL 
*stop) {
           if ([[obj objectForKey:ID_KEY] isEqualToString:identifier]) {
               returnItem = obj;
               *stop = YES;
           }
           id subarray = [obj objectForKey:CONTENTS_KEY];
           if (subarray) {
               innerFindItemForIdentifierInArray(subarray); // recursive
           }
       }];
   };
   innerFindItemForIdentifierInArray = findItemForIdentifierInArray;

This still has a problem.  It doesn't stop when you intend it to.  Any given 
-enumerate... call will stop if it directly finds the match, but an outer call 
won't.  You could add "if (returnItem) *stop = YES;" after the recursive call.  
Or, you could pass the "stop" variable into the recursive call so that an inner 
call can set it.

It seems like your contents are dictionaries.  It might be better to make them 
a custom class with the appropriate properties.  Then, you could add a method 
like:

- (TheClass*) descendantWithIdentifier:(NSString*)identifier
{
    if ([self.identifier isEqualToString:identifier])
        return self;

    for (TheClass* child in self.children)
    {
        TheClass* match = [child descendantWithIdentifier:identifier];
        if (match)
            return match;
    }

    return nil;
}

Regards,
Ken


_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to