You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
336 lines
10 KiB
336 lines
10 KiB
// |
|
// YYKitMacro.h |
|
// YYKit <https://github.com/ibireme/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 <UIKit/UIKit.h> |
|
#import <sys/time.h> |
|
#import <pthread.h> |
|
|
|
#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 <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
|
|
|