// // ASPropertyMacros.h // AsyncDisplayKit // // Created by Adlai Holler on 5/19/19. // Copyright © 2019 Pinterest. All rights reserved. // #import #import #import #include namespace AS { /** * Convenience function to wrap a msg-send in a C++ function. An example use * is to invoke `setNeedsDisplay`. The method must take no argument * and return no value. */ template NS_INLINE std::function ObjCCall(SEL selector) { return [selector](id self, __unused const T& old_value) { auto castMsgSend = (void(*)(id object, SEL s))objc_msgSend; castMsgSend(self, selector); }; } NS_INLINE id ObjCCopy(unowned id object) { return [object copyWithZone:nullptr]; } template NS_INLINE T ObjCGetter(Mutex &mutex, std::function get) { mutex.lock(); T result = get(); mutex.unlock(); return result; } /** * The primitive function for locked setters. * @param self The object the setter is being called on. * @param mutex The mutex to lock for the ivar. * @param getter The * @param value The new property value. * @param is_equal Optional custom comparison. * @param copy Optional custom copy. * @param release Optional custom release for old value. * @param side_effect Optional function to call before unlocking. */ template NS_INLINE bool ObjCSetter(unowned id self, Mutex &mutex, std::function getter, std::function setter, const T &value, std::function is_equal, std::function copy, std::function release, std::function side_effect) { mutex.lock(); const T old_value = getter(); const bool bail = is_equal ? is_equal(old_value, value) : std::equal_to()(old_value, value); if (bail) { mutex.unlock(); return false; } setter(copy ? copy(value) : value); if (side_effect) { side_effect(self, old_value); } if (release) { release(old_value); } mutex.unlock(); return true; } } #define AS_GETTER(lowerName, mutex, type, expr) \ - (type)lowerName { \ return AS::ObjCGetter(mutex, [&self]{ return expr; }); \ } #define AS_SETTER(upperName, lock, type, expr, isEqual, copy, release, sideEffect) \ - (void)set##upperName:(type)newValue { \ AS::ObjCSetter(self, lock, [&self]{ return expr; }, [&self](type newValue){ expr = newValue; }, newValue, isEqual, copy, release, sideEffect); \ } #define AS_SETTER_PLUS_METHOD(upperName, lock, type, expr, isEqual, copy, release, method) \ AS_SETTER(upperName, lock, type, expr, isEqual, copy, release, AS::ObjCCall(@selector(method))) #define AS_BASIC_SETTER(upperName, lock, type, expr) \ AS_SETTER(upperName, lock, type, expr, std::equal_to(), nullptr, nullptr, nullptr)