static void performSetter()

in RenderCore/View/CKComponentViewAttribute.mm [86:206]


static void performSetter(id object, SEL setter, id value)
{
  if ([value isKindOfClass:[NSValue class]]) {
    const CachedSetter &set = cachedSetterInvocation(object, setter);
    if (set.invocation) {
      if ([value isKindOfClass:[NSNumber class]]) {
        // We special case NSNumber because getting the correct byte width on both sides
        // is either hard (e.g. NSInteger), or impossible (e.g. CGFloat) on all architectures
        // simultaneously.
        // See https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
        // for more information on type encodings
        if (set.argumentType == nullptr || strlen(set.argumentType) != 1) {
          RCCAssert(NO, @"NSNumber: %@ cannot be used as an argument to a selector requiring '%s'; selector: %@; class %@",
                    value, set.argumentType ?: "NULL", NSStringFromSelector(setter), [object class]);
          return;
        }
        NSNumber *numValue = (NSNumber *)value;
        switch (*set.argumentType) {
          case 'c': {
            RCCAssertSizeOfEquals(char, set.argumentType, @"");
            char charValue = [numValue charValue];
            [set.invocation setArgument:&charValue atIndex:2];
            break;
          }
          case 'i': {
            RCCAssertSizeOfEquals(int, set.argumentType, @"");
            int intValue = [numValue intValue];
            [set.invocation setArgument:&intValue atIndex:2];
            break;
          }
          case 's': {
            RCCAssertSizeOfEquals(short, set.argumentType, @"");
            short shortValue = [numValue shortValue];
            [set.invocation setArgument:&shortValue atIndex:2];
            break;
          }
          case 'l': {
            // This is inconsistent, from the docs: "l is treated as a 32-bit quantity on 64-bit programs."
            RCCAssertSizeOfEquals(int32_t, set.argumentType, @"");
            int32_t longValue = [numValue intValue];
            [set.invocation setArgument:&longValue atIndex:2];
            break;
          }
          case 'q': {
            RCCAssertSizeOfEquals(long long, set.argumentType, @"");
            long long longLongValue = [numValue longLongValue];
            [set.invocation setArgument:&longLongValue atIndex:2];
            break;
          }
          case 'C': {
            RCCAssertSizeOfEquals(unsigned char, set.argumentType, @"");
            unsigned char uCharValue = [numValue unsignedCharValue];
            [set.invocation setArgument:&uCharValue atIndex:2];
            break;
          }
          case 'I': {
            RCCAssertSizeOfEquals(unsigned int, set.argumentType, @"");
            unsigned int uIntValue = [numValue unsignedIntValue];
            [set.invocation setArgument:&uIntValue atIndex:2];
            break;
          }
          case 'S': {
            RCCAssertSizeOfEquals(unsigned short, set.argumentType, @"");
            unsigned short uShortValue = [numValue unsignedShortValue];
            [set.invocation setArgument:&uShortValue atIndex:2];
            break;
          }
          case 'L': {
            // This is also inconsistent, and undocumented
            RCCAssertSizeOfEquals(uint32_t, set.argumentType, @"");
            uint32_t uLongValue = [numValue unsignedIntValue];
            [set.invocation setArgument:&uLongValue atIndex:2];
            break;
          }
          case 'Q': {
            RCCAssertSizeOfEquals(unsigned long long, set.argumentType, @"");
            unsigned long long uLongLongValue = [numValue unsignedLongLongValue];
            [set.invocation setArgument:&uLongLongValue atIndex:2];
            break;
          }
          case 'f': {
            RCCAssertSizeOfEquals(float, set.argumentType, @"");
            float floatValue = [numValue floatValue];
            [set.invocation setArgument:&floatValue atIndex:2];
            break;
          }
          case 'd': {
            RCCAssertSizeOfEquals(double, set.argumentType, @"");
            double doubleValue = [numValue doubleValue];
            [set.invocation setArgument:&doubleValue atIndex:2];
            break;
          }
          case 'B': {
            RCCAssertSizeOfEquals(BOOL, set.argumentType, @"");
            BOOL boolValue = [numValue boolValue];
            [set.invocation setArgument:&boolValue atIndex:2];
            break;
          }
          default:
            // This should just be: 'v', '*', '@', '#', ':', '?', none of which should be boxed as NSNumber
            RCCAssert(NO, @"NSNumber: %@ cannot be used as an argument to a selector requiring '%s'", value, set.argumentType);
            return;
        }
      } else {
        char buf[set.argumentSize];
        [value getValue:buf];
        [set.invocation setArgument:buf atIndex:2];
      }
      [set.invocation setSelector:setter];
      [set.invocation invokeWithTarget:object];
      return;
    }
  }

  // ARC is worried that the selector might have a return value it doesn't know about, or be annotated with ns_consumed.
  // Neither is typically the case for setters, so ignore the warning.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
  [object performSelector:setter withObject:value];
#pragma clang diagnostic pop
}