Objective C类别的实例变量

时间:2010-11-10 15:39:48

标签: objective-c cocoa oop categories

我的情况似乎需要将实例变量添加到类别中,但我从Apple的文档中知道我不能这样做。所以我想知道最好的选择或解决方法是什么。

我想要做的是添加一个为UIViewControllers添加功能的类别。我会发现它在我所有不同的UIViewControllers中都很有用,无论它们扩展了什么特定的UIViewController子类,所以我认为一个类别是最好的解决方案。为了实现这个功能,我需要几种不同的方法,我需要跟踪它们之间的数据,这就是我想要创建实例方法的原因。

如果它有用,这就是我特别想做的事情。我希望能够更容易地跟踪软件键盘何时隐藏和显示,以便我可以在视图中调整内容的大小。我发现可靠的唯一方法是将代码放在四个不同的UIViewController方法中,并跟踪实例变量中的额外数据。所以那些方法和实例变量是我想要放入一个类别的,所以每次我需要处理软件键盘时都不需要复制它们。 (如果对于这个确切的问题有一个更简单的解决方案,那也没关系 - 但我仍然想知道类别实例变量的答案以供将来参考!)

8 个答案:

答案 0 :(得分:28)

是的,你可以这样做,但既然你在问,我不得不问:你绝对确定你需要吗? (如果你说“是”,那就回去看看你想做什么,看看是否有不同的做法)

但是,如果您真的想将存储注入您无法控制的类中,请使用associative reference

答案 1 :(得分:14)

最近,我需要这样做(将状态添加到类别)。 @Dave DeLong对此有正确的看法。在研究最佳方法时,我找到了Tom Harrington的一个很棒的blog post。我喜欢@ JeremyP关于在类别上使用@property声明的想法,但不喜欢他的特定实现(不是全局单例或持有全局引用的粉丝)。联想参考是可行的方法。

这是为您的类别添加(似乎是)ivars的代码。我在博客中详细介绍了这一点here

在File.h中,调用者只能看到干净的高级抽象:

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

在File.m中,我们可以实现@property(注意:这些不能@ synthesize'd):

@implementation UIViewController (MyCategory)

- (NSUInteger)someObject
{
  return [MyCategoryIVars fetch:self].someObject;
}

- (void)setSomeObject:(NSUInteger)obj
{
  [MyCategoryIVars fetch:self].someObject = obj;
}

我们还需要声明和定义类MyCategoryIVars。为了便于理解,我已经从正确的编译顺序中解释了这一点。 @interface需要放在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

上面的代码声明并实现了保存我们的ivars(someObject)的类。因为我们无法真正扩展UIViewController,所以必须这样做。

答案 2 :(得分:6)

我相信现在可以将合成属性添加到类别中,并且实例变量是自动创建的,但我从未尝试过,所以我不确定它是否可行。

一个更黑客的解决方案:

创建一个单独的NSDictionary,它将UIViewController作为键(或者将其地址包装为NSValue),并将属性的值作为其值。

为实际进入字典的属性创建getter和setter以获取/设置属性。

@interface UIViewController(MyProperty)

@property (nonatomic, retain) id myProperty;
@property (nonatomic, readonly, retain) NSMutableDcitionary* propertyDictionary;

@end

@implementation  UIViewController(MyProperty)

-(NSMutableDictionary*) propertyDictionary
{
    static NSMutableDictionary* theDictionary = nil;
    if (theDictionary == nil)
    {
        theDictioanry = [[NSMutableDictionary alloc] init];
    }
    return theDictionary;
}


-(id) myProperty
{
    NSValue* key = [NSValue valueWithPointer: self];
    return [[self propertyDictionary] objectForKey: key];
}

-(void) setMyProperty: (id) newValue
{
    NSValue* key = [NSValue valueWithPointer: self];
    [[self propertyDictionary] setObject: newValue forKey: key];    
}

@end

上述方法存在两个潜在问题:

  • 无法删除已解除分配的视图控制器的键。只要您只跟踪少数,这应该不是问题。或者,一旦您知道完成该键,就可以添加一种从字典中删除键的方法。
  • 我不是100%确定NSValue的isEqual:方法比较内容(即包装指针)以确定相等性,或者它是否仅仅比较self以查看比较对象是否是完全相同的NSValue。如果是后者,则必须使用NSNumber而不是NSValue作为密钥(NSNumber numberWithUnsignedLong:将在32位和64位平台上执行此操作。)

答案 3 :(得分:6)

最好使用内置的ObjC功能关联对象(也称为关联引用),在下面的示例中,只需更改为您的类别,并使用您的变量名替换associatedObject。

<强> NSObject的+ AssociatedObject.h

arma::cube(dim1, dim2, dim3)

<强> NSObject的+ AssociatedObject.m

@interface NSObject (AssociatedObject)
@property (nonatomic, strong) id associatedObject;
@end

请参阅此处获取完整教程:

http://nshipster.com/associated-objects/

答案 4 :(得分:3)

在许多文档的网上都提到,你不能在类别中创建创建新变量,但我找到了一种非常简单的方法来实现它。这是让我们在类别中声明新变量的方法。

在你的.h文件中

@interface UIButton (Default)

   @property(nonatomic) UIColor *borderColor;

@end  

在你的.m文件中

#import <objc/runtime.h>
static char borderColorKey;

@implementation UIButton (Default)

- (UIColor *)borderColor
{
    return objc_getAssociatedObject(self, &borderColorKey);
}

- (void)setBorderColor:(UIColor *)borderColor
{
    objc_setAssociatedObject(self, &borderColorKey,
                             borderColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    self.layer.borderColor=borderColor.CGColor;
}

@end

现在你有了新的变量。

答案 5 :(得分:0)

为什么不简单地创建UIViewController的子类,为其添加功能,然后使用该类(或其子类)?

答案 6 :(得分:0)

根据您的操作,您可能需要使用静态类别方法。

所以,我认为你遇到了这样的问题:

ScrollView中有几个短信。文本编辑时的用户类型,您想要滚动滚动视图,以便在键盘上方显示文本编辑。

+ (void) staticScrollView: (ScrollView*)sv scrollsTo:(id)someView
{
  // scroll view to someviews's position or some such.
}

从此返回并不一定要求视图向后移动,因此不需要存储任何内容。

但是,如果没有代码示例,我就能想到这一切,抱歉。

答案 7 :(得分:0)

我相信可以使用Obj-C运行时向类中添加变量 我也发现了this discussion