in ComponentKit/Core/Action/CKAction.mm [360:442]
BOOL checkMethodSignatureAgainstTypeEncodings(SEL selector, Method method, const std::vector<const char *> &typeEncodings)
{
if (selector == NULL) {
return NO;
}
if (typeEncodings.size() + 3 < method_getNumberOfArguments(method)) {
RCCFailAssert(@"Expected action method %@ to take less than %llu arguments, but it supports %llu", NSStringFromSelector(selector), (unsigned long long)typeEncodings.size(), (unsigned long long)method_getNumberOfArguments(method) - 3);
return NO;
}
char *return_type = method_copyReturnType(method);
if (return_type == NULL) {
return NO;
}
const bool has_return_type = strcmp(return_type, "v") != 0; // "v" is void
free(return_type);
if (has_return_type) {
RCCFailAssert(@"Component action methods should not have any return value. Any objects returned from this method will be leaked.");
return NO;
}
// Skipping self, _cmd, and sender (the component).
for (int i = 0; i + 3 < method_getNumberOfArguments(method) && i < typeEncodings.size(); i++) {
char *cp_argType = method_copyArgumentType(method, i + 3); // freed later - DON'T early exit!
char *methodEncoding = cp_argType; // a pointer we can move around
const char *typeEncoding = typeEncodings[i];
// Type Encoding: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
// ref types get '^' prefixed to them. Since C++ would implicitly
// use pass-by-ref or pass-by-value based the called function, we
// treat a mismatch between ref & copy as valid.
if (methodEncoding != NULL && *methodEncoding == '^') {
methodEncoding++;
}
if (typeEncoding != NULL && *typeEncoding == '^') {
typeEncoding++;
}
BOOL doEncodingsMatch = NO;
if (methodEncoding == NULL || typeEncoding == NULL) {
// nothing to compare
doEncodingsMatch = YES;
} else if (*methodEncoding == '{' && *typeEncoding == '{') {
// types are structures. Due to an issue with c++ types not always being
// encoded the same even thought they are basically the same, we only
// compare the structure name. (see T23131874)
const char *nameEnd = strchr(methodEncoding, '=');
const size_t nameSize = nameEnd - methodEncoding;
doEncodingsMatch =
(nameEnd
&& strlen(typeEncoding) >= nameSize
&& strncmp(methodEncoding, typeEncoding, nameSize) == 0);
} else {
doEncodingsMatch = strcmp(methodEncoding, typeEncoding) == 0;
}
NSString *safe_methodEncoding = [NSString stringWithFormat:@"%s", methodEncoding];
free(cp_argType);
if (!doEncodingsMatch) {
RCCFailAssert(@"Implementation of %@ does not match expected types.\nExpected type %s, got %@", NSStringFromSelector(selector), typeEncoding, safe_methodEncoding);
return NO;
}
safe_methodEncoding = nil; // avoids -Wunused-variable
}
if (method_getNumberOfArguments(method) >= 3) {
char *const unasfe_methodEncoding = method_copyArgumentType(method, 2);
NSString *methodEncoding = [NSString stringWithFormat:@"%s", unasfe_methodEncoding ?: ""];
free(unasfe_methodEncoding);
if (methodEncoding != nil && [methodEncoding isEqualToString:@"@"] == NO) {
RCCFailAssert(@"Sender of %@ is not an object.\nGot %@ instead. Please add the component as the first argument when sending an action", NSStringFromSelector(selector), methodEncoding);
return NO;
}
}
return YES;
}