第一行,是C++的 using
命令,这里是使用AS命名空间下的MutexLocker
using AS::MutexLocker;
ASLayoutElementContext
的初始化方法,相应的参数都用参数名字来表示,十分好理解
#pragma mark - ASLayoutElementContext
- (instancetype)init
{
if (self = [super init]) {
_transitionID = ASLayoutElementContextDefaultTransitionID;
}
return self;
}
第一个定义的常量,默认设置为NAN
,一种特殊的浮点类型
CGFloat const ASLayoutElementParentDimensionUndefined = NAN;
其他常量定义看起来都是十分清晰的,用命名代表数值
CGSize const ASLayoutElementParentSizeUndefined = {ASLayoutElementParentDimensionUndefined, ASLayoutElementParentDimensionUndefined};
int32_t const ASLayoutElementContextInvalidTransitionID = 0;
int32_t const ASLayoutElementContextDefaultTransitionID = ASLayoutElementContextInvalidTransitionID + 1;
本地线程,这里用了__unsafe_unretained
有个疑问,为什么不用__weak
?
#ifdef __i386__
#define AS_TLS_AVAILABLE 0
#else
#define AS_TLS_AVAILABLE 1
这里分成两种模式来进行定义,i386和非i386
#if AS_TLS_AVAILABLE
static _Thread_local __unsafe_unretained ASLayoutElementContext *tls_context;
void ASLayoutElementPushContext(ASLayoutElementContext *context)
{
// NOTE: It would be easy to support nested contexts – just use an NSMutableArray here.
ASDisplayNodeCAssertNil(tls_context, @"Nested ASLayoutElementContexts aren't supported.");
tls_context = (__bridge ASLayoutElementContext *)(__bridge_retained CFTypeRef)context;
}
这里用了ASDisplayNodeCAssertNil
的宏命令
定义如下,内部调用了NSException
的方法,##__VA_ARGS__
的作用是省略掉多余的参数
#define ASDisplayNodeCAssert(condition, desc, ...) NSCAssert(condition, desc, ##__VA_ARGS__)
#define ASDisplayNodeCAssertNil(condition, desc, ...) ASDisplayNodeCAssert((condition) == nil, desc, ##__VA_ARGS__)
这里定义了一个获取当前Context的函数,让外部可以通过该方法拿到本地线程?
ASLayoutElementContext *ASLayoutElementGetCurrentContext()
{
// Don't retain here. Caller will retain if it wants to!
return tls_context;
}
释放本地线程方法
void ASLayoutElementPopContext()
{
ASDisplayNodeCAssertNotNil(tls_context, @"Attempt to pop context when there wasn't a context!");
CFRelease((__bridge CFTypeRef)tls_context);
tls_context = nil;
}
#else
静态方法,获取线程的key,里面使用了单例模式
static pthread_key_t ASLayoutElementContextKey() {
static pthread_key_t k;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
pthread_key_create(&k, NULL);
});
return k;
}
这里用到了线程储存方法pthread_getspecific
,pthread_setspecific
用到了const auto
命令,拷贝变量,但不允许修改,auto
表示可以推断类型
void ASLayoutElementPushContext(ASLayoutElementContext *context)
{
// NOTE: It would be easy to support nested contexts – just use an NSMutableArray here.
ASDisplayNodeCAssertNil(pthread_getspecific(ASLayoutElementContextKey()), @"Nested ASLayoutElementContexts aren't supported.");
const auto cfCtx = (__bridge_retained CFTypeRef)context;
pthread_setspecific(ASLayoutElementContextKey(), cfCtx);
}
获取当前线程的Context
ASLayoutElementContext *ASLayoutElementGetCurrentContext()
{
// Don't retain here. Caller will retain if it wants to!
const auto ctxPtr = pthread_getspecific(ASLayoutElementContextKey());
return (__bridge ASLayoutElementContext *)ctxPtr;
}
移除Context
void ASLayoutElementPopContext()
{
const auto ctx = (CFTypeRef)pthread_getspecific(ASLayoutElementContextKey());
ASDisplayNodeCAssertNotNil(ctx, @"Attempt to pop context when there wasn't a context!");
CFRelease(ctx);
pthread_setspecific(ASLayoutElementContextKey(), NULL);
}
原先在头文件定义的内容,在实现文件赋值定义
#pragma mark - ASLayoutElementStyle
NSString * const ASLayoutElementStyleWidthProperty = @"ASLayoutElementStyleWidthProperty";
此处省略一些...
此处用宏定义了两个函数,对于宏的理解,可以参考一下某大神的博客
宏定义的黑魔法
#define ASLayoutElementStyleSetSizeWithScope(x) \
({ \
__instanceLock__.lock(); \
const ASLayoutElementSize oldSize = _size.load(); \
ASLayoutElementSize newSize = oldSize; \
{x}; \
BOOL changed = !ASLayoutElementSizeEqualToLayoutElementSize(oldSize, newSize); \
if (changed) { \
_size.store(newSize); \
} \
__instanceLock__.unlock(); \
changed; \
})
#define ASLayoutElementStyleCallDelegate(propertyName)\
do {\
[self propertyDidChange:propertyName];\
[_delegate style:self propertyDidChange:propertyName];\
} while(0)
未完待续……