In-place optimizations free memory too early in ObjC library with fluent style


Suppose that you have a library that gives you convenient blocks to set parameters.

let q = Query().first(a)?.second(b)?.third(c)

It looks really nice and native in even ObjC code. ( without questions marks, hah ).


I have encountered with following bug:

let q = Query().first(a)?.second(b)?.third(c) // return nil
let p = Query()
p.first(a)?.second(b)?.third(c) // correct setup of `Query` instance

Here, q is assigned the result of the last call of the chain, third(c). If first or second returns nil, third is never called and the assigned result is nil too. It's equivalent and should behave similarly to this in Objective-C:

q = [[[[[Query alloc] init] first:a] second:b] third:c];

If any of these methods returns nil, q will end up being assigned nil.

Here you're assigning to p the result of Query(), so p will never be nil. Then you are calling the various methods which aren't going to change the value in p.

If something is wrong, you'll need to provide a better example of the involved Objective-C and equivalent Swift code.

Final note: this thread would be more appropriate in the Using Swift section of the forum. Or as a bug report if you believe this is a bug. Perhaps a moderator can move it.

1 Like

I emphasize that values in both styles are the same: a, b, c

None of these block-setters returns nil. They always return self.

It is a lack of bridging. These blocks have types in swift: (a: A?) -> Query!

Ah, my bad. So those are blocks inside of properties, not methods.

Objective-C is more lenient than Swift about the lifetime of objects, on purpose. If you are returning a block and the block does not retain self, after the block is returned the Query is discarded before calling the block, whereas Objective-C will postpone the cleanup until a little later.

What you need to do (I think) is on the Objective-C side: implement the property so it actually retains the block (allowing it to escape safely the context of the current function):

- (^Query(a A))first {
     // copy retains the captured variables, allowing the block to escape safely
     return [^(a A){ return self } copy];


// Query.h
@property (copy, nonatomic, readwrite) Query *(^first)(A a);


// Query.m
- (void)setup {
  __weak __auto_type weakSelf = self;
  self.first = ^(A a){ // setter will set `[value copy]`, right?
    return [weakSelf first:a]; // method which set a and return self.

I thought that setter self.first will copy block.

Ah, well, that'll never work with a weak self unfortunatly. (And you need a weak self to avoid a retain cycle since you're assigning the block to a property of self).

But since you already have methods with the right name on this object, you should use them directly in Swift. Simply exclude the block property declarations so they're not imported in Swift:

// Avoid importing block properties in Swift
@property (copy, nonatomic, readwrite) Query *(^first)(A a);
// <insert other block property declarations here>

And now you'll be able to call the methods directly, unencumbered by the block properties of the same name.

1 Like

Thank you for thorough answer!

I have added library as dependency to swift project and embrace categories with blocks into this condition, but clang ignores it and provides these methods as before.
If I comment out these categories, everything works as expected ( clang can't compile commented code, heh ).
Also I have tried to check this variable in info.plist $(DEPLOYMENT_RUNTIME_SWIFT) which results into empty string in log.

By the way, I check it on pretty old project with Swift 4.