Re: Issue with -[NSOutlineView autosaveExpandedItems] - SOLVED
Re: Issue with -[NSOutlineView autosaveExpandedItems] - SOLVED
- Subject: Re: Issue with -[NSOutlineView autosaveExpandedItems] - SOLVED
- From: Ken Thomases <email@hidden>
- Date: Sat, 12 Jul 2014 16:57:28 -0500
On Jul 12, 2014, at 3:50 PM, Bill Cheeseman <email@hidden> 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 (email@hidden)
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:
This email sent to email@hidden