2

Long-time Swift dev here but haven't touched Objective-C in about ten years, so definitely rusty. Trying to figure out how to tell if a block parameter is escaping or not (i.e. so I know whether to use a weak ref or not as it isn't needed for non-escaping blocks.)

For instance, from what I've read, by default in Objective-C, block parameters are implicitly escaping (which is the opposite of Swift BTW.) If you don't want that, you have to use the NS_NOESCAPE annotation, like so...

- (void)executeActions:(void (NS_NOESCAPE ^)(void))actions;

However, the enumerateObjectsUsingBlock function I'm told is non-escaping, yet I don't see that annotation on the method in Apple's documentation. Actually, they don't talk about escaping or non-escaping at all. They just list the method.

Again, here's the documentation.

I then tried to search out the header file. I came across NSArray.h which does mention it, but there too I don't see NS_NOESCAPE.

https://github.com/.../NSArray.h (Line 79)

(I think this may be a generated/scraped page so maybe that information wouldn't be found here and you need the original headers. Not sure. Again, rusty.)

So how can one determine if a method's block parameters are escaping or not?

Mark A. Donohoe
  • 28,442
  • 25
  • 137
  • 286
  • Jump to the definition: [Shortcut for jumping to definition in Xcode 9?](https://stackoverflow.com/questions/44497436/ios-shortcut-for-jumping-to-definition-in-xcode-9/44923604) – Willeke Jul 23 '20 at 09:01
  • I think this will answer your question https://stackoverflow.com/questions/52879202/does-objective-c-have-an-equivalent-to-swifts-escaping-annotation – Shreeram Bhat Jul 23 '20 at 15:50
  • @Willeke, we're not in Xcode, nor can we be bc of the size of the product. Changes are done in VS Code, then CLI tools kick off a massive build. That's why I'm asking how to get the code info from the docs/source reference. Hopefully, that shouldn't require a tool. – Mark A. Donohoe Jul 23 '20 at 19:25
  • @ShreeramBhat, I already know it supports it. That's what the `NS_NOESCAPE` attribute stands for. What I'm asking is how to check if that attribute is actually set on a parameter because as I called out, it's not referenced in the documentation, and didn't appear in the reference source code I found online, yet I'm being told it *is* set. I'm trying to ask how I can confirm that. – Mark A. Donohoe Jul 23 '20 at 19:26
  • The documentation of `enumerateObjectsUsingBlock:` says "This method executes synchronously.". – Willeke Jul 23 '20 at 21:00
  • Yeah, I saw that. Is that enough to say that it's non-escaping? Still, why wasn't `NS_NOESCAPE` in the header file I linked to? – Mark A. Donohoe Jul 23 '20 at 21:02
  • 1
    Because it is "Generated by RuntimeBrowser"? – Willeke Jul 23 '20 at 21:10
  • I suspected as much but wasn't sure if a 'runtime browser' would capture such information. It's a shame that Apple doesn't speak about it more clearly. For instance if it is because of it being synchronous, then simply add '...and as such, only takes non-escaping blocks' that would've gone a long way to clearing this up. And to that point, if you put your original comment in an answer (about it being synchronous and therefore by definition, non-escaping), I'll mark it as accepted. – Mark A. Donohoe Jul 23 '20 at 21:11

2 Answers2

1

The documentation of enumerateObjectsUsingBlock: says

This method executes synchronously.

The documentation wasn't updated when NS_NOESCAPE was added.

Willeke
  • 14,578
  • 4
  • 19
  • 47
0

I don't think it matters any more ... the code below compiles and runs no problem, no matter how I try to break it with NS_NOESCAPE. That and it does not matter where you put it ...

- (NSOperationQueue *)queue
{
    if ( ! _queue )
    {
        _queue = [[NSOperationQueue alloc] init];
    }

    return _queue;
}

- ( void ) t1:( NS_NOESCAPE void(^)(NSString *) ) b
{
    [self.queue addOperationWithBlock:^{
        b(@"a");
    }];
    [self t2:b];
}

- ( void ) t2:( void(^)(NSString *) ) NS_NOESCAPE b
{
    [self.queue addOperationWithBlock:^{
        b(@"b");
    }];
    [self t3:b];
}

- ( void ) t3:( void(NS_NOESCAPE ^)(NSString *) ) b
{
    [self.queue addOperationWithBlock:^{
        b(@"c");
    }];
    [self escape:b];
}

- ( void ) t4:( void(^)(void) ) NS_NOESCAPE b
{
    [self.queue addOperationWithBlock:b];
}

- ( void ) escape:( void (^)( NSString * ) ) b
{
    b ( @"away" );
    [self.queue waitUntilAllOperationsAreFinished];
}

(I was hoping to test this on a previous version of the compiler to verify but do not have one handy.)

skaak
  • 2,988
  • 1
  • 8
  • 16
  • 1
    It's not that it matters (because in some cases it does), it's how do you know whether it's escaping or not based solely on the docs. I didn't see any reference to that (outside of the synchronous mention which someone else said *is* what's referring to it) so that's what I'm trying to seek clarification on. – Mark A. Donohoe Jul 23 '20 at 21:12
  • 1
    I get that - what I mean is it does not seem to even be implemented any more. My little test is not conclusive but together with the lack of any reference to it in the docs I think this no longer makes any difference under the hood. I was thinking maybe you can get away with strong pointers as I suppose that changed along with this but that is nothing more than a suspicion ... – skaak Jul 23 '20 at 21:17
  • If however that suspicion is true then this is no longer relevant ... so maybe that may be a way to test, use weak or strong pointers and see if the compiler complains about it differently for escaping and non-escaping blocks. – skaak Jul 23 '20 at 21:20
  • PS - that is what I tried initially with my tests but I could not even get the compiler to complain at all and then stopped there.... – skaak Jul 23 '20 at 21:21
  • Was `NS_NOESCAPE` ever implemented? Or was it added for Swift interoperability? – Willeke Jul 25 '20 at 07:56
  • From what I saw on the internet I *think* it was implemented at some stage in Objective-C but I *guess* at some stage ARC got clever enough to no longer require it. It seems the whole reason behind it is for Swift interoperability and as this was only an issue for earlier versions of Swift I think it is now just a left over tag with nothing behind it. – skaak Jul 25 '20 at 08:02
  • OP mentions that it (```NS_NOESCAPE```) does matter somewhere. I don't have an appetite to pursue this really given how moot it is but I was going to ask for some sample code next as I could not get my test to trigger anything ... – skaak Jul 25 '20 at 08:04
  • ... but I *think* if you try to compile my test with an old enough compiler it will error on those escaping blocks. – skaak Jul 25 '20 at 08:14