2010-11-10 172 views
30

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

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

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

+5

你可以创建一个子类吗? – kennytm 2010-11-10 15:46:14

回答

0

为什么不简单地创建UIViewController的子类,为此,然后使用该类(或其子类)来代替?

+6

'UITableViewController'是'UIViewController'的子类。一个类将为两者添加功能,而一个子类则不会。 – 2010-11-10 15:50:33

+1

我怀疑OP遇到了他们无法控制的'UIViewController'子类。 – Justin 2010-11-10 15:51:56

+0

我明白你的观点。有些措辞让我觉得这些纯粹是OP自己的直接UIViewController子类。我怀疑KennyTM(根据他的评论)以同样的方式阅读。 :-) – 2010-11-10 16:01:58

0

根据你在做什么,你可能想要使用静态类别方法。

所以,我认为你有这样的问题:

滚动型有一对夫妇在他们textedits的。用户键入文本编辑时,您需要滚动滚动视图,以便在键盘上方显示文本编辑。

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

从这返回并不一定需要视图移回,因此它不需要存储任何东西。

但这就是我能想到没有代码示例,对不起。

0

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

+0

这是否使它成为iOS?我没有跟上这个新功能(它对我来说似乎太脏了:-))。 – 2010-11-10 16:05:30

7

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

更哈克的解决方案:

创建一个单独的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作为密钥(在32位和64位平台上,NSNumber numberWithUnsignedLong:都会起作用)。
+4

....或者你可以使用相关的参考文献 – 2010-11-10 17:28:09

26

是的,你可以做到这一点,但既然你问了,我必须问:你是否确定你需要? (如果你说“是”,然后回去,找出你想做什么,看看是否有不同的方式来做到这一点)

但是,如果你真的想注入存储到一个类, t控制,请使用associative reference

+0

我不知道关联参考,好的提示! – 2011-06-21 18:56:07

+4

新的关联参考链接:http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocAssociativeReferences.html – lilbyrdie 2011-06-24 17:55:30

+0

@lilbyrdie谢谢我修复了链接:) – 2011-06-24 18:00:01

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。为了便于理解,我已经从正确的编译顺序中解释了这一点。 @接口需要放在Category @实现之前。

@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,这将不得不做。

+1

在您的示例中实现,'-init'覆盖服务的目的是什么?这似乎是一个没有操作。谢谢! – Olie 2014-07-22 18:16:41

+1

@Olie,你是对的,这是不需要的。我倾向于用我的编码来强调一点OCD。由于有一个dealloc,我喜欢提供一个匹配的init来提醒自己在添加资源到对象时触及两者。你可以安全地消除它。 – 2014-07-23 19:17:50

+0

没问题。由于我刚接触'objc_setAssociatedObject()',我不确定是否存在某种奇怪的,令人满意的副作用。谢谢! – Olie 2014-07-23 22:44:55

5

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

NSObject的+ AssociatedObject.h

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

NSObject的+ AssociatedObject.m

#import <objc/runtime.h> 

@implementation NSObject (AssociatedObject) 
@dynamic associatedObject; 

- (void)setAssociatedObject:(id)object { 
    objc_setAssociatedObject(self, @selector(associatedObject), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 
} 

- (id)associatedObject { 
    return objc_getAssociatedObject(self, @selector(associatedObject)); 
} 

在这里看到完整的教程:

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

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 

这就是现在你有新的变量。