iOS AsyncDisplayKit 코드 읽기 노트
이 기사는 AsyncDisplayKit의 원본 읽기 노트입니다.흩어져 있는 기록들.읽고 나면 아마 AsyncDisplayKit를 분석할 수 있을 거예요.
1: 배경 해제 변수
void ASPerformBlockOnDeallocationQueue(void (^block)())
{
static dispatch_queue_t queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
queue = dispatch_queue_create("org.AsyncDisplayKit.deallocationQueue", DISPATCH_QUEUE_SERIAL);
});
dispatch_async(queue, block);
}
ASPerformBlockOnDeallocationQueue
대상을 백엔드 라인에서 방출하여 인터페이스를 더욱 원활하게 한다.예컨대 응용- (void)_clearImage
{
// Destruction of bigger images on the main thread can be expensive
// and can take some time, so we dispatch onto a bg queue to
// actually dealloc.
__block UIImage *image = self.image;
CGSize imageSize = image.size;
BOOL shouldReleaseImageOnBackgroundThread = imageSize.width > kMinReleaseImageOnBackgroundSize.width ||
imageSize.height > kMinReleaseImageOnBackgroundSize.height;
if (shouldReleaseImageOnBackgroundThread) {
ASPerformBlockOnDeallocationQueue(^{
image = nil;
});
}
///TODO
///
주석에 의하면 큰 그림은 주 라인에서 방출될 때 더욱 좋은 성능과 시간을 소모할 수 있다고 한다.여기, 최소 크기는
static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
20x20 사이즈의 그림, 우리 응용 프로그램에서 이것보다 훨씬 큰 그림이죠!하지만 다른 라인에서 풀어야 한다는 생각은 한 번도 해본 적이 없다!그래서 AsyncDisplayKit의 성능이 좋은 것은 일리가 있습니다. 성능 처리, 극대화!!
2: 일부 특정한 방법은 부류에서만 실현할 수 있다.
BOOL ASSubclassOverridesSelector(Class superclass, Class subclass, SEL selector)
{
Method superclassMethod = class_getInstanceMethod(superclass, selector);
Method subclassMethod = class_getInstanceMethod(subclass, selector);
IMP superclassIMP = superclassMethod ? method_getImplementation(superclassMethod) : NULL;
IMP subclassIMP = subclassMethod ? method_getImplementation(subclassMethod) : NULL;
return (superclassIMP != subclassIMP);
}
ASSubclassOverridesSelector
자류가 부류를 실현했는지 판단하는 데 사용되는 어떤 방법, 용법, 협조#define ASDisplayNodeAssert(...) NSAssert(__VA_ARGS__)
宏initialize
방법에서 단언을 하는데 어떤 방법은 자류가 반드시 실현될 수 없다.예컨대
ASImageNode
+ (void)initialize
{
[super initialize];
if (self != [ASImageNode class]) {
// Prevent custom drawing in subclasses
ASDisplayNodeAssert(!ASSubclassOverridesClassSelector([ASImageNode class], self, @selector(displayWithParameters:isCancelled:)), @"Subclass %@ must not override displayWithParameters:isCancelled: method. Custom drawing in %@ subclass is not supported.", NSStringFromClass(self), NSStringFromClass([ASImageNode class]));
}
}
3: 미션을 주 라인의 정확한 자세로
void ASPerformBlockOnMainThread(void (^block)())
{
if (block == nil){
return;
}
if (ASDisplayNodeThreadIsMain()) {
block();
} else {
dispatch_async(dispatch_get_main_queue(), block);
}
}
만약 판단을 하지 않는다면, 임무 수행의 시기에 변화가 생길 것이다!
4: 성능을 소모하는 조작, 가능한 한 한 한 번만 값을 구하는 조작을 한다
예를 들면 다음과 같습니다.
CGFloat ASScreenScale()
{
static CGFloat __scale = 0.0;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
ASDisplayNodeCAssertMainThread();
__scale = [[UIScreen mainScreen] scale];
});
return __scale;
}
ASScreenScale
의 호출 시기는 ASDisplayNode
의 load
방법에서+ (void)load
{
// Ensure this value is cached on the main thread before needed in the background.
ASScreenScale();
}
scale의 값이 이미 계산되었고 주 라인에서 계산되었는지 확인하십시오.UIKit 관련 코드를 사용했기 때문에 반드시 메인 라인에 있어야 합니다.
5:
ASSentinel
ASSentinel
의 역할은 레이어가 반복적으로 렌더링되는 것을 방지하는 것이다. 예를 들어 레이어가 화면을 넘어섰거나 다음에 렌더링된 데이터가 왔으면 표시가 필요하고 이번에는 렌더링을 하지 않아도 된다.@interface ASSentinel : NSObject
- (int32_t)value;
- (int32_t)increment;
@end
정의는 간단합니다. 현재 값을 되돌려줍니다.increment에서 현재 값을 증가시킵니다.기본
_initializeInstance
함수에서 초기화,_displaySentinel = [[ASSentinel alloc] init];
현재 렌더링을 닫아야 할 때 자동 증가
- (void)cancelDisplayAsyncLayer:(_ASDisplayLayer *)asyncLayer
{
[_displaySentinel increment];
}
- (void)cancelAsyncDisplay
{
ASDisplayNodeAssertMainThread();
[_displaySentinel increment];
.........
}
delegate 함수의 판단은 구조체를 사용하여 어떤 방법이 실현되었는지 표시할 수 있다. 예를 들어 다음과 같다.
struct ASDisplayNodeFlags {
// public properties
unsigned synchronous:1;
unsigned layerBacked:1;
unsigned displaysAsynchronously:1;
unsigned shouldRasterizeDescendants:1;
unsigned shouldBypassEnsureDisplay:1;
unsigned displaySuspended:1;
unsigned shouldAnimateSizeChanges:1;
unsigned hasCustomDrawingPriority:1;
}
구체적인 사용은 다음과 같다(ps저인 프로젝트에서의 사용).
struct {
unsigned hasJingxiangAction:1;
unsigned hasCameraAction :1;
unsigned hasMuteAction :1;
unsigned hasRoomManagerAction:1;
unsigned hasFollowlistAction:1;
}_delefateAction; ///)delegate {
_delegate = delegate;
_delefateAction.hasJingxiangAction = [_delegate respondsToSelector:@selector(jingxiangAction)];
_delefateAction.hasCameraAction = [_delegate respondsToSelector:@selector(cameraAction)];
_delefateAction.hasMuteAction = [_delegate respondsToSelector:@selector(muteAction)];
_delefateAction.hasRoomManagerAction = [_delegate respondsToSelector:@selector(roomManagerAciton)];
_delefateAction.hasFollowlistAction = [_delegate respondsToSelector:@selector(followerListAction)];
}
매번
respondsToSelector
방법을 사용하지 않아도 약간의 효율을 높일 수 있고, 사용할 때 이렇게 판단할 수 있다if (_delefateAction.hasRoomManagerAction) {
[_delegate roomManagerAciton];
}
이전의 번거로운 판단을 생략했다.
화려한 분할선!!!!
노드가view나layer를 언제 만들었는지 봅시다.(보기를 표시합니다.view나layer가 틀림없습니다.)
- (void)addSubnode:(ASDisplayNode *)subnode
{
/// #
ASDisplayNodeAssert(subnode, @"Cannot insert a nil subnode");
ASDisplayNode *oldParent = subnode.supernode;
///#
if (!subnode || subnode == self || oldParent == self) {
return;
}
///#
BOOL isMovingEquivalentParents = disableNotificationsForMovingBetweenParents(oldParent, self);
if (isMovingEquivalentParents) {
[subnode __incrementVisibilityNotificationsDisabled];
}
[subnode removeFromSupernode];
if (!_subnodes) {
_subnodes = [[NSMutableArray alloc] init];
}
[_subnodes addObject:subnode];
// This call will apply our .hierarchyState to the new subnode.
// If we are a managed hierarchy, as in ASCellNode trees, it will also apply our .interfaceState.
[subnode __setSupernode:self];
///# node view layer view layer 。。
if (self.nodeLoaded) {
ASPerformBlockOnMainThread(^{
[self _addSubnodeSubviewOrSublayer:subnode];
});
}
ASDisplayNodeAssert(isMovingEquivalentParents == disableNotificationsForMovingBetweenParents(oldParent, self), @"Invariant violated");
if (isMovingEquivalentParents) {
[subnode __decrementVisibilityNotificationsDisabled];
}
}
절차상, addSubnode에서 부모 node가 이미 표시되면 만듭니다.
뷰의 addSubnode 메서드를 호출하는 또 다른 방법이 있습니다.
- (void)addSubnode:(ASDisplayNode *)subnode
{
if (subnode.layerBacked) {
// Call -addSubnode: so that we use the asyncdisplaykit_node path if possible.
[self.layer addSubnode:subnode];
} else {
ASDisplayNode *selfNode = self.asyncdisplaykit_node;
if (selfNode) {
[selfNode addSubnode:subnode];
} else {
[self addSubview:subnode.view];
}
}
}
마지막으로 [self addSubview:subnode.view];뷰를 만드는 절차를 밟기 시작합니다.
view 만들기 절차:
- (UIView *)view
{
if (_flags.layerBacked) {
return nil;
}
if (!_view) {
ASDisplayNodeAssertMainThread();
[self _loadViewOrLayerIsLayerBacked:NO];
}
return _view;
}
- (CALayer *)layer
{
if (!_layer) {
ASDisplayNodeAssertMainThread();
if (!_flags.layerBacked) {
return self.view.layer;
}
[self _loadViewOrLayerIsLayerBacked:YES];
}
return _layer;
}
view나 layer를 만들 때 ASDisplay Node AssertMainThread () 를 사용합니다.매크로는 주 라인에서만 만들 수 있습니다.마지막으로
_loadViewOrLayerIsLayerBacked
함수를 호출할 것입니다.YES 가져오기 layer 만들기 뷰 가져오기 NO- (void)_loadViewOrLayerIsLayerBacked:(BOOL)isLayerBacked
{
/// node ,
if (self._isDeallocating) {
return;
}
/// ,
if (![self __shouldLoadViewOrLayer]) {
return;
}
/// layer
if (isLayerBacked) {
_layer = [self _layerToLoad];
_layer.delegate = (id)self;
} else {
/// view
_view = [self _viewToLoad];
_view.asyncdisplaykit_node = self;
_layer = _view.layer;
}
_layer.asyncdisplaykit_node = self;
self.asyncLayer.asyncDelegate = self;
///#
......
}
view를 만드는 과정은 다음과 같습니다.
- (UIView *)_viewToLoad
{
UIView *view;
ASDN::MutexLocker l(__instanceLock__);
if (_viewBlock) {
view = _viewBlock();
ASDisplayNodeAssertNotNil(view, @"View block returned nil");
ASDisplayNodeAssert(![view isKindOfClass:[_ASDisplayView class]], @"View block should return a synchronously displayed view");
_viewBlock = nil;
_viewClass = [view class];
} else {
if (!_viewClass) {
_viewClass = [self.class viewClass];
}
view = [[_viewClass alloc] init];
}
// Update flags related to special handling of UIImageView layers. More details on the flags
if (_flags.synchronous && [_viewClass isSubclassOfClass:[UIImageView class]]) {
_flags.canClearContentsOfLayer = NO;
_flags.canCallSetNeedsDisplayOfLayer = NO;
}
return view;
}
초기화할 때 제공된다면view Block, 바로viewBlock에서 기본view로 만들거나view를 되돌려줍니다.
+ (Class)viewClass
{
return [_ASDisplayView class];
}
+ (Class)layerClass
{
return [_ASDisplayLayer class];
}
ASDisplayNode
내장된view와layer,ASDisplayView는 이벤트를 전달하는 데 사용됩니다.ASDisplayLayer는 성능 향상의 열쇠입니다.위에서 보듯이 node 노드는view와layer를 포장한 캐리어로 node를 통해view나layer의 속성을 설정할 수 있습니다.view나layer가 표시될 때 속성을 설정합니다.
레이아웃과 관련된 코드를 무시했습니다.나중에 시간이 나면 보충해 주세요.
_ASDisplayView
_ASDisplayView는 ASDisplayNode의 개인 클래스로 ASDisplayNode에서만 사용할 수 있으며 계승하고 다시 쓸 수 없습니다.앞에서 언급한 적이 있다
+ (Class)viewClass
{
return [_ASDisplayView class];
}
_ASDisplayView의 역할은 UIView의 일부 방법을 다시 써서 ASDisplayNode에 이벤트를 전달하는 것이다. 다음은 주요 함수를 살펴보자.
+ (Class)layerClass
{
return [_ASDisplayLayer class];
}
layerClass 함수를 다시 쓰고 비동기적으로 그릴 수 있는 클래스를 사용합니다ASDisplayLayer.
- (void)willMoveToWindow:(UIWindow *)newWindow
{
BOOL visible = (newWindow != nil);
if (visible && !_node.inHierarchy) {
[_node __enterHierarchy];
}
}
window에 추가할 때 이벤트를 Node에 전달합니다.물론 다른 didMoveToWindow 같은 것도 있어요.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if (_node.methodOverrides & ASDisplayNodeMethodOverrideTouchesBegan) {
[_node touchesBegan:touches withEvent:event];
} else {
[super touchesBegan:touches withEvent:event];
}
}
터치 시리즈의 함수 재작성은 현재 node가 대응하는 방법을 실현했는지 판단하고 실현되면 이벤트를 node에 전달합니다.
ASControlNode
의 실현은 터치 함수의 전달 덕분이다.분할선
두 개의 obj가 같은지 아닌지를 판단하다
ASDISPLAYNODE_INLINE BOOL ASObjectIsEqual(id obj, id otherObj)
{
return obj == otherObj || [obj isEqual:otherObj];
}
매개 변수는nil 여기입니다!
_ASDisplayLayer
봐봐ASDisplayLayer 구현
typedef BOOL(^asdisplaynode_iscancelled_block_t)(void);
현재 비동기식 작업이 끝났는지 표시하는 데 사용
@interface _ASDisplayLayer : CALayer
///#
@property (nonatomic, assign) BOOL displaysAsynchronously;
///#
- (void)cancelAsyncDisplay;
///#
+ (dispatch_queue_t)displayQueue;
///# ,
@property (nonatomic, strong, readonly) ASSentinel *displaySentinel;
///#
@property (nonatomic, weak) id<_asdisplaylayerdelegate> asyncDelegate;
///#
@property (nonatomic, assign, getter=isDisplaySuspended) BOOL displaySuspended;
///#
- (void)displayImmediately;
ASCALayerExtendedDelegate
bounds가 바뀔 때 이 에이전트를 호출하는 방법입니다.
_ASDisplayLayerDelegate
비동기적으로 렌더링할 때 일부 리셋을 할 수 있다. node는 이러한 방법을 실현하여 자신의 렌더링을 실현할 수 있다.
_ASDisplayLayer 구현
- (instancetype)init
{
if ((self = [super init])) {
_displaySentinel = [[ASSentinel alloc] init];
self.opaque = YES;
}
return self;
}
_displaySentinel에서 비동기적으로 닫는 조건을 초기화합니다.
몇 가지 주요한 방법을 보다.
+ (id)defaultValueForKey:(NSString *)key
{
if ([key isEqualToString:@"displaysAsynchronously"]) {
return @YES;
} else {
return [super defaultValueForKey:key];
}
}
layer의 기본 속성 값을 설정합니다. 이것은 디스플레이 Asynchronously를 설정하는 데 기본적으로 YES입니다.(비동기 디스플레이 효과 기본값)
- (void)setNeedsDisplay
{
///#
ASDisplayNodeAssertMainThread();
///#
_displaySuspendedLock.lock();
/// , , , ,
[self cancelAsyncDisplay];
// Short circuit if display is suspended. When resumed, we will setNeedsDisplay at that time.
if (!_displaySuspended) {
[super setNeedsDisplay];
}
_displaySuspendedLock.unlock();
}
setNeedsDisplay 설정 내용을 다시 그려야 합니다.그리기 전에 지난번에 그리기 시작하지 않은 작업을 닫으십시오!
+ (dispatch_queue_t)displayQueue
{
static dispatch_queue_t displayQueue = NULL;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
displayQueue = dispatch_queue_create("org.AsyncDisplayKit.ASDisplayLayer.displayQueue", DISPATCH_QUEUE_CONCURRENT);
// we use the highpri queue to prioritize UI rendering over other async operations
dispatch_set_target_queue(displayQueue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0));
});
return displayQueue;
}
displayQueue가 비동기적으로 그려진 대기열입니다.대기열은 DISPATCH 입니다.QUEUE_CONCURRENT 병행입니다.그리고 디스패치set_target_queue 설정된 DISPATCHQUEUE_PRIORITY_HIGH.UI 드로잉과 관련된 대기열은 우선 순위가 가장 높습니다.
- (void)display
{
///#
ASDisplayNodeAssertMainThread();
[self _hackResetNeedsDisplay];
/// ,
if (self.isDisplaySuspended) {
return;
}
///
[self display:self.displaysAsynchronously];
}
- (void)display:(BOOL)asynchronously
{
/// 0
if (CGRectIsEmpty(self.bounds)) {
_attemptedDisplayWhileZeroSized = YES;
}
id<_asdisplaylayerdelegate> NS_VALID_UNTIL_END_OF_SCOPE strongAsyncDelegate;
{
_asyncDelegateLock.lock();
strongAsyncDelegate = _asyncDelegate;
_asyncDelegateLock.unlock();
}
/// ,
[strongAsyncDelegate displayAsyncLayer:self asynchronously:asynchronously];
}
so, 그림 부분은 에이전트, 즉 노드에 맡겼는데...ok, 우리 노드의 그리는 부분을 보자...관련 코드는
ASDisplayNode+AsyncDisplay.mm
파일에서- (void)displayAsyncLayer:(_ASDisplayLayer *)asyncLayer asynchronously:(BOOL)asynchronously
{
///
ASDisplayNodeAssertMainThread();
///
ASDN::MutexLocker l(__instanceLock__);
if (_hierarchyState & ASHierarchyStateRasterized) {
return;
}
/// block
ASSentinel *displaySentinel = (asynchronously ? _displaySentinel : nil);
int32_t displaySentinelValue = [displaySentinel increment];
asdisplaynode_iscancelled_block_t isCancelledBlock = ^{
return BOOL(displaySentinelValue != displaySentinel.value);
};
/// block image layer
asyncdisplaykit_async_transaction_operation_block_t displayBlock = [self _displayBlockWithAsynchronous:asynchronously isCancelledBlock:isCancelledBlock rasterizing:NO];
///# block,
if (!displayBlock) {
return;
}
/// runloop block
ASDisplayNodeAssert(_layer, @"Expect _layer to be not nil");
asyncdisplaykit_async_transaction_operation_completion_block_t completionBlock = ^(id value, BOOL canceled){
ASDisplayNodeCAssertMainThread();
if (!canceled && !isCancelledBlock()) {
UIImage *image = (UIImage *)value;
BOOL stretchable = (NO == UIEdgeInsetsEqualToEdgeInsets(image.capInsets, UIEdgeInsetsZero));
if (stretchable) {
ASDisplayNodeSetupLayerContentsWithResizableImage(_layer, image);
} else {
_layer.contentsScale = self.contentsScale;
_layer.contents = (id)image.CGImage;
}
[self didDisplayAsyncLayer:self.asyncLayer];
}
};
/// layer
[self willDisplayAsyncLayer:self.asyncLayer];
if (asynchronously) {/// , runloop
// Async rendering operations are contained by a transaction, which allows them to proceed and concurrently
// while synchronizing the final application of the results to the layer's contents property (completionBlock).
// First, look to see if we are expected to join a parent's transaction container.
CALayer *containerLayer = _layer.asyncdisplaykit_parentTransactionContainer ? : _layer;
// In the case that a transaction does not yet exist (such as for an individual node outside of a container),
// this call will allocate the transaction and add it to _ASAsyncTransactionGroup.
// It will automatically commit the transaction at the end of the runloop.
_ASAsyncTransaction *transaction = containerLayer.asyncdisplaykit_asyncTransaction;
// Adding this displayBlock operation to the transaction will start it IMMEDIATELY.
// The only function of the transaction commit is to gate the calling of the completionBlock.
[transaction addOperationWithBlock:displayBlock priority:self.drawingPriority queue:[_ASDisplayLayer displayQueue] completion:completionBlock];
} else {
/// , image, layer !!
UIImage *contents = (UIImage *)displayBlock();
completionBlock(contents, NO);
}
}
여기에서 관련 기술은 YY대신블로그를 통해 인터페이스를 원활하게 유지하는 기교를 이해할 수 있다
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.