Search code examples
objective-cmultithreadingblock

How do I deal with arbitrary signatures in Objective-C blocks?


How do I refactor arbitrary blocks?

I have two functions with vastly different signatures that I would like to pass as a block so that I can ensure thread-safety, since setenv() and unsetenv() are not guaranteed to be thread-safe:

Old Code

-(X*)foo1
{
  X* x;
  @synchronized( self )
  {
    setenv( ... );
    x = worker_1( ... );
    unsetenv( ... );
  }
  return x;
}

-(Y*)foo2
{
  Y* y;
  @synchronized( self )
  {
    setenv( ... );
    y = worker_2( ... );
    unsetenv( ... );
  }
  return y;
}

Note that the two chunks of code are identical except that the signatures to worker_1() and worker_2() are vastly different.

In other words, I need to refactor the same wrappers, but completely different guts returning arbitrary objects.

New Code

- (void)aThreadSafeWrapper:my_block  // broken: needs fixing
{
    @synchronized( self )
    {
       setenv( ... );
       my_block();                   // broken: needs fixing
       unsetenv( ... );
    }
}

Is there any way I can accomplish this refactoring in Objective-C?


Solution

  • I don't know if I understand your question, but it looks like all you need is something like...

    - (void)performWithMySpecialEnvironment:(void(^)(void))block
    {
        @synchronized( self )
        {
           setenv( ... );
           block();
           unsetenv( ... );
        }
    }
    

    Then, you can do whatever you want in your block...

    [foo performWithMySpecialEnvironment:^{
        X x = worker_1( ... );
    }];
    
    [foo performWithMySpecialEnvironment:^{
        Y y = worker_2( ... );
    }];
    

    Note, that I do not endorse the @synchronized/setenv/unsetenv usage, but since that's what you are doing, and it's not part of the real question at hand, I'll leave it alone since it may just confuse the real "blocks" issue.

    EDIT

    +1 for the wrapper suggestion. Actually, if I change the block signature to return (void*) and do some really ugly casting, I can get this to work. If this ends up obfuscating, I may just have to copy-paste the wrapping code (yuck). I love ObjC, but this is where some functionality like C++ templates would come in handy. I will accept this answer if no better solution comes along. tyvm! – kfmfe04

    Apparently, you need the casting and return because you want to access the variable outside the block. That's easily remedied.

    __block X x;
    [foo performWithMySpecialEnvironment:^{
        x = worker_1( ... );
    }];