【问题标题】:A strange type casting in Objective-cObjective-c 中的一种奇怪的类型转换
【发布时间】:2012-12-18 03:47:00
【问题描述】:

我找到了一个演示 sn-ps,它使用了这样的类型转换:(int)view.'view' 是 UIView 对象的指针。我从来不知道它可以用来转换类型。有人可以帮我解释一下吗? 在此处粘贴代码

- (CGPoint)accelerationForView:(UIView *)view
{
    // return
    CGPoint accelecration;

    // get acceleration
    NSValue *pointValue = [self._accelerationsOfSubViews objectForKey:
                                     [NSNumber numberWithInteger:(int)view]];
    if (pointValue == nil) {
        accelecration = CGPointZero;
    }
    else {
        [pointValue getValue:&accelecration];
    }

    return accelecration;
}

- (void)willRemoveSubview:(UIView *)subview
{
    [self._accelerationsOfSubViews removeObjectForKey:
                         [NSNumber numberWithInt:(int)subview]];
}

【问题讨论】:

  • 这些演示 sn-ps 在哪里?
  • 你必须发布代码。
  • 您需要在此处显示实际的 sn-p(s) 以供检查。从表面上看,这样做没有充分的理由(甚至没有很多不好的理由)。
  • @BenZotto: sizeof(int) 不能保证等于sizeof(id),所以就是这样。事实上,在 64 位 Mac 上,它明确不是
  • 他们显然使用视图的地址作为键。不稳定,在给定 64 位地址的情况下不一定可靠,但在技术上是合法的。

标签: objective-c ios casting uikit


【解决方案1】:
[NSNumber numberWithInteger:(int)view]

view 不是UIView 类型的对象,它是UIView* 类型的指针。上面的代码将指针转换为 int 以便将其存储在 NSNumber 中,显然这样它就可以用作字典中的键。由于指针本身不是对象,因此您不能将它们用作字典键。但是,如果您从指针创建NSNumber 的实例,则可以使用生成的对象作为键。人们有时会做这样的事情来跟踪他们想要与一些未存储在对象本身中的对象(如视图)相关联的信息(如加速度)。

正如我在下面的评论中提到的,这里的代码使用+numberWithInteger:,这很好,因为该方法采用NSInteger,在 32 位系统上为 32 位,在 64 位系统上为 64 位系统。然而,作者随后通过强制转换为int 取消了这个良好的决定,即使在 64 位系统上通常也是 32 位。演员应该真的是NSInteger,像这样:

[NSNumber numberWithInteger:(NSInteger)view]

【讨论】:

  • 这很危险。看起来原始工程师正在尝试使用对对象的弱引用作为字典的键。问题是,如果关键对象(视图)被释放,最终会导致有效的内存泄漏。如果在内存中的同一点分配了另一个视图,那么您将拥有不应该与之关联的数据。更不用说sizeof(id) 并不总是等于sizeof(int),这意味着演员可以丢弃重要的位。
  • @JonathanGrynspan 我不知道我是否会称其为 dangerous - 视图不会自行释放。只要字典跟踪的视图由单个对象管理,例如包含视图或视图控制器,我认为这不是一件可怕的事情。不过,同意您的第二点——假设指针与int 的大小相同,这是一个糟糕的计划。原作者使用+numberWithInteger: 的想法是正确的,因为NSInteger 更接近于指针的大小,但转换应该是NSInteger 而不是int
  • 如果要使用弱键映射到对象,请看 NSHashMap
  • 另外,如果要封装对象指针,请使用+[NSValue valueWithPointer:]
  • @nielsbot 这就是人们喜欢 Lisp 的原因。只有一种数据结构,您不会有选择错误的危险。 ;-)
【解决方案2】:

(注意:这是基于 @Caleb 的回答,假设原始代码试图将 acceleration 值与 UIView 关联)

我会通过类别向 UIView 添加加速属性,如下所示:

UIView+acceleration.h:

@interface UIView ( Acceleration )
@property ( nonatomic ) CGPoint acceleration ;
@end

UIView+acceleration.m

#import <objc/runtime.h>

@implementation UIView ( Acceleration )

const char * kAccelerationKey = "acceleration" ; // should use something with a prefix just in case

-(void)setAcceleration:(CGPoint)acceleration
{
    objc_setAssociatedObject( self, kAccelerationKey, [ NSValue valueWithCGPoint:acceleration ], OBJC_ASSOCIATION_RETAIN_NONATOMIC ) ;
}

-(CGPoint)acceleration
{
    return [ objc_setAssociatedObject( self, kAccelerationKey ) CGPointValue ] ;
}

@end

删除-accelerationForView:-willRemoveSubview: 并使用view.acceleration = &lt;some point&gt;&lt;some point&gt; = view.acceleration

【讨论】:

  • 根据编写代码的时间,原作者可能没有该选项。此外,将其添加到UIView 的一个缺点是,您的应用程序中的每个视图都会有一个加速,这可能是矫枉过正。更好的方法可能是创建一个跟踪加速的 UIView 子类。然后,您可以将其用作任何其他视图的容器。
  • 我更喜欢组合而不是子类化。这里的重点是将acceleration显式添加到系统中的所有UIView,这样您就不必用子类替换所有UIViews。 (这只是普通的旧UIView。)资源消耗几乎没有,大约等于OP的当前代码。 2 种方法(每个进程存在一次),零存储,除了那些已设置 acceleration 的视图。
猜你喜欢
  • 2017-08-29
  • 2015-06-14
  • 1970-01-01
  • 1970-01-01
  • 2014-10-23
  • 2011-10-07
  • 2014-02-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多