Xcode 12 Issues with protocol parameters in Objc

I've had a function that always worked, still works in older xcode versions (currently using 11.4) but breaks in 12.1.

I'm getting an error "Incompatible block pointer typers sending 'void(^)(ObjName *strong)' to parameter of type 'void (^)(strong id<ProtocolName>)'

ObjName is defined as:
Code Block
@interface ObjName : OtherObject <ProtocolName>

The function throwing the error is defined as:
Code Block
- (void)functionName:(BOOL)enabled
topic:(id <ProtocolName>)topic
 success:(void (^)(id <ProtocolName> topic))success
 failure:(void (^)(NSError *error))failure;


and I'm calling it with:
Code Block
[self functionName:NO topic:otherObj.topic
success:^(ObjName *obj) {
} failure:^(NSError *error) {
}];


The error is highlighting the start of
Code Block
^(ObjName *obj)


Any ideas what causes this?

I’m not able to reproduce the error you’re seeing. I took your code, fleshed it out, and put it in a new macOS > Command Line Tool target and it compiles just fine.

The full code is below.

Are you using Objective-C or Objective-C++?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"



Code Block
@import Foundation;
@protocol ProtocolName
@end
@interface OtherObject : NSObject
@end
@implementation OtherObject
@end
@interface ObjName : OtherObject <ProtocolName>
@end
@implementation ObjName
@end
@interface Main : NSObject
@end
@implementation Main
- (void)functionName:(BOOL)enabled
topic:(id <ProtocolName>)topic
success:(void (^)(id <ProtocolName> topic))success
failure:(void (^)(NSError *error))failure;
{
}
- (void)test {
ObjName * topic = [[ObjName alloc] init];
[self functionName:NO topic:topic success:^(ObjName *obj) {
#pragma unused(obj)
} failure:^(NSError *error) {
#pragma unused(error)
}];
}
@end
int main(int argc, char **argv) {
#pragma unused(argc)
#pragma unused(argv)
[[[Main alloc] init] test];
return EXIT_SUCCESS;
}

What you have looks right and runs for me too. This is objc. Here are the actual snippets in case I missed something, there are 3 instances of this being called that are showing the error message:

Code Block
-(void)setRemoteNotification:(BOOL)enabled
topic:(id <SNSObject>)topic
 success:(void (^)(id <SNSObject> topic))success
 failure:(void (^)(NSError *error))failure {


Code Block
[[SPSubscriptionManager sharedManager] setRemoteNotification:notificationOn topic:zone success:^(Zone *zone) {


Code Block
@interface Zone : MKPolygon <SNSObject>



What you have looks right and runs for me too.

OK. Can you edit my code to a state where it reproduces the error and then post that? Working through that process yields two benefits:
  • It’s common that the work you do to reduce a failing case will actually reveal the source of the problem.

  • If it doesn’t then the final result — a minimal program that you can share with others — is super useful in and of itself.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"
if you take the example you did but put it in an ios project in the ViewController instead of a command line target it seems to replicate the error for me

Code Block
#import "ViewController.h"
@interface ViewController ()
@end
@protocol ProtocolName
@end
@interface OtherObject : NSObject
@end
@implementation OtherObject
@end
@interface ObjName : OtherObject <ProtocolName>
@end
@implementation ObjName
@end
@interface Main : NSObject
@end
@implementation ViewController
- (void)viewDidLoad {
  [super viewDidLoad];
  // Do any additional setup after loading the view.
}
- (void)functionName:(BOOL)enabled
  topic:(id <ProtocolName>)topic
  success:(void (^)(id <ProtocolName> topic))success
  failure:(void (^)(NSError *error))failure;
{
}
- (void)test {
  ObjName * topic = [[ObjName alloc] init];
  [self functionName:NO topic:topic success:^(ObjName *obj) {
    #pragma unused(obj)
  } failure:^(NSError *error) {
    #pragma unused(error)
  }];
}
@end


Well isn’t that strange.

So, the error itself makes sense. The parameters to a block can’t be more specific than the block’s type. What you’re saying here is that the success block expects a parameter of type ObjName *, but the -functionName:topic:success:failure: method only guarantees to call it with a parameter of type id <ProtocolName>. Those types simply don’t line up. Based on its declaration the method could call success with an object that conforms to ProtocolName but isn’t an instance of ObjName. The fact that you know that it won’t do this is irrelevant to the type checker. Objective-C has no way for you to tell the type checker about that equivalence.

Note In Swift you can do this by declaring a generic parameter that must be used in both places. For example, func functionName<T>(enabled: Bool, topic: T, success: (T) -> Void, failure: (Error) -> Void) where T: ProtocolName. Objective-C’s lightweight generics aren’t up for that task, alas.

The weird thing is that the compiler doesn’t complain about this when building the code in a command-line tool target. I suspect that it’s being triggered by one of Objective-C’s many warning flags, but I’m not sure which one and I just don’t have time to grind through them all the context of DevForums. If you’d like more help with this I recommend that you open a DTS tech support incident.

ps If you’d like to know more about these type relationships read up on covariance and contravariance). One day I’ll be able to remember which is which (-:

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"
Xcode 12 Issues with protocol parameters in Objc
 
 
Q