【问题标题】:setting new properties in category interface/implementation在类别界面/实现中设置新属性
【发布时间】:2010-11-04 16:16:46
【问题描述】:

好的,我有这个,但它不会工作:

@interface UILabel (touches)

@property (nonatomic) BOOL isMethodStep;

@end


@implementation UILabel (touches)

-(BOOL)isMethodStep {
    return self.isMethodStep;
}

-(void)setIsMethodStep:(BOOL)boolean {
    self.isMethodStep = boolean;
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    if(self.isMethodStep){
        // set all labels to normal font:
        UIFont *toSet = (self.font == [UIFont fontWithName:@"Helvetica" size:16]) ? [UIFont fontWithName:@"Helvetica-Bold" size:16] : [UIFont fontWithName:@"Helvetica" size:16];

        id superView = self.superview;
        for(id theView in [(UIView *)superView subviews])
            if([theView isKindOfClass:[UILabel class]])
                [(UILabel *)theView setFont:[UIFont fontWithName:@"Helvetica" size:16]];

        self.font = toSet;
    }
}

@end

如果我取出 getter 和 setter 方法,那么它不起作用,它告诉我需要创建一些 getter 和 setter 方法(或使用 @synthesize - 但将 @synthesize 放入 @implementation 也会引发错误)。但是使用 getter 和 setter 方法,我得到一个 EXC_BAD_ACCESS 和一个崩溃。有任何想法吗?谢谢

汤姆

【问题讨论】:

  • 这个家伙 'Dave DeLong' 发布了一个你应该检查的答案......这可能是做你想做的事情的“正确”方式...... :) here's the link...
  • 关联参考是解决方案...在这里找到很好的教程techpaa.com/2012/04/adding-properties-to-categories-and.html
  • 另外,您的 isMethodStep getter 是一个无限递归函数。这就是您遇到 EXC_BAD_ACCESS 崩溃的原因。 self.isMethodStep 等价于[self isMethodStep]

标签: objective-c ios categories


【解决方案1】:

无法通过类别向现有类添加成员和属性——只能通过方法。

https://developer.apple.com/library/content/documentation/General/Conceptual/DevPedia-CocoaCore/Category.html

一种可能的解决方法是编写“setter/getter-like”方法,该方法使用单例来保存本来是成员的变量。

-(void)setMember:(MyObject *)someObject
{
    NSMutableDictionary *dict = [MySingleton sharedRegistry];
    [dict setObject:someObject forKey:self];
}

-(MyObject *)member
{
    NSMutableDictionary *dict = [MySingleton sharedRegistry];
    return [dict objectforKey:self];
}

或者——当然——编写一个继承自 UILabel 的自定义类


请注意,现在可以在运行时注入关联对象The Objective C Programming Language: Associative References

【讨论】:

  • "或者——当然——编写一个继承自 UILabel 的自定义类"会少得多。 :) 坏消息是吗?啊……嗯,谢谢。 :)
  • 你应该使用 [NSValue valueWithPointer:self] 作为键,而不是 self。
  • @Dustin:正如我在编辑中指出的那样,现在没有必要再使用这个古老的代码了——如果伪造属性是明智的。感谢您的反对。
  • 避免关联对象。格式不好。
  • @ZaBlanc,我同意。实际上我从未使用过它们。但问题是关于类别中的属性。比他们应该被提及。
【解决方案2】:

检查了所有答案,没有找到最常见的解决方案:

#import <objc/runtime.h>

static void const *key;

@interface ClassName (CategoryName)
@property (nonatomic) BOOL myProperty;
@end

@implementation ClassName (CategoryName)
- (BOOL)myProperty {
    return [objc_getAssociatedObject(self, key) boolValue];
}

- (void)setMyProperty:(BOOL)value {
    objc_setAssociatedObject(self, key, @(value), OBJC_ASSOCIATION_RETAIN);
}
@end

迅速:

private struct AssociatedKeys {
    static var keyName = "keyName"
}

