mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 14:20:20 +00:00
Fix unsafe unretained references in ASControlNode.
Fix regression from the ARC conversion. Change the raw, unretained pointers in ASControlNode to weak references. Use NSMapTable instead of the pointer-as-NSValue system.
This commit is contained in:
@@ -9,8 +9,8 @@
|
||||
#import <AsyncDisplayKit/ASDisplayNode.h>
|
||||
|
||||
/**
|
||||
@abstract Kinds of events possible for control nodes.
|
||||
@discussion These events are identical to their UIControl counterparts.
|
||||
@abstract Kinds of events possible for control nodes.
|
||||
@discussion These events are identical to their UIControl counterparts.
|
||||
*/
|
||||
enum _ASControlNodeEvent
|
||||
{
|
||||
@@ -27,74 +27,74 @@ enum _ASControlNodeEvent
|
||||
typedef NSUInteger ASControlNodeEvent;
|
||||
|
||||
/**
|
||||
@abstract ASControlNode is the base class for control nodes (such as buttons), or nodes that track touches to invoke targets with action messages.
|
||||
@discussion ASControlNode cannot be used directly. It instead defines the common interface and behavior structure for all its subclasses. Subclasses should import "ASControlNode+Subclasses.h" for information on methods intended to be overriden.
|
||||
@abstract ASControlNode is the base class for control nodes (such as buttons), or nodes that track touches to invoke targets with action messages.
|
||||
@discussion ASControlNode cannot be used directly. It instead defines the common interface and behavior structure for all its subclasses. Subclasses should import "ASControlNode+Subclasses.h" for information on methods intended to be overriden.
|
||||
*/
|
||||
@interface ASControlNode : ASDisplayNode
|
||||
|
||||
#pragma mark - Control State
|
||||
|
||||
/**
|
||||
@abstract Indicates whether or not the receiver is enabled.
|
||||
@discussion Specify YES to make the control enabled; otherwise, specify NO to make it disabled. The default value is YES. If the enabled state is NO, the control ignores touch events and subclasses may draw differently.
|
||||
@abstract Indicates whether or not the receiver is enabled.
|
||||
@discussion Specify YES to make the control enabled; otherwise, specify NO to make it disabled. The default value is YES. If the enabled state is NO, the control ignores touch events and subclasses may draw differently.
|
||||
*/
|
||||
@property (nonatomic, assign, getter=isEnabled) BOOL enabled;
|
||||
|
||||
/**
|
||||
@abstract Indicates whether or not the receiver is highlighted.
|
||||
@discussion This is set automatically when the there is a touch inside the control and removed on exit or touch up. This is different from touchInside in that it includes an area around the control, rather than just for touches inside the control.
|
||||
@abstract Indicates whether or not the receiver is highlighted.
|
||||
@discussion This is set automatically when the there is a touch inside the control and removed on exit or touch up. This is different from touchInside in that it includes an area around the control, rather than just for touches inside the control.
|
||||
*/
|
||||
@property (nonatomic, readonly, assign, getter=isHighlighted) BOOL highlighted;
|
||||
|
||||
#pragma mark - Tracking Touches
|
||||
/**
|
||||
@abstract Indicates whether or not the receiver is currently tracking touches related to an event.
|
||||
@discussion YES if the receiver is tracking touches; NO otherwise.
|
||||
@abstract Indicates whether or not the receiver is currently tracking touches related to an event.
|
||||
@discussion YES if the receiver is tracking touches; NO otherwise.
|
||||
*/
|
||||
@property (nonatomic, readonly, assign, getter=isTracking) BOOL tracking;
|
||||
|
||||
/**
|
||||
@abstract Indicates whether or not a touch is inside the bounds of the receiver.
|
||||
@discussion YES if a touch is inside the receiver's bounds; NO otherwise.
|
||||
@abstract Indicates whether or not a touch is inside the bounds of the receiver.
|
||||
@discussion YES if a touch is inside the receiver's bounds; NO otherwise.
|
||||
*/
|
||||
@property (nonatomic, readonly, assign, getter=isTouchInside) BOOL touchInside;
|
||||
|
||||
#pragma mark - Action Messages
|
||||
/**
|
||||
@abstract Adds a target-action pair for a particular event (or events).
|
||||
@param target The object to which the action message is sent. If this is nil, the responder chain is searched for an object willing to respond to the action message. target is not retained.
|
||||
@param action A selector identifying an action message. May optionally include the sender and the event as parameters, in that order. May not be NULL.
|
||||
@param controlEvents A bitmask specifying the control events for which the action message is sent. May not be 0. See "Control Events" for bitmask constants.
|
||||
@discussion You may call this method multiple times, and you may specify multiple target-action pairs for a particular event.
|
||||
@abstract Adds a target-action pair for a particular event (or events).
|
||||
@param target The object to which the action message is sent. If this is nil, the responder chain is searched for an object willing to respond to the action message. target is not retained.
|
||||
@param action A selector identifying an action message. May optionally include the sender and the event as parameters, in that order. May not be NULL.
|
||||
@param controlEvents A bitmask specifying the control events for which the action message is sent. May not be 0. See "Control Events" for bitmask constants.
|
||||
@discussion You may call this method multiple times, and you may specify multiple target-action pairs for a particular event. Targets are held weakly.
|
||||
*/
|
||||
- (void)addTarget:(id)target action:(SEL)action forControlEvents:(ASControlNodeEvent)controlEvents;
|
||||
|
||||
/**
|
||||
@abstract Returns the actions that are associated with a target and a particular control event.
|
||||
@param target The target object. May not be nil.
|
||||
@param controlEvent A single constant of type ASControlNodeEvent that specifies a particular user action on the control; for a list of these constants, see "Control Events". May not be 0 or ASControlNodeEventAllEvents.
|
||||
@result An array of selector names as NSString objects, or nil if there are no action selectors associated with controlEvent.
|
||||
@abstract Returns the actions that are associated with a target and a particular control event.
|
||||
@param target The target object. May not be nil.
|
||||
@param controlEvent A single constant of type ASControlNodeEvent that specifies a particular user action on the control; for a list of these constants, see "Control Events". May not be 0 or ASControlNodeEventAllEvents.
|
||||
@result An array of selector names as NSString objects, or nil if there are no action selectors associated with controlEvent.
|
||||
*/
|
||||
- (NSArray *)actionsForTarget:(id)target forControlEvent:(ASControlNodeEvent)controlEvent;
|
||||
|
||||
/**
|
||||
@abstract Returns all target objects associated with the receiver.
|
||||
@result A set of all targets for the receiver. The set may include NSNull to indicate at least one nil target (meaning, the responder chain is searched for a target.)
|
||||
@abstract Returns all target objects associated with the receiver.
|
||||
@result A set of all targets for the receiver. The set may include NSNull to indicate at least one nil target (meaning, the responder chain is searched for a target.)
|
||||
*/
|
||||
- (NSSet *)allTargets;
|
||||
|
||||
/**
|
||||
@abstract Removes a target-action pair for a particular event.
|
||||
@param target The target object. Pass nil to remove all targets paired with action and the specified control events.
|
||||
@param action A selector identifying an action message. Pass NULL to remove all action messages paired with target.
|
||||
@param controlEvents A bitmask specifying the control events associated with target and action. See "Control Events" for bitmask constants. May not be 0.
|
||||
@abstract Removes a target-action pair for a particular event.
|
||||
@param target The target object. Pass nil to remove all targets paired with action and the specified control events.
|
||||
@param action A selector identifying an action message. Pass NULL to remove all action messages paired with target.
|
||||
@param controlEvents A bitmask specifying the control events associated with target and action. See "Control Events" for bitmask constants. May not be 0.
|
||||
*/
|
||||
- (void)removeTarget:(id)target action:(SEL)action forControlEvents:(ASControlNodeEvent)controlEvents;
|
||||
|
||||
/**
|
||||
@abstract Sends the actions for the control events for a particular event.
|
||||
@param controlEvents A bitmask specifying the control events for which to send actions. See "Control Events" for bitmask constants. May not be 0.
|
||||
@param event The event which triggered these control actions. May be nil.
|
||||
@abstract Sends the actions for the control events for a particular event.
|
||||
@param controlEvents A bitmask specifying the control events for which to send actions. See "Control Events" for bitmask constants. May not be 0.
|
||||
@param event The event which triggered these control actions. May be nil.
|
||||
*/
|
||||
- (void)sendActionsForControlEvents:(ASControlNodeEvent)controlEvents withEvent:(UIEvent *)event;
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
// Initial capacities for dispatch tables.
|
||||
#define kASControlNodeEventDispatchTableInitialCapacity 4
|
||||
#define kASControlNodeTargetDispatchTableInitialCapacity 2
|
||||
#define kASControlNodeActionDispatchTableInitialCapacity 4
|
||||
|
||||
@interface ASControlNode ()
|
||||
@@ -43,7 +42,7 @@
|
||||
...
|
||||
}
|
||||
*/
|
||||
__block NSMutableDictionary *_controlEventDispatchTable;
|
||||
NSMutableDictionary *_controlEventDispatchTable;
|
||||
}
|
||||
|
||||
// Read-write overrides.
|
||||
@@ -51,37 +50,21 @@
|
||||
@property (nonatomic, readwrite, assign, getter=isTracking) BOOL tracking;
|
||||
@property (nonatomic, readwrite, assign, getter=isTouchInside) BOOL touchInside;
|
||||
|
||||
/**
|
||||
@abstract Indicates whether the receiver is interested in receiving touches.
|
||||
*/
|
||||
//! @abstract Indicates whether the receiver is interested in receiving touches.
|
||||
- (BOOL)_isInterestedInTouches;
|
||||
|
||||
/**
|
||||
@abstract Returns a key to be used in _controlEventDispatchTable that identifies the control event.
|
||||
@param controlEvent A control event.
|
||||
@result A key for use in _controlEventDispatchTable.
|
||||
@abstract Returns a key to be used in _controlEventDispatchTable that identifies the control event.
|
||||
@param controlEvent A control event.
|
||||
@result A key for use in _controlEventDispatchTable.
|
||||
*/
|
||||
id<NSCopying> _ASControlNodeEventKeyForControlEvent(ASControlNodeEvent controlEvent);
|
||||
|
||||
/**
|
||||
@abstract Returns a key to be used inside the dictionaries within _controlEventDispatchTable that identifies the target.
|
||||
@param target A target. May safely be nil.
|
||||
@result A key for use in in the dictionaries within _controlEventDispatchTable.
|
||||
*/
|
||||
id<NSCopying> _ASControlNodeTargetKeyForTarget(id target);
|
||||
|
||||
/**
|
||||
@abstract Returns the target for invocation from a given targetKey.
|
||||
@param targetKey A target key created with _ASControlNodeTargetKeyForTarget(). May not be nil.
|
||||
@result The target, or nil if no target was originally used.
|
||||
*/
|
||||
id _ASControlNodeTargetForTargetKey(id<NSCopying> targetKey);
|
||||
|
||||
/**
|
||||
@abstract Enumerates the ASControlNode events included mask, invoking the block for each event.
|
||||
@param mask An ASControlNodeEvent mask.
|
||||
@param block The block to be invoked for each ASControlNodeEvent included in mask.
|
||||
@param anEvent An even that is included in mask.
|
||||
@abstract Enumerates the ASControlNode events included mask, invoking the block for each event.
|
||||
@param mask An ASControlNodeEvent mask.
|
||||
@param block The block to be invoked for each ASControlNodeEvent included in mask.
|
||||
@param anEvent An even that is included in mask.
|
||||
*/
|
||||
void _ASEnumerateControlEventsIncludedInMaskWithBlock(ASControlNodeEvent mask, void (^block)(ASControlNodeEvent anEvent));
|
||||
|
||||
@@ -222,10 +205,6 @@ void _ASEnumerateControlEventsIncludedInMaskWithBlock(ASControlNodeEvent mask, v
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark - Control Attributes
|
||||
|
||||
#pragma mark - Tracking Touches
|
||||
|
||||
#pragma mark - Action Messages
|
||||
- (void)addTarget:(id)target action:(SEL)action forControlEvents:(ASControlNodeEvent)controlEventMask
|
||||
{
|
||||
@@ -238,23 +217,22 @@ void _ASEnumerateControlEventsIncludedInMaskWithBlock(ASControlNodeEvent mask, v
|
||||
{
|
||||
// Do we already have an event table for this control event?
|
||||
id<NSCopying> eventKey = _ASControlNodeEventKeyForControlEvent(controlEvent);
|
||||
NSMutableDictionary *eventDispatchTable = [_controlEventDispatchTable objectForKey:eventKey];
|
||||
NSMapTable *eventDispatchTable = [_controlEventDispatchTable objectForKey:eventKey];
|
||||
// Create it if necessary.
|
||||
if (!eventDispatchTable)
|
||||
{
|
||||
// Create the dispatch table for this event.
|
||||
eventDispatchTable = [[NSMutableDictionary alloc] initWithCapacity:kASControlNodeTargetDispatchTableInitialCapacity]; // enough to handle common types without re-hashing the dictionary when adding entries
|
||||
eventDispatchTable = [NSMapTable weakToStrongObjectsMapTable];
|
||||
[_controlEventDispatchTable setObject:eventDispatchTable forKey:eventKey];
|
||||
}
|
||||
|
||||
// Have we seen this target before for this event?
|
||||
id<NSCopying> targetKey = _ASControlNodeTargetKeyForTarget(target);
|
||||
NSMutableArray *targetActions = [eventDispatchTable objectForKey:targetKey];
|
||||
NSMutableArray *targetActions = [eventDispatchTable objectForKey:target];
|
||||
if (!targetActions)
|
||||
{
|
||||
// Nope. Create an actions array for it.
|
||||
targetActions = [[NSMutableArray alloc] initWithCapacity:kASControlNodeActionDispatchTableInitialCapacity]; // enough to handle common types without re-hashing the dictionary when adding entries.
|
||||
[eventDispatchTable setObject:targetActions forKey:targetKey];
|
||||
[eventDispatchTable setObject:targetActions forKey:target];
|
||||
}
|
||||
|
||||
// Add the action message.
|
||||
@@ -271,12 +249,12 @@ void _ASEnumerateControlEventsIncludedInMaskWithBlock(ASControlNodeEvent mask, v
|
||||
NSParameterAssert(controlEvent != 0 && controlEvent != ASControlNodeEventAllEvents);
|
||||
|
||||
// Grab the event dispatch table for this event.
|
||||
NSDictionary *eventDispatchTable = [_controlEventDispatchTable objectForKey:_ASControlNodeEventKeyForControlEvent(controlEvent)];
|
||||
NSMapTable *eventDispatchTable = [_controlEventDispatchTable objectForKey:_ASControlNodeEventKeyForControlEvent(controlEvent)];
|
||||
if (!eventDispatchTable)
|
||||
return nil;
|
||||
|
||||
// Grab and return the actions for this target.
|
||||
return [eventDispatchTable objectForKey:_ASControlNodeTargetKeyForTarget(target)];
|
||||
// Return the actions for this target.
|
||||
return [eventDispatchTable objectForKey:target];
|
||||
}
|
||||
|
||||
- (NSSet *)allTargets
|
||||
@@ -284,11 +262,11 @@ void _ASEnumerateControlEventsIncludedInMaskWithBlock(ASControlNodeEvent mask, v
|
||||
NSMutableSet *targets = [[NSMutableSet alloc] init];
|
||||
|
||||
// Look at each event...
|
||||
for (NSDictionary *eventDispatchTable in [_controlEventDispatchTable allValues])
|
||||
for (NSMapTable *eventDispatchTable in [_controlEventDispatchTable allValues])
|
||||
{
|
||||
// and each event's targets...
|
||||
for (id <NSCopying> targetKey in eventDispatchTable)
|
||||
[targets addObject:_ASControlNodeTargetForTargetKey(targetKey)];
|
||||
for (id target in eventDispatchTable)
|
||||
[targets addObject:target];
|
||||
}
|
||||
|
||||
return targets;
|
||||
@@ -304,15 +282,15 @@ void _ASEnumerateControlEventsIncludedInMaskWithBlock(ASControlNodeEvent mask, v
|
||||
{
|
||||
// Grab the dispatch table for this event (if we have it).
|
||||
id<NSCopying> eventKey = _ASControlNodeEventKeyForControlEvent(controlEvent);
|
||||
NSMutableDictionary *eventDispatchTable = [_controlEventDispatchTable objectForKey:eventKey];
|
||||
NSMapTable *eventDispatchTable = [_controlEventDispatchTable objectForKey:eventKey];
|
||||
if (!eventDispatchTable)
|
||||
return;
|
||||
|
||||
void (^removeActionFromTarget)(id <NSCopying> targetKey, SEL action) = ^
|
||||
(id <NSCopying> targetKey, SEL theAction)
|
||||
(id aTarget, SEL theAction)
|
||||
{
|
||||
// Grab the targetActions for this target.
|
||||
NSMutableArray *targetActions = [eventDispatchTable objectForKey:targetKey];
|
||||
NSMutableArray *targetActions = [eventDispatchTable objectForKey:aTarget];
|
||||
|
||||
// Remove action if we have it.
|
||||
if (theAction)
|
||||
@@ -324,7 +302,7 @@ void _ASEnumerateControlEventsIncludedInMaskWithBlock(ASControlNodeEvent mask, v
|
||||
// If there are no actions left, remove this target entry.
|
||||
if ([targetActions count] == 0)
|
||||
{
|
||||
[eventDispatchTable removeObjectForKey:targetKey];
|
||||
[eventDispatchTable removeObjectForKey:aTarget];
|
||||
|
||||
// If there are no targets for this event anymore, remove it.
|
||||
if ([eventDispatchTable count] == 0)
|
||||
@@ -337,11 +315,11 @@ void _ASEnumerateControlEventsIncludedInMaskWithBlock(ASControlNodeEvent mask, v
|
||||
if (!target)
|
||||
{
|
||||
// Look at every target, removing target-pairs that have action (or all of its actions).
|
||||
for (id <NSCopying> targetKey in eventDispatchTable)
|
||||
removeActionFromTarget(targetKey, action);
|
||||
for (id aTarget in eventDispatchTable)
|
||||
removeActionFromTarget(aTarget, action);
|
||||
}
|
||||
else
|
||||
removeActionFromTarget(_ASControlNodeTargetKeyForTarget(target), action);
|
||||
removeActionFromTarget(target, action);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -354,13 +332,12 @@ void _ASEnumerateControlEventsIncludedInMaskWithBlock(ASControlNodeEvent mask, v
|
||||
_ASEnumerateControlEventsIncludedInMaskWithBlock(controlEvents, ^
|
||||
(ASControlNodeEvent controlEvent)
|
||||
{
|
||||
NSDictionary *eventDispatchTable = [_controlEventDispatchTable objectForKey:_ASControlNodeEventKeyForControlEvent(controlEvent)];
|
||||
NSMapTable *eventDispatchTable = [_controlEventDispatchTable objectForKey:_ASControlNodeEventKeyForControlEvent(controlEvent)];
|
||||
|
||||
// For each target interested in this event...
|
||||
for (id <NSCopying> targetKey in eventDispatchTable)
|
||||
for (id target in eventDispatchTable)
|
||||
{
|
||||
id target = _ASControlNodeTargetForTargetKey(targetKey);
|
||||
NSArray *targetActions = [eventDispatchTable objectForKey:targetKey];
|
||||
NSArray *targetActions = [eventDispatchTable objectForKey:target];
|
||||
|
||||
// Invoke each of the actions on target.
|
||||
for (NSString *actionMessage in targetActions)
|
||||
@@ -387,16 +364,6 @@ id<NSCopying> _ASControlNodeEventKeyForControlEvent(ASControlNodeEvent controlEv
|
||||
return [NSNumber numberWithInteger:controlEvent];
|
||||
}
|
||||
|
||||
id<NSCopying> _ASControlNodeTargetKeyForTarget(id target)
|
||||
{
|
||||
return (target ? [NSValue valueWithPointer:(__bridge const void *)(target)] : [NSNull null]);
|
||||
}
|
||||
|
||||
id _ASControlNodeTargetForTargetKey(id<NSCopying> targetKey)
|
||||
{
|
||||
return (targetKey != [NSNull null] ? [(NSValue *)targetKey pointerValue] : nil);
|
||||
}
|
||||
|
||||
void _ASEnumerateControlEventsIncludedInMaskWithBlock(ASControlNodeEvent mask, void (^block)(ASControlNodeEvent anEvent))
|
||||
{
|
||||
// Start with our first event (touch down) and work our way up to the last event (touch cancel)
|
||||
|
||||
Reference in New Issue
Block a user