Skip to content

Commit

Permalink
Break the strong reference cycle between CADisplayLink and TOMSMorphi…
Browse files Browse the repository at this point in the history
…ngLabel.

CADisplayLink retains the object set as its target and TOMSMorphingLabel references the display link strongly. This gives a strong reference cycle and TOMSMorphingLabel is never deallocated. This commit adds a helper class named TOMSMorphingLabelWeakWrapper which holds a weak reference to the label and forwards necessary messages to it. This breaks the reference cycle and allows deallocating the label.

Note that if the weak wrapper's label reference gets nullified due to the label being deallocated, the display link would continue firing its events to the weak wrapper. So it is necessary to properly invalidate the display link in the TOMSMorphingLabel's dealloc.
  • Loading branch information
wanderwaltz committed Nov 22, 2014
1 parent 3bb1a66 commit d181a01
Showing 1 changed file with 62 additions and 8 deletions.
70 changes: 62 additions & 8 deletions Classes/TOMSMorphingLabel.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,18 @@

#define kTOMSKernFactorAttributeName @"kTOMSKernFactorAttributeName"

/** A helper class which is used to break the strong reference cycle
* between CADisplayLink and TOMSMorphingLabel. Does nothing significant,
* just forwards all messages to its morphingLabel property.
*/
@interface TOMSMorphingLabelWeakWrapper : NSObject
@property (nonatomic, weak, readonly) TOMSMorphingLabel *morphingLabel;

- (instancetype)initWithLabel:(TOMSMorphingLabel *)label;

@end


@interface TOMSMorphingLabel (){
void (^_setTextCompletionBlock)(void);
}
Expand Down Expand Up @@ -78,16 +90,20 @@ - (void)designatedInitialization
_morphingEnabled = YES;
self.animating = NO;

self.displayLink = [CADisplayLink displayLinkWithTarget:self
selector:@selector(tickInitial)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop]
forMode:NSRunLoopCommonModes];
[self setupDisplayLinkWithSelector:@selector(tickInitial)];

self.animationDuration = 0.37;
self.characterAnimationOffset = 0.25;
self.characterShrinkFactor = 4;
}


- (void)dealloc
{
[_displayLink invalidate];
_displayLink = nil;
}

#pragma mark - Setters

- (void)numberOfAttributionStagesShouldChange
Expand Down Expand Up @@ -144,16 +160,27 @@ - (void)tickInitial
self.displayLink.paused = YES;
CFTimeInterval duration = self.displayLink.duration;

self.displayLink = [CADisplayLink displayLinkWithTarget:self
selector:@selector(tickMorphing)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop]
forMode:NSRunLoopCommonModes];
[self setupDisplayLinkWithSelector: @selector(tickMorphing)];
self.displayLink.paused = YES;

self.displayLinkDuration = duration;
}
}


- (void)setupDisplayLinkWithSelector:(SEL)selector
{
TOMSMorphingLabelWeakWrapper *displayLinkTarget = [[TOMSMorphingLabelWeakWrapper alloc] initWithLabel:self];
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:displayLinkTarget
selector:selector];

[displayLink addToRunLoop:[NSRunLoop currentRunLoop]
forMode:NSRunLoopCommonModes];

[self.displayLink invalidate];
self.displayLink = displayLink;
}

#pragma mark - Getters

- (CGFloat)easedValue:(CGFloat)p
Expand Down Expand Up @@ -431,3 +458,30 @@ - (void)tickMorphing
}

@end


@implementation TOMSMorphingLabelWeakWrapper

- (instancetype)initWithLabel:(TOMSMorphingLabel *)label
{
self = [super init];

if (self) {
_morphingLabel = label;
}

return self;
}

- (void)tickInitial
{
[self.morphingLabel tickInitial];
}

- (void)tickMorphing
{
[self.morphingLabel tickMorphing];
}

@end

0 comments on commit d181a01

Please sign in to comment.