// // YYKitMacro.h // YYKit // // 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 #import #import #ifndef YYKitMacro_h #define YYKitMacro_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 ******************************************************************************* Example: @interface NSObject (MyAdd) @property (nonatomic, retain) UIColor *myColor; @end #import @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 ******************************************************************************* Example: @interface NSObject (MyAdd) @property (nonatomic, retain) CGPoint myPoint; @end #import @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)) { // version /* extern double CACurrentMediaTime (void); double begin, end, ms; begin = CACurrentMediaTime(); block(); end = CACurrentMediaTime(); ms = (end - begin) * 1000.0; complete(ms); */ // 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