It's because valueForKeyPath:
also applies to the dictionaries. And how does it work? It looks in each dictionary, tries to use "self"
as a key, finds no such key, and returns NSNull to represent the missing value.
To see what I mean, consider this variation on your example:
NSArray *toBeFlatten = @[@[@{@"self":@"hey"}],@[@{@"other":@"ho"}]];
NSArray *flat = [toBeFlatten valueForKeyPath:@"@unionOfArrays.self"];
The result is @[@"hey", NSNull.null]
— the value "hey"
for the matching key "self"
in the first dictionary, and the null because no key matched in the second dictionary.
What you probably meant to say is valueForKeyPath:@"@unionOfArrays.@self"
(notice the at-sign, making self
an operator, not a key).