Objective C - Change all attributes in NSAttribute

2019-04-06 12:39发布

问题:

[attributedString enumerateAttributesInRange:range options:NSAttributedStringEnumerationReverse usingBlock:
     ^(NSDictionary *attributes, NSRange range, BOOL *stop) {

         NSMutableDictionary *mutableAttributes = [NSMutableDictionary dictionaryWithDictionary:attributes];
         [mutableAttributes setObject:[NSNumber numberWithInt:1] forKey:@"NSUnderline"];
         attributes = mutableAttributes;

     }];

I am trying to loop through all attributed and add NSUnderline to them. when debugging it seems like NSUnderline is added to the dictionary, but when i loop for the second time they are removed. Am I doing anything wrong while updating NSDictionaries?

回答1:

Jonathan's answer does a good job of explaining why it doesn't work. To make it work, you need to tell the attributed string to use these new attributes.

[attributedString enumerateAttributesInRange:range options:NSAttributedStringEnumerationReverse usingBlock:
     ^(NSDictionary *attributes, NSRange range, BOOL *stop) {

         NSMutableDictionary *mutableAttributes = [NSMutableDictionary dictionaryWithDictionary:attributes];
         [mutableAttributes setObject:[NSNumber numberWithInt:1] forKey:@"NSUnderline"];
         [attributedString setAttributes:mutableAttributes range:range];

 }];

Changing the attributes of an attributed string requires that it is a NSMutableAttributedString.

There is also an easier way to do this. NSMutableAttributedString defines the addAttribute:value:range: method, which sets the value of a specific attribute over the specified range, without changing other attributes. You can replace your code with a simple call to this method (still requiring a mutable string).

[attributedString addAttribute:@"NSUnderline" value:[NSNumber numberWithInt:1] range:(NSRange){0,[attributedString length]}];


回答2:

You're modifying a local copy of the dictionary; the attributed string does not have any way to see the change.

Pointers in C are passed by value (and thus what they point to is passed by reference.) So when you assign a new value to attributes, the code that called the block has no idea you changed it. The change does not propagate outside of the block's scope.