문제

Hello all, I've been working on some enumeration routines in Objective-C that perform object introspection. In particular, I'm fast enumerating an NSSet and making sure that the objects therein belong to class BBBallView (an fairly unfortunate name, I agree) before tweaking their properties and/or calling their methods.

In order to make the parser and compiler happy, however, I end up casting the object to its class on every single line; moreover, in order to access its properties, the object's cast has to be in parentheses, otherwise dot notation won't work. This leads to somewhat messy code:

for (id otherBall in self.gameField.subviews) {
    if ([otherBall isKindOfClass:[BBBallView class]]) {
        if ( !((BBBallView *)otherBall).isEnlarged ) {
            CGRect otherFrame = ((BBBallView *)otherBall).frame;
            /* ... */
        }
    }
}

Is there any way to tell the compiler something like "at this point I know that otherBall is a BBBallView, so stop telling me it doesn't respond to these selectors and properties"? That way, one could just write:

for (id otherBall in self.gameField.subviews) {
    if ([otherBall isKindOfClass:[BBBallView class]]) {
        if ( !otherBall.isEnlarged ) {
            CGRect otherFrame = otherBall.frame;
            /* ... */
        }
    }
}

and so on.

I tried otherBall = (BBBallView *)otherBall but "fast enumeration variables can't be modified in ARC by default". Changing the enumeration variable to __strong id fixes it, but doesn't help the fact that any subsequent line gives out errors such as "property isEnlarged not found on object of type 'const __strong id'", so I'm back to square one.

I'm not even sure why exactly this happens: shouldn't the compiler stay out of the way when an variable is of type id? In any case, the whole ordeal particularly messy in methods that need to perform several calculations on objects' properties, as it quickly becomes unreadable with all those parentheses.

Is there any way around this?

Thanks in advance!

도움이 되었습니까?

해결책

You can either create a local temp with the correct type or just not use dot notation.

  1. local temp

    for (UIView *view in self.gameField.subviews) {
      if ([view isKindOfClass:[BBBallView class]]) {
        BBBallView *ballView = view;
        if (!ballView.isEnlarged) {
          CGRect otherFrame = ballView.frame;
          /* ... */
        }
      }
    }
    
  2. don't use dot notation

    for (id otherBall in self.gameField.subviews) {
      if ([otherBall isKindOfClass:[BBBallView class]]) {
        if ( ![otherBall isEnlarged]) {
          CGRect otherFrame = [otherBall frame];
          /* ... */
        }
      }
    }
    

If I was only doing a couple of things I would be tempted to not use dot notation. If it became awkward to read and there was a lot of accesses then I would consider local temp

다른 팁

I think you're struggling because the logic appears misplaced. The language is fighting you because of your program's structure, not because the language is deficient.

Is the parent type of subviews really appropriate? Or are you violating the Liskov Substitution Principle by stuffing a round object in a square type?

You can also ask if is it really appropriate to examine the internals of BBBallView logic from outside? Can you move the logic into the appropriate class?

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top