5

有人能告诉我为什么 localComplete 块和 self.block 的内存地址是一样的吗?self.complete 的属性设置为复制,只是为了确保在将其分配给 self.complete 时我也在 localComplete 上调用复制。

- (void) test {

    CompletionBlock localComplete = ^{

    };

    NSLog(@"localComplete - %p", localComplete);

    self.block = [localComplete copy];

    NSLog(@"self.complete - %p", self.block);

    self.block();
}

这是输出:

2013-10-05 08:39:18.549 TestApp[90703:a0b] localComplete - 0x60b8
2013-10-05 08:39:18.550 TestApp[90703:a0b] self.complete - 0x60b8

作为另一个示例,我创建字符串:

// creating string
self.carType = [[NSString alloc] initWithFormat: @"Good%@", @"year"];
NSLog(@"self.carType - %p", self.carType);

// same memory address???
NSString *carInitString = [[NSString alloc] initWithString: self.carType];
NSLog(@"carInitString - %p", carInitString);

// same memory address???
NSString *carCopy = [self.carType copy];
NSLog(@"carCopy - %p", carCopy);

// different memory address
NSString *carInitWithFormat = [[NSString alloc] initWithFormat: @"%@", self.carType];
NSLog(@"carInitWithFormat - %p", carInitWithFormat);

和输出:

2013-10-05 09:45:01.667 TestApp[91103:a0b] self.carType - 0xa084910
2013-10-05 09:45:01.668 TestApp[91103:a0b] carInitString - 0xa084910
2013-10-05 09:45:01.668 TestApp[91103:a0b] carCopy - 0xa084910
2013-10-05 09:45:01.668 TestApp[91103:a0b] carInitWithFormat - 0xa336b70

为什么 carInitString 和 carCopy 不是不同的内存地址?在项目构建设置中关闭了优化。

4

2 回答 2

3

关于您最初的问题,通常在堆栈上分配块。复制一个块(使用Block_Copy函数或-copy方法)将在堆栈上移动块(进一步调用只会增加块的保留计数)

Block_copy [...],给定一个块指针,将底层块对象复制到堆,将其引用计数设置为 1 并返回新的块指针,或者(如果块对象已经在堆上)增加其引用以 1 计数

来源

因此,在您的示例中,您可能期望不同的地址,因为第一个块是本地的,而第二个块在堆上,但是由于该特定块没有对周围范围进行任何引用,编译器会将其标记为全局块。全局块不在堆栈上分配,而是在内存中的固定位置。

复制全局块不会将该块移动到任何地方。它只会增加它的保留计数,因为对象已经在堆上。这就是为什么你没有得到两个不同的地址。

尝试使用对周围上下文的引用来创建一个块,您将拥有两个不同的地址(堆栈和堆)。


现在,让我们解决您关于NSString.

复制不可变对象可能会导致retain作为优化(我的意思是框架设计优化,编译器与它无关),具体取决于类如何实现NSCopying协议。

这适用于许多基础课程,如NSString, NSArray, NSSet...

这完全符合NSCopying协议规范,您可以在文档中阅读:

您实现此协议的选项如下:

...

  • 当类及其内容不可变时,通过保留原始副本而不是创建新副本来实现 NSCopying。

正如 Greg Parker 在评论中指出的那样,-[NSString initWithString:]执行相同类型的优化。如果您将不可变字符串作为参数传递,它只会保留并返回相同的字符串。


这在某些情况下是一个有用的行为,这里有一个例子:你声明一个属性

@property (nonatomic, copy) NSArray *anArray;

然后在界面中公开它。

将其声明为copy一种很好的做法,以确保您正在处理的对象以后不会被客户端更改。如果您只是保留它并且客户端传入 aNSMutableArray您无法阻止她操作该对象。

所以复制是好的,但它看起来是有代价的:即使你不需要复制对象(即它是不可变的),你也会复制对象。

但是,由于上面讨论的行为,您不必付出这样的代价。发送copy给一个NSArray会保留它,而发送给一个NSMutableArray会实际复制它,所以在这种情况下是一个很大的胜利。

于 2013-10-05T17:01:05.060 回答
1

这可能是因为副本不是必需的。您不会捕获任何变量或类似的东西。所以也许块是恒定的。尝试让它引用本地或 __block 变量。

于 2013-10-05T15:58:42.173 回答