extension Foo {
    var bar: Any! {
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.keyName)
        }
        set {
            objc_setAssociatedObject(self, &AssociatedKeys.keyName , newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
}

【讨论】:

  • 完美解决方案!谢谢!
【解决方案3】:

实际上有一种方法,可能并不理想,但确实有效。
要使其工作,您需要为类 X 创建一个类别,并且只能用于同一 X 的 子类(例如类别 UIView (Background) 可以与类 MyView : UIView 一起使用,但是不直接用UIView)

// UIView+Background.h

@interface UIView (Background)

@property (strong, nonatomic) NSString *hexColor;

- (void)someMethodThatUsesHexColor;

@end

// UIView+Background.h

@implementation UIView (Background)

@dynamic hexColor; // Must be declared as dynamic

- (void)someMethodThatUsesHexColor {
    NSLog(@"Color %@", self.hexColor);
}

@end

然后

// MyView.m

#import "UIView+Background.h"

@interface MyView : UIView

@property (strong, nonatomic) NSString *hexColor;

@end

@implementation MyView ()

- (void)viewDidLoad {
    [super viewDidLoad];
    [self setHexColor:@"#BABACA"];
    [self someMethodThatUsesHexColor];
}

@end

使用此方法,您需要“重新声明”您的属性,但之后,您可以在您的类别中进行所有操作。

【讨论】:

    【解决方案4】:

    您可以在运行时注入关联的对象。

    #import <objc/runtime.h>
    
    @interface UIView (Private)
    
    @property (nonatomic, assign) CGPoint initialTouchPoint;
    @property (nonatomic, strong) UIWindow *alertWindow;
    
    @end
    
    @implementation UIView (Private)
    
    @dynamic initialTouchPoint, alertWindow;
    
    - (CGPoint)initialTouchPoint {
        return CGPointFromString(objc_getAssociatedObject(self, @selector(initialTouchPoint)));
    }
    
    - (void)setInitialTouchPoint:(CGPoint)initialTouchPoint {
        objc_setAssociatedObject(self, @selector(initialTouchPoint), NSStringFromCGPoint(initialTouchPoint), OBJC_ASSOCIATION_RETAIN);
    }
    
    - (void)setAlertWindow:(UIWindow *)alertWindow {
        objc_setAssociatedObject(self, @selector(alertWindow), alertWindow, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    
    - (UIWindow *)alertWindow {
        return objc_getAssociatedObject(self, @selector(alertWindow));
    }
    
    @end
    

    【讨论】:

      【解决方案5】:

      编辑:警告:对于类的所有实例,此属性将具有唯一值。

      这对我有用,但只是因为我的应用中只有一个此类的实例。

      #import <AVFoundation/AVFoundation.h>
      
      @interface AVAudioPlayer (AstroAVAudioPlayer)
      
      @property (nonatomic) BOOL redPilot;
      
      @end
      
      
      #import "AVAudioPlayer+AstroAVAudioPlayer.h"
      
      @implementation AVAudioPlayer (AstroAVAudioPlayer)
      
      BOOL _redPilot;
      
      -(void) setRedPilot:(BOOL)redPilot
      {
          _redPilot = redPilot;
      }
      
      -(BOOL) redPilot
      {
          return _redPilot;
      }
      
      @end
      

      【讨论】:

      • 这个答案应该被标记为不正确。上面的实现只为 AVAudioPlayer 的所有实例分配了一个名为 _redPilot 的属性。换句话说,如果你有三个 AVAudioPlayer 实例,那么 avAudioPlayer1.redPilot == avAudioPlayer2.redPilot == avAudioPlayer3.redPilot 总是。
      • @nobular,你说得对,先生。事实上,在我的情况下,我只有一个 AVAudioPlayer 实例,所以我并不太在意。无论如何,我在这里回答了这个问题,因为我认为这可能对某人有所帮助,例如像我这样的人。我会编辑答案,以便对上述问题发出警告。
      • 这是我过去用来解决类别属性缺失问题的一种技术:techpaa.com/2012/04/adding-properties-to-categories-and.html
      • 就我而言,这是一个完美的解决方案。我正在尝试将 NSDateFormatter 添加到 NSDate 类别(也许我疯了),以便在我的所有 NSDate 对象中只有一个格式化程序实例。你怎么看?
      • @Floydian,你知道,有句话说:“这不是一个错误。这是一个功能。” :) 因此,如果您想要这样的功能,请选择它,如果它适合您。
      【解决方案6】:

      我发现的一个解决方案是给每个你想要标记的对象一个唯一的标签。

      我创建了一个 UILabel 类别来为我的所有标签添加自定义字体,但有些我希望它们是粗体的,所以我这样做了 ->

      - (void) layoutSubviews {
          [super layoutSubviews];
          [self addCustomFont];   
      }
      
      - (void) addCustomFont {
          if (self.tag == 22) {
              [self setFont:[UIFont fontWithName:SEGOE_BOLD size:self.font.pointSize]];
          }else{
              [self setFont:[UIFont fontWithName:SEGOE_LIGHT size:self.font.pointSize]];
          }
      }
      

      【讨论】:

        【解决方案7】:

        似乎从 Xcode 7 (7.0.1, 7A1001) 开始,类别中支持属性。我注意到 Xcode 现在为 Core Data 子类生成类别。

        例如,我得到了文件:

        位置+CoreDataProperties.h

        #import "Location.h"
        
        NS_ASSUME_NONNULL_BEGIN
        
        @interface Location (CoreDataProperties)
        
        @property (nullable, nonatomic, retain) NSNumber *altitude;
        @property (nullable, nonatomic, retain) NSNumber *latitude;
        @property (nullable, nonatomic, retain) NSNumber *longitude;
        
        @end
        
        NS_ASSUME_NONNULL_END
        

        位置+CoreDataProperties.m

        #import "Location+CoreDataProperties.h"
        
        @implementation Location (CoreDataProperties)
        
        @dynamic altitude;
        @dynamic latitude;
        @dynamic longitude;
        
        @end
        

        所以看起来类别中的属性现在可以工作了。我没有在非Core Data 类上进行测试。

        我注意到他们确实将类别文件包含回原始类中:

        Location.h

        @interface Location : NSManagedObject
        
        @end
        
        #import "Location+CoreDataProperties.h"
        

        这允许原始类编辑类别指定的属性。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-04-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-03-11
          • 1970-01-01
          • 1970-01-01
          • 2012-01-11
          相关资源
          最近更新 更多