Подтвердить что ты не робот

Переменные экземпляра для объектов Objective C

У меня есть ситуация, когда мне кажется, что мне нужно добавить переменные экземпляра в категорию, но я знаю из документов Apple, что я не могу этого сделать. Поэтому мне интересно, какая лучшая альтернатива или обходной путь.

Что я хочу сделать, так это добавить категорию, которая добавит функции UIViewControllers. Я бы счел это полезным во всех моих разных UIViewControllers, независимо от того, какой конкретный подкласс UIViewController они расширяют, поэтому я считаю, что категория является лучшим решением. Чтобы реализовать эту функциональность, мне нужно несколько разных методов, и мне нужно отслеживать данные между ними, так что это заставило меня хотеть создавать методы экземпляра.

В случае, если это полезно, вот то, что я специально хочу сделать. Я хочу, чтобы было легче отслеживать, когда клавиатура программного обеспечения скрывает и показывает, так что я могу изменить размер содержимого в моем представлении. Я нашел, что единственный способ сделать это надежно - поставить код в четыре разных метода UIViewController и отслеживать дополнительные данные в переменных экземпляра. Поэтому эти методы и переменные экземпляра - это то, что я хотел бы поместить в категорию, поэтому мне не нужно копировать их каждый раз, когда мне нужно обрабатывать программную клавиатуру. (Если есть более простое решение для этой точной проблемы, это тоже хорошо, но мне все же хотелось бы узнать ответ на переменные экземпляра категории для дальнейшего использования!)

4b9b3361

Ответ 1

Да, вы можете это сделать, но, поскольку вы спрашиваете, я должен спросить: вы абсолютно уверены, что вам нужно? (Если вы скажете "да", вернитесь назад, выясните, что вы хотите сделать, и посмотрите, есть ли другой способ сделать это)

Однако, если вы действительно хотите добавить хранилище в класс, который вы не контролируете, используйте ассоциативную ссылку .

Ответ 2

Недавно мне нужно было сделать это (добавьте состояние в категорию). @Dave DeLong имеет правильную точку зрения на это. Изучая лучший подход, я нашел замечательный пост в блоге от Тома Харрингтона. Мне нравится идея @JeremyP использовать объявления @property в категории, но не его конкретную реализацию (не поклонник глобального синглтона или проведение глобальных ссылок). Ассоциативные ссылки - это путь.

Здесь код, который нужно добавить (то, что кажется) ivars в вашу категорию. Я подробно рассказал об этом здесь.

В 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, это должно будет сделать.

Ответ 3

Я считаю, что теперь можно добавить синтезированные свойства в категорию, и переменные экземпляра автоматически создаются, но я никогда не пробовал, поэтому я не уверен, что это сработает.

Более хакерское решение:

Создайте однострочный 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%, что метод isEqual: NSValue сравнивает контент (т.е. обернутый указатель) для определения равенства или если он просто сравнивает себя, чтобы увидеть, является ли объект сравнения тем же самым NSValue. Если последнее, вам придется использовать NSNumber вместо NSValue для ключей (NSNumber numberWithUnsignedLong: выполнит трюк как на 32-битной, так и на 64-битной платформах).

Ответ 4

Это лучше всего достигается с помощью встроенной функции ObjC Associated Objects (aka Associated References), в приведенном ниже примере просто измените вашу категорию и замените связанный объект с вашим именем переменной.

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/

Ответ 5

Во многих документах он упоминал, что вы не можете создать новую переменную в категории, но я нашел очень простой способ добиться этого. Вот как можно объявить новую переменную в категории.

В вашем .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

Теперь у вас есть новая переменная.

Ответ 6

Почему бы просто не создать подкласс UIViewController, добавить к нему функциональность, а затем использовать этот класс (или его подкласс)?

Ответ 7

В зависимости от того, что вы делаете, вы можете использовать методы статической категории.

Итак, я предполагаю, что у вас есть такая проблема:

ScrollView имеет пару текстовых элементов. Типы пользователей при редактировании текста, вы хотите прокрутить прокрутку, чтобы текст редактировался над клавиатурой.

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

возврат из этого необязательно требует, чтобы представление возвращалось назад, и поэтому ему ничего не нужно хранить.

Но все, что я могу представить без примеров кода, извините.

Ответ 8

Я считаю, что добавлять переменные в класс можно с помощью среды выполнения Obj-C.
Я нашел эту дискуссию.