【问题标题】:Objective-C define block after passing it to a method将Objective-C定义块传递给方法后
【发布时间】:2021-07-13 17:15:14
【问题描述】:

是否可以在将块传递给方法后定义它?我想这样做,所以代码在某种程度上是它运行的顺序:

// Declare the block
void (^doStuffBlock)(void);

// Pass the block.
[self prepareToDoStuffWithCompletion:doStuffBlock];

// Define the block.
doStuffBlock = ^void() {
  // Do stuff
};

不起作用,因为在 prepareToDoStuffWithCompletion: 内部块 doStuffBlock 为零。

【问题讨论】:

    标签: objective-c objective-c-blocks


    【解决方案1】:

    你应该先定义块然后将它传递给方法:

    // Declare the block
    void (^doStuffBlock)(void);
    
    // Define the block.
    doStuffBlock= ^void() {
      // Do stuff
    };
    
    // Pass the block.
    [self prepareToDoStuffWithCompletion:doStuffBlock];
    

    【讨论】:

    • 我是这样理解的,我问的是在调用prepareToDoStuffWithCompletion:doStuffBlock之后是否可以定义doStuffBlock
    【解决方案2】:

    你可以使用 typedef。

    typedef void (^TypeName)(void);
    - (void)bar:(TypeName)completion {
       completion();
    }
    
    TypeName foo = ^() { /*...*/ };
    
    [self bar:foo]; 
    
    

    (我的 obj-c 语法可能有点生疏,但你想做的事情在 Objective-C 和 Swift 中都是可能的。

    https://stackoverflow.com/a/29580490/620197

    http://goshdarnblocksyntax.com/

    【讨论】:

    • 我想在传递块后定义块,即这条线 TypeName foo = ^() { /*...*/ }; 以某种方式跟随这条线 [self bar:foo];
    【解决方案3】:

    如果您提供完成处理程序闭包,您实际上是在说“这是我希望您使用的闭包”。

    如果您稍后要提供闭包,您可能会定义一个属性:

    @property (nonatomic, copy, nullable) void (^doStuff)(void);
    

    然后,在调用方法时提供闭包,而是引用这个属性:

    - (void)prepareToDoStuff {
        [self somethingAsynchronousWithCompletion:^{
            if (self.doStuff) {
                self.doStuff();
    
                // if completion handler, you’d often release it when done, e.g. 
                //
                // self.doStuff = nil;
            }
        }];
    }
    

    然后,您可以稍后调用此方法并提供闭包:

    [self prepareToDoStuff];
    
    self.doStuff = ^{
        NSLog(@"do stuff done");
    };
    

    一些额外的注意事项:

    1. 确保您同步访问此doStuff 属性。例如,在上面,我假设 somethingAsynchronousWithCompletion 在主线程上调用它的完成处理程序。如果没有,请同步您的访问(就像您在多线程环境中处理任何非线程安全的属性一样)。

    2. 如果您首先调用最终将调用块的方法,然后才设置该块属性,则存在逻辑竞争。有时这很好(例如,也许您只是想指定异步过程完成时要更新的 UI)。其他时候,比赛可能会咬你。这取决于块属性的功能意图。

    3. 我会给块属性一个更能反映其功能用途的名称(例如completionHandlernotificationHandlerdidReceiveValue 或其他)。

    【讨论】:

      【解决方案4】:

      如果您确定该方法将在您“定义”它之后运行您的doStuffBlock,您可以做的是让您的doStuffBlock 捕获一个包含第二个块的变量,其中包含它应该做什么的真实逻辑。你可以在创建doStuffBlock之后设置实逻辑块,并且你需要确保保存实逻辑块的变量是__block变量,以便在函数范围内看到对变量的更改块范围。

      __block void (^realLogicBlock)(void);
      
      [self prepareToDoStuffWithCompletion:^{
        if (realLogicBlock)
          realLogicBlock();
      }];
      
      realLogicBlock = ^void() {
        // Do stuff
      };
      

      您可能必须小心保留周期 - 如果在 realLogicBlock 内,您捕获对 self 或将引用 prepareToDoStuffWithCompletion: 完成处理程序的引用,您将有一个保留周期,在在这种情况下,您可能必须在某处引入弱引用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-09-27
        相关资源
        最近更新 更多