Instance Variables for Objective C Categories

前端 未结 8 1897
终归单人心
终归单人心 2020-12-02 15:43

I have a situation where it seems like I need to add instance variables to a category, but I know from Apple\'s docs that I can\'t do that. So I\'m wondering what the best a

8条回答
  •  渐次进展
    2020-12-02 16:18

    Recently, I needed to do this (add state to a Category). @Dave DeLong has the correct perspective on this. In researching the best approach, I found a great blog post by Tom Harrington. I like @JeremyP's idea of using @property declarations on the Category, but not his particular implementation (not a fan of the global singleton or holding global references). Associative References are the way to go.

    Here's code to add (what appear to be) ivars to your Category. I've blogged about this in detail here.

    In File.h, the caller only sees the clean, high-level abstraction:

    @interface UIViewController (MyCategory)
    @property (retain,nonatomic) NSUInteger someObject;
    @end
    

    In File.m, we can implement the @property (NOTE: These cannot be @synthesize'd):

    @implementation UIViewController (MyCategory)
    
    - (NSUInteger)someObject
    {
      return [MyCategoryIVars fetch:self].someObject;
    }
    
    - (void)setSomeObject:(NSUInteger)obj
    {
      [MyCategoryIVars fetch:self].someObject = obj;
    }
    

    We also need to declare and define the class MyCategoryIVars. For ease of understanding, I've explained this out of proper compilation order. The @interface needs to be placed before the Category @implementation.

    @interface MyCategoryIVars : NSObject
    @property (retain,nonatomic) NSUInteger someObject;
    + (MyCategoryIVars*)fetch:(id)targetInstance;
    @end
    
    @implementation MyCategoryIVars
    
    @synthesize someObject;
    
    + (MyCategoryIVars*)fetch:(id)targetInstance
    {
      static void *compactFetchIVarKey = &compactFetchIVarKey;
      MyCategoryIVars *ivars = objc_getAssociatedObject(targetInstance, &compactFetchIVarKey);
      if (ivars == nil) {
        ivars = [[MyCategoryIVars alloc] init];
        objc_setAssociatedObject(targetInstance, &compactFetchIVarKey, ivars, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        [ivars release];
      } 
      return ivars;
    }
    
    - (id)init
    {
      self = [super init];
      return self;
    }
    
    - (void)dealloc
    {
      self.someObject = nil;
      [super dealloc];
    }
    
    @end
    

    The above code declares and implements the class which holds our ivars (someObject). As we cannot really extend UIViewController, this will have to do.

提交回复
热议问题