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.

337 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