即使在指针设置为 nil 后 UIMenuItem 仍会继续显示
Posted
技术标签:
【中文标题】即使在指针设置为 nil 后 UIMenuItem 仍会继续显示【英文标题】:UIMenuItem continuing to show up even after the pointer is set as nil 【发布时间】:2014-07-06 17:45:51 【问题描述】:在下面的代码中,当我选择一行时,会显示一个删除菜单。现在,当我开始画一条新线(保留删除菜单原样)之后,第一个顶部的删除菜单应该会消失。那是因为当我开始绘制第二条线时,uimenucontroller 单例对象会破坏,因为指向它的指针设置为 nil。这是应该发生的事情。但显然不是..当我在drawRect方法中记录指针时,它确实显示为nil..那么为什么菜单仍然显示?这是代码---
-(void)tap:(UIGestureRecognizer *)sender
NSLog(@"Recognized tap-%@",sender);
CGPoint p= [sender locationInView:self];
selectedLine= [self lineAtPoint:p];//Not going to return anything with just a tap which draws a (non drawable) point and not a line.
[linesInProcess removeAllObjects]; //Avoid adding a new line while performing the tap gesture
if (self.selectedLine)
NSLog(@"setting up delete menu");
[self becomeFirstResponder];
menu= [UIMenuController sharedMenuController];
// create a "Delete" menu item
deleteItem= [[UIMenuItem alloc] initWithTitle:@"Delete" action:@selector(deleteLine:)];
[menu setMenuItems:@[deleteItem]];
// Tell the menu where it should come from and show it.
[menu setTargetRect:CGRectMake(p.x, p.y, 2, 2) inView:self];
[menu setMenuVisible:YES animated:YES];
else
// Hide the menu if no line is selected
[menu setMenuVisible:NO animated:YES];
[self setNeedsDisplay];
NSLog(@"test");
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
NSLog(@"%@",NSStringFromSelector(_cmd));
// Silver challenge
selectedLine=nil;
menu=nil
int i=0;
for (UITouch *touch in touches)
NSLog(@"%dth iteration using touch- %@",++i,touch);
// Use the touch object [packed in an NSValue] as the key
NSValue *key= [NSValue valueWithNonretainedObject:touch];
NSLog(@"key-%@ touch-%@",key,touch);
// Create a line for the value
CGPoint loc= [touch locationInView:self];
Line *newLine= [[Line alloc] initWithLocation:loc];//Line to be drawn..Plus, setting begin and end pts. happens here.
NSLog(@"new line- %@",newLine);
// put pair in dictionary
[linesInProcess setObject:newLine forKey:key];
- (void)drawRect:(CGRect)rect
NSLog(@"%@",NSStringFromSelector(_cmd));
// Drawing code
CGContextRef context= UIGraphicsGetCurrentContext();
CGContextSetLineCap(context, kCGLineCapRound);//style of the line-endpoints
NSLog(@"delete menu-%@",menu);
// Draw complete lines in black
[[UIColor blackColor] set];
for (Line *line in self.rootObj.completeLines)
NSLog(@"Iterating over array");
NSLog(@"complete line- %@",line);
NSLog(@"begin pt.- (%f, %f) and end pt.- (%f, %f)",[line begin].x,[line begin].y,[line end].x,[line end].y);
CGContextMoveToPoint(context, [line begin].x, [line begin].y);
CGContextAddLineToPoint(context, [line end].x, [line end].y);
CGContextStrokePath(context);
// Draw lines in process in red..
[[UIColor redColor] set];
for (NSValue *v in linesInProcess)
NSLog(@"Iterating over dictionary");
Line *line= [linesInProcess objectForKey:v];
NSLog(@"line being drawn- %@",line);
CGContextMoveToPoint(context, [line begin].x, [line begin].y);
CGContextAddLineToPoint(context, [line end].x,[line end].y);
CGContextStrokePath(context);
NSLog(@"selected line: %@",self.selectedLine);
// Change the color of selected line. Redraw that line as green colored.
if (selectedLine)
NSLog(@"reseting color");
[[UIColor greenColor] set];
CGContextMoveToPoint(context, [selectedLine begin].x, [selectedLine begin].y);
CGContextAddLineToPoint(context, [selectedLine end].x, [selectedLine end].y);
CGContextStrokePath(context);
NSLog(@"drawing ended");
我正在通过 Big Nerd Ranch Guide 解决这个问题。在 touchesBegan: 和 tap: 方法中,使用了 'menu'。它是一个 ivar。请注意 touchesBegan: 没有调用 setNeedsDisplay。这只是一个代码 sn-p。在实际程序中,setNeedsDisplay 已从其他方法(如 touchesMoved: 和 touchesEnded:)调用。我只是想知道为什么视图上的菜单会保留,当我在 touchesBegan: 方法中通过将其指针设置为 nil 来破坏它时。
在选择第一行以显示删除菜单期间和之后的控制流程是这样的:
-
点击消息传递,菜单对象被创建,所有相关设置都完成。
操作:- 第一行被选中,删除菜单显示在其顶部。
操作:- 然后(保留第一行的删除菜单),正在绘制第二行。
touchesBegan:消息被传递到视图并且菜单指针设置为 nil。
tap 方法可能会被调用,也可能不会被调用,如果调用了,则 if 块不会运行,因此永远不会重新实例化“菜单”。
最终drawRect:消息传递,实现所有线条和设置。
第一行的删除菜单保持原样。 (这就是问题所在)
【问题讨论】:
【参考方案1】:由于您的控制器是单例,它可能有一个 strong
引用作为其自身实例的 static
,并且该实例是在您调用 sharedInstance
时返回给您的指针的内容。
(我假设sharedMenuController
方法是)
+ (id)sharedMenucontroller
static UIMenuController *sharedMenuController = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
sharedMenuController = [[self alloc] init];
);
return sharedMyManager;
这意味着 arc 不会通过将指向该单例的静态变量的指针设置为 nil 来清除它。
如果你只是想隐藏它,调用setVisible:NO
就可以了,是最简单的方法。
【讨论】:
“什么类型”是什么意思?是 UIMenucontroller 指针...? UIMenuController 内部的静态实现。我不明白。 我已经解决了将 setVisible: 消息传递给菜单对象。但这对我来说似乎不合逻辑:当你放开所谓的唯一一个强指针(菜单)时,如何不破坏对象? 是的,我很抱歉问“什么类型”。我的意思是我找不到仍然指向该对象的 UIMenucontroller 指针。是的,代码很有意义。 sharedMenuController(是一个静态的)将永久指向该对象,直到应用程序被杀死。谢谢。 这是单例的权衡。在 sharedMenuController 被调用一次后,UIMenuController 会保留它自己的一个实例。可能有一些方法可以使 arc 用于释放这些实例,但您不能指望这一点,您必须保护您的代码。尽管如此,在指向单例时将变量设置为 nil 仍然是一个好习惯,如果确实存在这些 arc 方法,则保持对该单例的强引用将使其不会被清理。 我猜想将 menu var 设置为 nil 会降低保留计数(menu 也是一个强指针)。所以在这样做的过程中,即使 UIMenuController 没有被破坏,我们仍然保持低内存占用。我在这里可能大错特错。请协助。我对此比较陌生。以上是关于即使在指针设置为 nil 后 UIMenuItem 仍会继续显示的主要内容,如果未能解决你的问题,请参考以下文章
free() 在 c 代码和代码中收集垃圾值即使在释放后将指针设置为 NULL 也不起作用
Objective-C在ARC下,把strong指针设置为nil,请问这样不会不释放指针的内存空间?