7

我熟悉__block语句,它使块中的变量'可分配'。 但是我看到,当使用一些Objective-C功能使用块作为方法中的参数时,即使它们没有用此__block语句声明,也可以分配一些变量。iOS >>块>>更改块外部变量的值

下面是实施例2级的代码:

[UIView animateWithDuration:2 animations:^ 
    { 
     self.animatedView.backgroundColor = [UIColor blueColor]; 
     self.animatedView.center = CGPointMake(100, 100); 
    }]; 

(animatedView是一个IBOutlet连接的简单的UIView)。

int myInt = 10; 
    NSMutableString* mString = [NSMutableString stringWithString:@"Mutable Hello"]; 
    NSString* imString = @"Imutable Hello"; 

    void (^myBlock)(void) =^
    { 
     [mString appendString:@" Block"]; //working 
     imString = @"Imutable Hello Block"; //error 
     myInt = 11; //error 
    }; 

我的问题是:为什么我可以将值分配给UIView的实例属性?

我没有解决一个对象并改变它,就像我的mString。

我期望'中心'属性的行为就像我的myInt一样,因为它是直接访问的C结构,而不是指向对象的指针。

我期望'backgroundColor'的行为就像我的imString一样,因为它是一个指向一个新对象的对象的指针,不是吗?

我无法在文档中找到令人满意的解释......如果有人能提供一个,或者将我定位到一个,我将不胜感激。

回答

8

这是分配和使用之间的区别。用法是方法调用。您完全可以调用实例上的方法([mString appendString:@" Block"]; //working),但不能在未标记变量的情况下分配(imString = @"Imutable Hello Block"; //error),以告知编译器它应该启用它。

此代码:

self.animatedView.backgroundColor = [UIColor blueColor]; 

还没有真正的分配,这是一个“隐藏”的方法调用。点符号不是一个赋值,它是方法调用的语法糖。它实际上转化为:

[[self animatedView] setBackgroundColor:[UIColor blueColor]]; 

分配给本地变量和对象内的变量之间的区别是在内存中,它们驻留在的位置。基本上,它们会存在足够长时间才能有用。这是堆栈和堆上的数据之间的区别。

+0

让“BOOL隐藏”的事实或“CGPoint中心”追究他们背后的ivars不会有什么区别?这是否意味着它们是属性(通过Setter方法访问)会改变它们在内存中的分配方式?我也尝试过在我的课堂上声明的伊娃(而不是方法中的局部变量),它也起作用 - 而且它没有通过任何方法。 –

+1

该方法是一个副作用(为了更好的术语)。区别在于内存位置。局部变量在堆栈上创建,所以除非您告诉编译器将它们复制到堆中,否则它们不可访问。编译器在块内部提供数据时也使用了一些'技巧'(并且所采用的路由在指针和原语之间是不同的)。这是一个深入的主题,请阅读此主题:http://stackoverflow.com/questions/15082265/why-is-a-block-variable-is-moved-to-the-heap-before-the-块被复制 – Wain

+0

明白了。 Wain,你是男人!谢谢。 –

3

要允许一个变量在块内改变,使用_ 块存储类型修饰符,请参阅“_block Storage Type”。

__block NSString* imString = @"Imutable Hello"; 

参考由apple doc

以下规则适用于块中使用的变量:

  1. 全局变量是可访问的,包括en中存在的静态变量关闭词汇范围。
  2. 传递给块的参数是可访问的(就像函数的参数一样)。 将封闭词法作用域局部的堆栈(非静态)变量作为常量变量捕获。
  3. 它们的值取自程序中块表达式的位置。在嵌套块中,该值是从最近的封闭范围捕获的。

  4. 变量本地与__block存储修饰符声明封闭词法作用域通过引用提供等是可变的。

  5. 任何变化都反映在封闭词法作用域,包括相同的封闭词法范围内所定义的任何其他块。这些在“__block存储类型”中有更详细的讨论。

  6. 局部变量在块的词法范围内声明,其行为与函数中的局部变量完全相同。

  7. 块的每次调用都会提供该变量的新副本。这些变量可以反过来用作块内封闭块的const或by-reference变量。
+0

你忘了提及这是从https://developer.apple.com/library/ios/documentation/cocoa/Conceptual/Blocks/Articles/bxVariables.html#//apple_ref/doc/uid/的逐字拷贝TP40007502-CH6-SW2。 - 解释这如何适用于特定问题会更有帮助。 –

+0

声音很好... – codercat

+0

我阅读了这份文件......但仍然没有回答我的问题。这些规则中的哪一个使得UIView属性(不使用__block定义)可以在动画块中进行分配。我的原始变量是不是可分配的,UIView的基本属性是什么;为什么我不能将新对象赋给指针,除非我在声明中使用__block,并且UIView属性可以。 –

2

在第一个示例中,块“捕获”变量self - 这是一个指向保留对象的指针。你不修改本身在你的例子,当你写:

self.someProperty = someValue; 

自我的价值仍然是相同的 - 也就是说,它仍指向同一个对象。

你会修改例如如果你写:

self = nil; 
+0

好的......这个答案实际上导致了某个地方......所以我将一个对象传递给该块的事实使得它的属性可分配?我通过添加一个int属性和一个int ivar来测试它 - 我的类没有__block - ,并且我看到我可以在块中为它们分配一个新值。哪个原则/规则可以使属性/ ivars在块内分配? Block定义的方法中的局部变量不是? –

+1

@OhadRegev你可以调用一个method_的事实是,接收者的指针(here _self_)不会因为调用一个方法(实际上是指派一个属性)而改变。注意_self_是一个指针,它的值可能类似于'self equals 0xabcd1230' _before_调用一个方法,并且在调用方法之后它仍然是相同的:'self等于0xabcd1230'。 – CouchDeveloper

+1

@OhadRegev在处理块或lambdas时,捕获的变量(这里指针_self_的值为0xabcd1230)是_const_,这只是一个_common约定。它们也可以被修改。 – CouchDeveloper

相关问题