New file |
| | |
| | | // |
| | | // YYCategoriesMacro.h |
| | | // YYCategories <https://github.com/ibireme/YYCategories> |
| | | // |
| | | // Created by ibireme on 13/3/29. |
| | | // Copyright (c) 2015 ibireme. |
| | | // |
| | | // This source code is licensed under the MIT-style license found in the |
| | | // LICENSE file in the root directory of this source tree. |
| | | // |
| | | |
| | | #import <UIKit/UIKit.h> |
| | | #import <sys/time.h> |
| | | #import <pthread.h> |
| | | |
| | | #ifndef YYCategoriesMacro_h |
| | | #define YYCategoriesMacro_h |
| | | |
| | | #ifdef __cplusplus |
| | | #define YY_EXTERN_C_BEGIN extern "C" { |
| | | #define YY_EXTERN_C_END } |
| | | #else |
| | | #define YY_EXTERN_C_BEGIN |
| | | #define YY_EXTERN_C_END |
| | | #endif |
| | | |
| | | |
| | | |
| | | YY_EXTERN_C_BEGIN |
| | | |
| | | #ifndef YY_CLAMP // return the clamped value |
| | | #define YY_CLAMP(_x_, _low_, _high_) (((_x_) > (_high_)) ? (_high_) : (((_x_) < (_low_)) ? (_low_) : (_x_))) |
| | | #endif |
| | | |
| | | #ifndef YY_SWAP // swap two value |
| | | #define YY_SWAP(_a_, _b_) do { __typeof__(_a_) _tmp_ = (_a_); (_a_) = (_b_); (_b_) = _tmp_; } while (0) |
| | | #endif |
| | | |
| | | |
| | | #define YYAssertNil(condition, description, ...) NSAssert(!(condition), (description), ##__VA_ARGS__) |
| | | #define YYCAssertNil(condition, description, ...) NSCAssert(!(condition), (description), ##__VA_ARGS__) |
| | | |
| | | #define YYAssertNotNil(condition, description, ...) NSAssert((condition), (description), ##__VA_ARGS__) |
| | | #define YYCAssertNotNil(condition, description, ...) NSCAssert((condition), (description), ##__VA_ARGS__) |
| | | |
| | | #define YYAssertMainThread() NSAssert([NSThread isMainThread], @"This method must be called on the main thread") |
| | | #define YYCAssertMainThread() NSCAssert([NSThread isMainThread], @"This method must be called on the main thread") |
| | | |
| | | |
| | | /** |
| | | Add this macro before each category implementation, so we don't have to use |
| | | -all_load or -force_load to load object files from static libraries that only |
| | | contain categories and no classes. |
| | | More info: http://developer.apple.com/library/mac/#qa/qa2006/qa1490.html . |
| | | ******************************************************************************* |
| | | Example: |
| | | YYSYNTH_DUMMY_CLASS(NSString_YYAdd) |
| | | */ |
| | | #ifndef YYSYNTH_DUMMY_CLASS |
| | | #define YYSYNTH_DUMMY_CLASS(_name_) \ |
| | | @interface YYSYNTH_DUMMY_CLASS_ ## _name_ : NSObject @end \ |
| | | @implementation YYSYNTH_DUMMY_CLASS_ ## _name_ @end |
| | | #endif |
| | | |
| | | |
| | | /** |
| | | Synthsize a dynamic object property in @implementation scope. |
| | | It allows us to add custom properties to existing classes in categories. |
| | | |
| | | @param association ASSIGN / RETAIN / COPY / RETAIN_NONATOMIC / COPY_NONATOMIC |
| | | @warning #import <objc/runtime.h> |
| | | ******************************************************************************* |
| | | Example: |
| | | @interface NSObject (MyAdd) |
| | | @property (nonatomic, retain) UIColor *myColor; |
| | | @end |
| | | |
| | | #import <objc/runtime.h> |
| | | @implementation NSObject (MyAdd) |
| | | YYSYNTH_DYNAMIC_PROPERTY_OBJECT(myColor, setMyColor, RETAIN, UIColor *) |
| | | @end |
| | | */ |
| | | #ifndef YYSYNTH_DYNAMIC_PROPERTY_OBJECT |
| | | #define YYSYNTH_DYNAMIC_PROPERTY_OBJECT(_getter_, _setter_, _association_, _type_) \ |
| | | - (void)_setter_ : (_type_)object { \ |
| | | [self willChangeValueForKey:@#_getter_]; \ |
| | | objc_setAssociatedObject(self, _cmd, object, OBJC_ASSOCIATION_ ## _association_); \ |
| | | [self didChangeValueForKey:@#_getter_]; \ |
| | | } \ |
| | | - (_type_)_getter_ { \ |
| | | return objc_getAssociatedObject(self, @selector(_setter_:)); \ |
| | | } |
| | | #endif |
| | | |
| | | |
| | | /** |
| | | Synthsize a dynamic c type property in @implementation scope. |
| | | It allows us to add custom properties to existing classes in categories. |
| | | |
| | | @warning #import <objc/runtime.h> |
| | | ******************************************************************************* |
| | | Example: |
| | | @interface NSObject (MyAdd) |
| | | @property (nonatomic, retain) CGPoint myPoint; |
| | | @end |
| | | |
| | | #import <objc/runtime.h> |
| | | @implementation NSObject (MyAdd) |
| | | YYSYNTH_DYNAMIC_PROPERTY_CTYPE(myPoint, setMyPoint, CGPoint) |
| | | @end |
| | | */ |
| | | #ifndef YYSYNTH_DYNAMIC_PROPERTY_CTYPE |
| | | #define YYSYNTH_DYNAMIC_PROPERTY_CTYPE(_getter_, _setter_, _type_) \ |
| | | - (void)_setter_ : (_type_)object { \ |
| | | [self willChangeValueForKey:@#_getter_]; \ |
| | | NSValue *value = [NSValue value:&object withObjCType:@encode(_type_)]; \ |
| | | objc_setAssociatedObject(self, _cmd, value, OBJC_ASSOCIATION_RETAIN); \ |
| | | [self didChangeValueForKey:@#_getter_]; \ |
| | | } \ |
| | | - (type)_getter_ { \ |
| | | _type_ cValue = { 0 }; \ |
| | | NSValue *value = objc_getAssociatedObject(self, @selector(_setter_:)); \ |
| | | [value getValue:&cValue]; \ |
| | | return cValue; \ |
| | | } |
| | | #endif |
| | | |
| | | /** |
| | | Synthsize a weak or strong reference. |
| | | |
| | | Example: |
| | | @weakify(self) |
| | | [self doSomething^{ |
| | | @strongify(self) |
| | | if (!self) return; |
| | | ... |
| | | }]; |
| | | |
| | | */ |
| | | #ifndef weakify |
| | | #if DEBUG |
| | | #if __has_feature(objc_arc) |
| | | #define weakify(object) autoreleasepool{} __weak __typeof__(object) weak##_##object = object; |
| | | #else |
| | | #define weakify(object) autoreleasepool{} __block __typeof__(object) block##_##object = object; |
| | | #endif |
| | | #else |
| | | #if __has_feature(objc_arc) |
| | | #define weakify(object) try{} @finally{} {} __weak __typeof__(object) weak##_##object = object; |
| | | #else |
| | | #define weakify(object) try{} @finally{} {} __block __typeof__(object) block##_##object = object; |
| | | #endif |
| | | #endif |
| | | #endif |
| | | |
| | | #ifndef strongify |
| | | #if DEBUG |
| | | #if __has_feature(objc_arc) |
| | | #define strongify(object) autoreleasepool{} __typeof__(object) object = weak##_##object; |
| | | #else |
| | | #define strongify(object) autoreleasepool{} __typeof__(object) object = block##_##object; |
| | | #endif |
| | | #else |
| | | #if __has_feature(objc_arc) |
| | | #define strongify(object) try{} @finally{} __typeof__(object) object = weak##_##object; |
| | | #else |
| | | #define strongify(object) try{} @finally{} __typeof__(object) object = block##_##object; |
| | | #endif |
| | | #endif |
| | | #endif |
| | | |
| | | |
| | | /** |
| | | Convert CFRange to NSRange |
| | | @param range CFRange @return NSRange |
| | | */ |
| | | static inline NSRange YYNSRangeFromCFRange(CFRange range) { |
| | | return NSMakeRange(range.location, range.length); |
| | | } |
| | | |
| | | /** |
| | | Convert NSRange to CFRange |
| | | @param range NSRange @return CFRange |
| | | */ |
| | | static inline CFRange YYCFRangeFromNSRange(NSRange range) { |
| | | return CFRangeMake(range.location, range.length); |
| | | } |
| | | |
| | | /** |
| | | Same as CFAutorelease(), compatible for iOS6 |
| | | @param arg CFObject @return same as input |
| | | */ |
| | | static inline CFTypeRef YYCFAutorelease(CFTypeRef CF_RELEASES_ARGUMENT arg) { |
| | | if (((long)CFAutorelease + 1) != 1) { |
| | | return CFAutorelease(arg); |
| | | } else { |
| | | id __autoreleasing obj = CFBridgingRelease(arg); |
| | | return (__bridge CFTypeRef)obj; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | Profile time cost. |
| | | @param ^block code to benchmark |
| | | @param ^complete code time cost (millisecond) |
| | | |
| | | Usage: |
| | | YYBenchmark(^{ |
| | | // code |
| | | }, ^(double ms) { |
| | | NSLog("time cost: %.2f ms",ms); |
| | | }); |
| | | |
| | | */ |
| | | static inline void YYBenchmark(void (^block)(void), void (^complete)(double ms)) { |
| | | // <QuartzCore/QuartzCore.h> version |
| | | /* |
| | | extern double CACurrentMediaTime (void); |
| | | double begin, end, ms; |
| | | begin = CACurrentMediaTime(); |
| | | block(); |
| | | end = CACurrentMediaTime(); |
| | | ms = (end - begin) * 1000.0; |
| | | complete(ms); |
| | | */ |
| | | |
| | | // <sys/time.h> version |
| | | struct timeval t0, t1; |
| | | gettimeofday(&t0, NULL); |
| | | block(); |
| | | gettimeofday(&t1, NULL); |
| | | double ms = (double)(t1.tv_sec - t0.tv_sec) * 1e3 + (double)(t1.tv_usec - t0.tv_usec) * 1e-3; |
| | | complete(ms); |
| | | } |
| | | |
| | | static inline NSDate *_YYCompileTime(const char *data, const char *time) { |
| | | NSString *timeStr = [NSString stringWithFormat:@"%s %s",data,time]; |
| | | NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]; |
| | | NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; |
| | | [formatter setDateFormat:@"MMM dd yyyy HH:mm:ss"]; |
| | | [formatter setLocale:locale]; |
| | | return [formatter dateFromString:timeStr]; |
| | | } |
| | | |
| | | /** |
| | | Get compile timestamp. |
| | | @return A new date object set to the compile date and time. |
| | | */ |
| | | #ifndef YYCompileTime |
| | | // use macro to avoid compile warning when use pch file |
| | | #define YYCompileTime() _YYCompileTime(__DATE__, __TIME__) |
| | | #endif |
| | | |
| | | /** |
| | | Returns a dispatch_time delay from now. |
| | | */ |
| | | static inline dispatch_time_t dispatch_time_delay(NSTimeInterval second) { |
| | | return dispatch_time(DISPATCH_TIME_NOW, (int64_t)(second * NSEC_PER_SEC)); |
| | | } |
| | | |
| | | /** |
| | | Returns a dispatch_wall_time delay from now. |
| | | */ |
| | | static inline dispatch_time_t dispatch_walltime_delay(NSTimeInterval second) { |
| | | return dispatch_walltime(DISPATCH_TIME_NOW, (int64_t)(second * NSEC_PER_SEC)); |
| | | } |
| | | |
| | | /** |
| | | Returns a dispatch_wall_time from NSDate. |
| | | */ |
| | | static inline dispatch_time_t dispatch_walltime_date(NSDate *date) { |
| | | NSTimeInterval interval; |
| | | double second, subsecond; |
| | | struct timespec time; |
| | | dispatch_time_t milestone; |
| | | |
| | | interval = [date timeIntervalSince1970]; |
| | | subsecond = modf(interval, &second); |
| | | time.tv_sec = second; |
| | | time.tv_nsec = subsecond * NSEC_PER_SEC; |
| | | milestone = dispatch_walltime(&time, 0); |
| | | return milestone; |
| | | } |
| | | |
| | | /** |
| | | Whether in main queue/thread. |
| | | */ |
| | | static inline bool dispatch_is_main_queue() { |
| | | return pthread_main_np() != 0; |
| | | } |
| | | |
| | | /** |
| | | Submits a block for asynchronous execution on a main queue and returns immediately. |
| | | */ |
| | | static inline void dispatch_async_on_main_queue(void (^block)()) { |
| | | if (pthread_main_np()) { |
| | | block(); |
| | | } else { |
| | | dispatch_async(dispatch_get_main_queue(), block); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | Submits a block for execution on a main queue and waits until the block completes. |
| | | */ |
| | | static inline void dispatch_sync_on_main_queue(void (^block)()) { |
| | | if (pthread_main_np()) { |
| | | block(); |
| | | } else { |
| | | dispatch_sync(dispatch_get_main_queue(), block); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | Initialize a pthread mutex. |
| | | */ |
| | | static inline void pthread_mutex_init_recursive(pthread_mutex_t *mutex, bool recursive) { |
| | | #define YYMUTEX_ASSERT_ON_ERROR(x_) do { \ |
| | | __unused volatile int res = (x_); \ |
| | | assert(res == 0); \ |
| | | } while (0) |
| | | assert(mutex != NULL); |
| | | if (!recursive) { |
| | | YYMUTEX_ASSERT_ON_ERROR(pthread_mutex_init(mutex, NULL)); |
| | | } else { |
| | | pthread_mutexattr_t attr; |
| | | YYMUTEX_ASSERT_ON_ERROR(pthread_mutexattr_init (&attr)); |
| | | YYMUTEX_ASSERT_ON_ERROR(pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE)); |
| | | YYMUTEX_ASSERT_ON_ERROR(pthread_mutex_init (mutex, &attr)); |
| | | YYMUTEX_ASSERT_ON_ERROR(pthread_mutexattr_destroy (&attr)); |
| | | } |
| | | #undef YYMUTEX_ASSERT_ON_ERROR |
| | | } |
| | | |
| | | |
| | | YY_EXTERN_C_END |
| | | #endif |