Error in NSMutableDictionary setObject processing (after updating to iOS10)

Update iOS10. after an error has occurred.

So far it had been operating normally.

Do you know solution?

(Objective-C of the specification change?)


(Error)

: *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFDictionary setObject:forKey:]: mutating method sent to">Oct 3 14:15:08 XXXXXXXX (CoreFoundation) [401] <Notice>: *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '- [__ NSCFDictionary setObject: forKey:]: mutating method sent to immutable object '


(Part of the Source code)

NSMutableDictionary * metadata;


switch (_selectImagePicker) {

case kCamera: {

// Get from the camera's metadata

NSMutableDictionary * metadataCamera = (NSMutableDictionary *) [info objectForKey: UIImagePickerControllerMediaMetadata];

Newly creating a metadata // copy the metadata acquired from the camera

metadata = [NSMutableDictionary dictionaryWithDictionary: metadataCamera];

break;

}


~ Snip ~


int imageW = resize.size.width;

int imageH = resize.size.height;

int Orient = resize.imageOrientation;


[Metadata setObject: [NSNumber numberWithInt: Orient]

forKey: (NSString *) kCGImagePropertyOrientation];

// Get a reference to the Exif

NSMutableDictionary * exif = metadata [(NSString *) kCGImagePropertyExifDictionary];


// Set the width and height of the resized ★ Error This Process ★

[Exif setObject: [NSNumber numberWithInt: imageH]

forKey: (NSString *) kCGImagePropertyExifPixelYDimension];


I am Japanese.

I'm sorry for my bad English.

Accepted Reply

The question that remains, is that for the first time error in iOS10.

The relationship between Foundation’s NSDictionary and Core Foundation’s CFDictionary (and now Swift’s Dictionary) is a long and complex one. Depending on how that pans out, some immutable dictionaries will detect mutation and some won’t. For example, for a long time the implementation of CFDictionary did not support immutability; internally, all dictionaries were mutable.

Moreover, this is not just limited to the behaviour of these low-level frameworks. Sometimes high-level frameworks use a mutable dictionary as a working object and return that to the caller, and sometimes the do that work in an immutable dictionary. Imagine if version 1 of a framework has code like this:

- (NSDictionary *)someProperty {
    NSMutableDictionary * result = [[NSMutableDictionary alloc] init];
    result[@"a"] = @1;
    result[@"b"] = @2;
    return result;
}

And in version 2 it gets updated to this:

- (NSDictionary *)someProperty {
    return @{
        @"a": @1,
        @"b": @2
    };
}

Both are correct, but version 1 will tolerate the client mistakenly mutating the result and version 2 won’t.

The upshot of this is that this mistake (mutating an defined-to-be-immutable dictionary) may or may not be trapped by the system.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Replies

Ah, I see. The issue here is that this line:

metadata = [NSMutableDictionary dictionaryWithDictionary: metadataCamera];

makes a mutable copy of the top-level dictionary but these lines:

NSMutableDictionary * exif = metadata [(NSString *) kCGImagePropertyExifDictionary];

[Exif setObject: [NSNumber numberWithInt: imageH]
        forKey: (NSString *) kCGImagePropertyExifPixelYDimension];

are mutating a nested dictionary within that top-level dictionary. That’s not guaranteed to work. When you make a mutable copy of an object, it only makes the top-level object mutable; any nested objects may or may not be mutable, depending on implementation details.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

I love reading the Might Quinn's posts. What he doesn't explicitly say is that the line:


NSMutableDictionary * exif = metadata [(NSString *) kCGImagePropertyExifDictionary];


sets exif to point to the object that is stored in the NSMutableDictionary "metadata" under the key kCGImagePropertyExifDictionary. This line does not make the object it is pointing to an "NSMutableDictionary" even though you explicitly tell the compiler it is an NSMutableDictionary. The compiler does not complain about the next line which requires that it be an NSMutableDictionary because you explicitly told it that it is an NSMutableDictionary. But when you try to execute that line it discovers that it is not an NSMutableDictionary because, as Quinn pointed out:

metadata = [NSMutableDictionary dictionaryWithDictionary: metadataCamera];

does not guarantee that the objects that are copied in that NSMutableDictionary are themselves mutable.


If you are sure the object is mutable you could replace:

NSMutableDictionary * metadataCamera = (NSMutableDictionary *) [info objectForKey: UIImagePickerControllerMediaMetadata];

with

metadata= (NSMutableDictionary *) [info objectForKey: UIImagePickerControllerMediaMetadata];

rather than making copies.

eskimo,PBK.

Thank you very much.


That the mutable object did not mutableCopy is I understand the cause.

I think I try to fix below.


NSMutableDictionary * exif = metadata [(NSString *) kCGImagePropertyExifDictionary mutableCopy];


The question that remains, is that for the first time error in iOS10.

What design changes in Objective-C?

What became severely?

The question that remains, is that for the first time error in iOS10.

The relationship between Foundation’s NSDictionary and Core Foundation’s CFDictionary (and now Swift’s Dictionary) is a long and complex one. Depending on how that pans out, some immutable dictionaries will detect mutation and some won’t. For example, for a long time the implementation of CFDictionary did not support immutability; internally, all dictionaries were mutable.

Moreover, this is not just limited to the behaviour of these low-level frameworks. Sometimes high-level frameworks use a mutable dictionary as a working object and return that to the caller, and sometimes the do that work in an immutable dictionary. Imagine if version 1 of a framework has code like this:

- (NSDictionary *)someProperty {
    NSMutableDictionary * result = [[NSMutableDictionary alloc] init];
    result[@"a"] = @1;
    result[@"b"] = @2;
    return result;
}

And in version 2 it gets updated to this:

- (NSDictionary *)someProperty {
    return @{
        @"a": @1,
        @"b": @2
    };
}

Both are correct, but version 1 will tolerate the client mistakenly mutating the result and version 2 won’t.

The upshot of this is that this mistake (mutating an defined-to-be-immutable dictionary) may or may not be trapped by the system.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

I am not an expert on the various 'other forms' used to mimic Objective C commands but I think your line:

NSMutableDictionary * exif = metadata [(NSString *) kCGImagePropertyExifDictionary mutableCopy];

will not compile. You want:

NSMutableDictionary * exif = [metadata [(NSString *) kCGImagePropertyExifDictionary] mutableCopy];

or better (i.e. more understandable):

NSMutableDictionary * exif = [ [metadata objectForKey:(NSString *)kCGImagePropertyExifDictionary] mutableCopy];

Post not yet marked as solved Up vote reply of PBK Down vote reply of PBK

eskimo.

Thank you very much.



Is operating by chance, I understand that originally was wrong.

Thank you for teaching!

PBK.

Thank you very much.


Thank you compile error pointed out.

It was helped.