CKComponentViewAttributeValue CKComponentActionAttribute()

in ComponentKit/Core/Action/CKAction.mm [265:315]


CKComponentViewAttributeValue CKComponentActionAttribute(const CKAction<UIEvent *> action,
                                                         UIControlEvents controlEvents) noexcept
{
  if (!action) {
    return {
      {"CKComponentActionAttribute-no-op", ^(UIControl *control, id value) {}, ^(UIControl *control, id value) {}},
      // Use a bogus value for the attribute's "value". All the information is encoded in the attribute itself.
      @YES
    };
  }

  static ForwarderMap *map = new ForwarderMap(); // access on main thread only; never destructed to avoid static destruction fiasco
  return {
    {
      std::string("CKComponentActionAttribute-") + action.identifier() + "-" + std::to_string(controlEvents),
      ^(UIControl *control, id value){
        CKComponentActionList *list = RCGetAssociatedObject_MainThreadAffined(control, ck_actionListKey);
        if (list == nil) {
          list = [CKComponentActionList new];
          RCSetAssociatedObject_MainThreadAffined(control, ck_actionListKey, list);
        }
        if (list->_registeredForwarders.insert(controlEvents).second) {
          // Since this is the first time we've seen this {control, events} pair, add a Forwarder as a target.
          const auto it = map->find(controlEvents);
          CKComponentActionControlForwarder *const forwarder =
          (it == map->end())
          ? map->insert({controlEvents, [[CKComponentActionControlForwarder alloc] initWithControlEvents:controlEvents]}).first->second
          : it->second;
          [control addTarget:forwarder
                      action:@selector(handleControlEventFromSender:withEvent:)
            forControlEvents:controlEvents];
        }
        list->_actions[controlEvents].push_back(action);
      },
      ^(UIControl *control, id value){
        CKComponentActionList *const list = RCGetAssociatedObject_MainThreadAffined(control, ck_actionListKey);
        RCCAssertNotNil(list, @"Unapplicator should always find an action list installed by applicator");
        auto &actionList = list->_actions[controlEvents];
        auto it = CK::find(actionList, action);
        if (it == actionList.end()) {
          RCCFailAssert(@"Unapplicator should always find item in action list");
          return;
        }
        actionList.erase(it);
        // Don't bother unsetting the action list or removing the forwarder as a target; both are harmless.
      }
    },
    // Use a bogus value for the attribute's "value". All the information is encoded in the attribute itself.
    @YES
  };
}