|
| 1 | +/* |
| 2 | + * This file is part of the SDWebImage package. |
| 3 | + * (c) Olivier Poitrey <[email protected]> |
| 4 | + * |
| 5 | + * For the full copyright and license information, please view the LICENSE |
| 6 | + * file that was distributed with this source code. |
| 7 | + */ |
| 8 | + |
| 9 | +#import <Foundation/Foundation.h> |
| 10 | +#import <os/lock.h> |
| 11 | +#import <libkern/OSAtomic.h> |
| 12 | +#import "SDmetamacros.h" |
| 13 | + |
| 14 | +#define SD_USE_OS_UNFAIR_LOCK TARGET_OS_MACCATALYST ||\ |
| 15 | + (__IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_10_0) ||\ |
| 16 | + (__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_12) ||\ |
| 17 | + (__TV_OS_VERSION_MIN_REQUIRED >= __TVOS_10_0) ||\ |
| 18 | + (__WATCH_OS_VERSION_MIN_REQUIRED >= __WATCHOS_3_0) |
| 19 | + |
| 20 | +#ifndef SD_LOCK_DECLARE |
| 21 | +#if SD_USE_OS_UNFAIR_LOCK |
| 22 | +#define SD_LOCK_DECLARE(lock) os_unfair_lock lock |
| 23 | +#else |
| 24 | +#define SD_LOCK_DECLARE(lock) os_unfair_lock lock API_AVAILABLE(ios(10.0), tvos(10), watchos(3), macos(10.12)); \ |
| 25 | +OSSpinLock lock##_deprecated; |
| 26 | +#endif |
| 27 | +#endif |
| 28 | + |
| 29 | +#ifndef SD_LOCK_DECLARE_STATIC |
| 30 | +#if SD_USE_OS_UNFAIR_LOCK |
| 31 | +#define SD_LOCK_DECLARE_STATIC(lock) static os_unfair_lock lock |
| 32 | +#else |
| 33 | +#define SD_LOCK_DECLARE_STATIC(lock) static os_unfair_lock lock API_AVAILABLE(ios(10.0), tvos(10), watchos(3), macos(10.12)); \ |
| 34 | +static OSSpinLock lock##_deprecated; |
| 35 | +#endif |
| 36 | +#endif |
| 37 | + |
| 38 | +#ifndef SD_LOCK_INIT |
| 39 | +#if SD_USE_OS_UNFAIR_LOCK |
| 40 | +#define SD_LOCK_INIT(lock) lock = OS_UNFAIR_LOCK_INIT |
| 41 | +#else |
| 42 | +#define SD_LOCK_INIT(lock) if (@available(iOS 10, tvOS 10, watchOS 3, macOS 10.12, *)) lock = OS_UNFAIR_LOCK_INIT; \ |
| 43 | +else lock##_deprecated = OS_SPINLOCK_INIT; |
| 44 | +#endif |
| 45 | +#endif |
| 46 | + |
| 47 | +#ifndef SD_LOCK |
| 48 | +#if SD_USE_OS_UNFAIR_LOCK |
| 49 | +#define SD_LOCK(lock) os_unfair_lock_lock(&lock) |
| 50 | +#else |
| 51 | +#define SD_LOCK(lock) if (@available(iOS 10, tvOS 10, watchOS 3, macOS 10.12, *)) os_unfair_lock_lock(&lock); \ |
| 52 | +else OSSpinLockLock(&lock##_deprecated); |
| 53 | +#endif |
| 54 | +#endif |
| 55 | + |
| 56 | +#ifndef SD_UNLOCK |
| 57 | +#if SD_USE_OS_UNFAIR_LOCK |
| 58 | +#define SD_UNLOCK(lock) os_unfair_lock_unlock(&lock) |
| 59 | +#else |
| 60 | +#define SD_UNLOCK(lock) if (@available(iOS 10, tvOS 10, watchOS 3, macOS 10.12, *)) os_unfair_lock_unlock(&lock); \ |
| 61 | +else OSSpinLockUnlock(&lock##_deprecated); |
| 62 | +#endif |
| 63 | +#endif |
| 64 | + |
| 65 | +#ifndef SD_OPTIONS_CONTAINS |
| 66 | +#define SD_OPTIONS_CONTAINS(options, value) (((options) & (value)) == (value)) |
| 67 | +#endif |
| 68 | + |
| 69 | +#ifndef SD_CSTRING |
| 70 | +#define SD_CSTRING(str) #str |
| 71 | +#endif |
| 72 | + |
| 73 | +#ifndef SD_NSSTRING |
| 74 | +#define SD_NSSTRING(str) @(SD_CSTRING(str)) |
| 75 | +#endif |
| 76 | + |
| 77 | +#ifndef SD_SEL_SPI |
| 78 | +#define SD_SEL_SPI(name) NSSelectorFromString([NSString stringWithFormat:@"_%@", SD_NSSTRING(name)]) |
| 79 | +#endif |
| 80 | + |
| 81 | +#ifndef weakify |
| 82 | +#define weakify(...) \ |
| 83 | +sd_keywordify \ |
| 84 | +metamacro_foreach_cxt(sd_weakify_,, __weak, __VA_ARGS__) |
| 85 | +#endif |
| 86 | + |
| 87 | +#ifndef strongify |
| 88 | +#define strongify(...) \ |
| 89 | +sd_keywordify \ |
| 90 | +_Pragma("clang diagnostic push") \ |
| 91 | +_Pragma("clang diagnostic ignored \"-Wshadow\"") \ |
| 92 | +metamacro_foreach(sd_strongify_,, __VA_ARGS__) \ |
| 93 | +_Pragma("clang diagnostic pop") |
| 94 | +#endif |
| 95 | + |
| 96 | +#define sd_weakify_(INDEX, CONTEXT, VAR) \ |
| 97 | +CONTEXT __typeof__(VAR) metamacro_concat(VAR, _weak_) = (VAR); |
| 98 | + |
| 99 | +#define sd_strongify_(INDEX, VAR) \ |
| 100 | +__strong __typeof__(VAR) VAR = metamacro_concat(VAR, _weak_); |
| 101 | + |
| 102 | +#if DEBUG |
| 103 | +#define sd_keywordify autoreleasepool {} |
| 104 | +#else |
| 105 | +#define sd_keywordify try {} @catch (...) {} |
| 106 | +#endif |
| 107 | + |
| 108 | +#ifndef onExit |
| 109 | +#define onExit \ |
| 110 | +sd_keywordify \ |
| 111 | +__strong sd_cleanupBlock_t metamacro_concat(sd_exitBlock_, __LINE__) __attribute__((cleanup(sd_executeCleanupBlock), unused)) = ^ |
| 112 | +#endif |
| 113 | + |
| 114 | +typedef void (^sd_cleanupBlock_t)(void); |
| 115 | + |
| 116 | +#if defined(__cplusplus) |
| 117 | +extern "C" { |
| 118 | +#endif |
| 119 | + void sd_executeCleanupBlock (__strong sd_cleanupBlock_t *block); |
| 120 | +#if defined(__cplusplus) |
| 121 | +} |
| 122 | +#endif |
| 123 | + |
| 124 | +/** |
| 125 | + * \@keypath allows compile-time verification of key paths. Given a real object |
| 126 | + * receiver and key path: |
| 127 | + * |
| 128 | + * @code |
| 129 | +
|
| 130 | +NSString *UTF8StringPath = @keypath(str.lowercaseString.UTF8String); |
| 131 | +// => @"lowercaseString.UTF8String" |
| 132 | +
|
| 133 | +NSString *versionPath = @keypath(NSObject, version); |
| 134 | +// => @"version" |
| 135 | +
|
| 136 | +NSString *lowercaseStringPath = @keypath(NSString.new, lowercaseString); |
| 137 | +// => @"lowercaseString" |
| 138 | +
|
| 139 | + * @endcode |
| 140 | + * |
| 141 | + * ... the macro returns an \c NSString containing all but the first path |
| 142 | + * component or argument (e.g., @"lowercaseString.UTF8String", @"version"). |
| 143 | + * |
| 144 | + * In addition to simply creating a key path, this macro ensures that the key |
| 145 | + * path is valid at compile-time (causing a syntax error if not), and supports |
| 146 | + * refactoring, such that changing the name of the property will also update any |
| 147 | + * uses of \@keypath. |
| 148 | + */ |
| 149 | +#define keypath(...) \ |
| 150 | + _Pragma("clang diagnostic push") \ |
| 151 | + _Pragma("clang diagnostic ignored \"-Warc-repeated-use-of-weak\"") \ |
| 152 | + (NO).boolValue ? ((NSString * _Nonnull)nil) : ((NSString * _Nonnull)@(cStringKeypath(__VA_ARGS__))) \ |
| 153 | + _Pragma("clang diagnostic pop") \ |
| 154 | + |
| 155 | +#define cStringKeypath(...) \ |
| 156 | + metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__))(keypath1(__VA_ARGS__))(keypath2(__VA_ARGS__)) |
| 157 | + |
| 158 | +#define keypath1(PATH) \ |
| 159 | + (((void)(NO && ((void)PATH, NO)), \ |
| 160 | + ({ char *__extobjckeypath__ = strchr(# PATH, '.'); NSCAssert(__extobjckeypath__, @"Provided key path is invalid."); __extobjckeypath__ + 1; }))) |
| 161 | + |
| 162 | +#define keypath2(OBJ, PATH) \ |
| 163 | + (((void)(NO && ((void)OBJ.PATH, NO)), # PATH)) |
| 164 | + |
| 165 | +/** |
| 166 | + * \@collectionKeypath allows compile-time verification of key paths across collections NSArray/NSSet etc. Given a real object |
| 167 | + * receiver, collection object receiver and related keypaths: |
| 168 | + * |
| 169 | + * @code |
| 170 | + |
| 171 | + NSString *employeesFirstNamePath = @collectionKeypath(department.employees, Employee.new, firstName) |
| 172 | + // => @"employees.firstName" |
| 173 | + |
| 174 | + NSString *employeesFirstNamePath = @collectionKeypath(Department.new, employees, Employee.new, firstName) |
| 175 | + // => @"employees.firstName" |
| 176 | +
|
| 177 | + * @endcode |
| 178 | + * |
| 179 | + */ |
| 180 | +#define collectionKeypath(...) \ |
| 181 | + metamacro_if_eq(3, metamacro_argcount(__VA_ARGS__))(collectionKeypath3(__VA_ARGS__))(collectionKeypath4(__VA_ARGS__)) |
| 182 | + |
| 183 | +#define collectionKeypath3(PATH, COLLECTION_OBJECT, COLLECTION_PATH) \ |
| 184 | + (YES).boolValue ? (NSString * _Nonnull)@((const char * _Nonnull)[[NSString stringWithFormat:@"%s.%s", cStringKeypath(PATH), cStringKeypath(COLLECTION_OBJECT, COLLECTION_PATH)] UTF8String]) : (NSString * _Nonnull)nil |
| 185 | + |
| 186 | +#define collectionKeypath4(OBJ, PATH, COLLECTION_OBJECT, COLLECTION_PATH) \ |
| 187 | + (YES).boolValue ? (NSString * _Nonnull)@((const char * _Nonnull)[[NSString stringWithFormat:@"%s.%s", cStringKeypath(OBJ, PATH), cStringKeypath(COLLECTION_OBJECT, COLLECTION_PATH)] UTF8String]) : (NSString * _Nonnull)nil |
0 commit comments