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:
@@ -65,7 +65,7 @@ typedef NSUInteger ASControlNodeEvent;
|
||||
@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.
|
||||
@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;
|
||||
|
||||
|
||||
@@ -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,9 +50,7 @@
|
||||
@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;
|
||||
|
||||
/**
|
||||
@@ -63,20 +60,6 @@
|
||||
*/
|
||||
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.
|
||||
@@ -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