我的情况似乎需要将实例变量添加到类别中,但我从Apple的文档中知道我不能这样做。所以我想知道最好的选择或解决方法是什么。
我想要做的是添加一个为UIViewControllers添加功能的类别。我会发现它在我所有不同的UIViewControllers中都很有用,无论它们扩展了什么特定的UIViewController子类,所以我认为一个类别是最好的解决方案。为了实现这个功能,我需要几种不同的方法,我需要跟踪它们之间的数据,这就是我想要创建实例方法的原因。
如果它有用,这就是我特别想做的事情。我希望能够更容易地跟踪软件键盘何时隐藏和显示,以便我可以在视图中调整内容的大小。我发现可靠的唯一方法是将代码放在四个不同的UIViewController方法中,并跟踪实例变量中的额外数据。所以那些方法和实例变量是我想要放入一个类别的,所以每次我需要处理软件键盘时都不需要复制它们。 (如果对于这个确切的问题有一个更简单的解决方案,那也没关系 - 但我仍然想知道类别实例变量的答案以供将来参考!)
答案 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
上述方法存在两个潜在问题:
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
请参阅此处获取完整教程:
答案 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。