为什么改变字典中包含的可变数组不需要更新字典?

3
我在尝试CS193P(Objective-C)课程的一段代码时注意到编译器的工作方式。一个名为photos的NSMutableArray被添加到了一个NSMutableDictionary中,该字典叫做photosByPhotographer。稍后,对于photos进行了更改,而没有对photosByPhotographer进行任何更改。当我输出photosByPhotographer时,更改自动应用到它上面,不需要任何额外的代码!
我想知道编译器是如何实现这个功能的?有什么相关的材料可以阅读吗?
以下是代码:
- (void)updatePhotosByPhotographer
{
    NSMutableDictionary *photosByPhotographer = [NSMutableDictionary dictionary];
    for (NSDictionary *photo in self.photos) {
        NSString *photographer = [photo objectForKey:FLICKR_PHOTO_OWNER];
        NSMutableArray *photos = [photosByPhotographer objectForKey:photographer];
        if (!photos) {
            photos = [NSMutableArray array];
            [photosByPhotographer setObject:photos forKey:photographer];
            NSLog(@"photosByPhotographer in if: %@", photosByPhotographer);
        }
        [photos addObject:photo];
        NSLog(@"photosByPhotographer after if: %@", photosByPhotographer);
    }
    self.photosByPhotographer = photosByPhotographer;
}
NSLog() 的结果如下:
2012-07-20 20:05:57.618 Shutterbug[453:f803] photosByPhotographer in if: {
Dowbiggin =     (
);
}

2012-07-20 20:05:57.620 Shutterbug[453:f803] photosByPhotographer after if: {
Dowbiggin =     (
            {
        accuracy = 16;
        context = 0;
        dateupload = 1342836026;
        description =             {
            "_content" = "";
        };
        farm = 9;
        "geo_is_contact" = 0;
        "geo_is_family" = 0;
        "geo_is_friend" = 0;
        "geo_is_public" = 1;
        id = 7612787270;
        isfamily = 0;
        isfriend = 0;
        ispublic = 1;
        latitude = "37.307085";
        longitude = "-121.900395";
        originalformat = jpg;
        originalsecret = 052e70d412;
        owner = "22751315@N05";
        ownername = Dowbiggin;
        "place_id" = cils8sJUV7MeXHwt9A;
        secret = 4437007c99;
        server = 8161;
        tags = "square squareformat iphoneography instagramapp uploaded:by=instagram foursquare:venue=49f13597f964a5209c691fe3";
        title = "My little goofball";
        woeid = 55971033;
    }
);
}

2
这与编译器无关,而是可变对象的工作方式。 - jscs
4个回答

5

这是因为在Cocoa中,您使用对象和按引用传递。

想象一下这段代码:

NSMutableArray *a, *b;

a  = [NSMutableArray new];
[a addObject:@"A"]; // a contains @"A"
b = a; // b is assigned to the same object
[b addObject:@"B"]; // a and b contain @"A", @"B"

NSLog(@"a = %p and b = %p", a, b); // same values

变量 ab 指向 同一个对象。比较指针的值也可以看出这一点。
然而,如果我们执行以下操作:
NSMutableArray *a, *b;

a  = [NSMutableArray new];
[a addObject:@"A"]; // a contains @"A"
b = [[a mutableCopy] autorelease]; // b is assigned to a COPY of the object
[b addObject:@"B"]; // b contains @"A", @"B", while a still contains @"A"

NSLog(@"a = %p and b = %p", a, b); // NOT the same values

b被赋值为a的一份拷贝(而不是原始对象),因此b指向另一个对象。您可以通过比较这些对象的地址来进行检查。


3

您需要了解指针和引用的工作原理。基本上,与您代码中实际的完整结构和对象变量不同,通常它们是对这些结构的引用或指针。您可以以多种方式形象化这个概念,但也许有用的一种方式是将变量视为:

NSDictionary *myDict;

实际上,这只是一个数字,它是myDict对象的内存地址。以下是它的工作方式的可视化:

enter image description here

你的NSDictionary引用指向内存中的一个对象,该对象本身持有对其他对象的多个引用,这些引用可以更改而不影响字典本身。在这个例子中,我展示了一个NSArray添加了一个新对象。来自字典的数组引用保持不变。任何时候都只有一个数组及其中的对象的实例。


3

photos数组以引用的方式存储在NSDictionary中,而不是作为副本。因此,如果您更改底层数组,当您访问字典时,您将看到这一点。

这就是为什么通过引用提供某些可变类型而不是存储它们的副本可能会很危险的原因。


0
当您在NSDictionary对象上调用NSLog时,它会调用它的description方法。
description方法的实现中,NSDictionary会调用每个键的值(对象)的description方法。 因此,当一个值(对象)发生变化时,它的description方法的输出也会随之改变。 这就是为什么再次在NSDictionary对象上调用NSLog时可以反映出来的原因。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接