【问题标题】:Can a category access instance variables defined in the class it extends?类别可以访问它扩展的类中定义的实例变量吗?
【发布时间】:2014-04-21 21:35:14
【问题描述】:

我知道尝试将属性放在一个类别中并不是一个好主意。我可以从扩展它的类别中访问类的实例变量吗?或者是否有必要在被扩展的类上公开一个访问器?

例如,假设我有一个名为“Person”的类,它的实现如下所示:

#import "Person.h"

@interface Person()
{
    NSMutableArray *_friends;
}
@end

@implementation Person

- (instancetype)init
{
    self = [super init];
    if (self) {
        _friends = [NSMutableArray array];
    }
    return self;
}

-(instancetype)initWithFirstname:(NSString *)firstName lastname:(NSString *)lastName
{
    self = [self init];
    if (self) {
        _firstName = firstName;
        _lastName = lastName;
    }
    return self;
}

-(NSString *)getFullName{
    return [NSString stringWithFormat:@"%@ %@", _firstName, _lastName];
}

@end

请注意 ivar _friends。假设(出于某种原因)我想将处理一个人的朋友的所有操作分成一个类别,如下所示:

#import "Person.h"

@interface Person (Friends)
-(NSArray *)getFriends;
-(void)addFriend:(Person *)person;
-(void)removeFriend:(Person *)person;
@end

Person(Friends) 类别中,编译器不会知道Person 的ivar _friends

//Person.h 

@interface Person
@property(nonatomic, strong) NSMutableArray *friends;
...
@end

最好不要暴露这个。

【问题讨论】:

  • “尝试将实例变量放在一个类别中并不是一个好主意” 这甚至是不可能的。类别不能声明存储,只能声明方法。您的代码似乎已经回答了您的问题。你能更清楚你在问什么吗?
  • @JoshCaswell 我指的是类别中的属性。这是可能的。对不起。我想知道一个类别应该如何访问成员数据。因此,从我的示例中,我希望能够从 Person 的“_friends”ivar 中添加/删除对象。但我宁愿不在 Person 公共标头中公开 _friends 。这有意义吗?
  • 您可以访问post your own answer。请这样做,而不是将其添加到问题正文中。如果您认为这是最好的,您也可以将您的答案标记为已接受(更改复选标记不会伤害我的感受或任何事情)。

标签: objective-c visibility categories ivar


【解决方案1】:

一般情况下,categories 不能访问 ivars;从类扩展合成的 ivars 和 ivars 是私有的,在主实现之外是不可见的。

但是,您可以通过在其私有标头中的扩展中声明 ivar 并将该标头导入到类别的实现文件中来做您想做的事情。确保还将私有头文件导入到类的主实现文件中。

【讨论】:

    【解决方案2】:

    谁告诉你编译器不会知道Person_friends? 它知道。只需在 @interface 类中声明 _friends,而不是在扩展中。

    @interface Person : NSObject
    {
    @protected
          NSMutableArray *_friends;
    }
    @end
    

    使用@protected _friends 将无法被其他对象访问。

    【讨论】:

    • 这是错误的;合成的 ivars 在类别中不可见。
    • @JoshCaswell,如果你在扩展中定义它们,那么是 - 不可见,如果你在类定义中定义它们,那么不 - 可见。
    • 没错,但是,public-@interface-declared ivars 不是好的做法,OP 明确表示他不想这样做。
    • 对。在我的帖子中,_friends 是在类延续中声明的。并且该 ivar 对类别不可见。我不想将 _friends 放入公共界面。它是 Person 内部的,不应被其他对象访问。
    • 这里有很多争吵——只要编码人员在暴露 的存在方面没有问题(不要与直接访问混淆),这个解决方案就可以工作 伊瓦尔。 @JoshCaswell 的解决方案可能更常见,但并不遥远—— ivars 仍然需要在@interface Person … 中公开,在.h 文件中。 JoshCaswell 的好处是,当它全部用于共享库时,更容易隐藏 .h 文件以防止其他项目访问,但每个人都有自己的。
    【解决方案3】:

    如果您有很多协议、委托、数据源等。 MainViewController 并且您想将他们的 回调 外包给单独的文件(类别),例如

    "MainViewController+DelegateCallbacks.h"
    "MainViewController+DelegateCallbacks.m"
    

    但同时仍希望能够从这些类别中访问所有控制器的私有 @properties,而不必在公共接口中公开它们

    "MainViewController.h"
    

    最优雅的解决方案仍然是在单独的头文件中创建一个私有接口(扩展),例如

    "MainViewController_PrivateInterface.h"
    

    但是 - 而不是 ivars - 就像 Josh Caswell 已经在上面解释的那样,将所有 @properties (这些外包代表需要访问)也在该扩展中。这样一来,您就可以将它们全部准私有隐藏起来,其他任何人都无法看到它们。最重要的是不在您的公共界面中!您甚至可以选择直接在代码中访问您的 @properties 的后备存储 ivars(而不是方便的点表示法),只需在此私有外部接口文件中手动创建相应的后备存储 ivars。只是不要忘记在您想访问这些 ivars 的任何地方导入您的私有接口标头(包括您的 MainViewController ;-)


    //
    //  MainViewController.m
    //
    
    #import "MainViewController.h"
    #import "MainViewController+DelegateCallbacks.h"
    
    #import "MainViewController_PrivateInterface.h"
    
    
    @interface MainViewController () <UICollectionViewDelegate,
                                      UICollectionViewDataSource,
                                      UICollectionViewDelegateFlowLayout,
                                      UIGestureRecognizerDelegate>
    
    #pragma mark - <UIGestureRecognizerDelegate>
    #pragma mark - <UIContentContainer>
    #pragma mark - <UITraitEnvironment>
    
    // etc.
    
    @end
    
    ------------------------------------------------------------------------
    
    
    //
    //  MainViewController+DelegateCallbacks.h
    //
    
    #import "MainViewController.h"
    
    
    @interface MainViewController (DelegateCallbacks)
    
    @end
    
    ------------------------------------------------------------------------
    
    //
    //  MainViewController+DelegateCallbacks.m
    //
    
    #import "MainViewController+DelegateCallbacks.h"
    #import "MainViewController_PrivateInterface.h"
    
    
    @implementation MainViewController (DelegateCallbacks)
    
    #pragma mark <UICollectionViewDataSource>
    #pragma mark <UICollectionViewDelegate>
    #pragma mark <UICollectionViewDelegateFlowLayout>
    
    // etc.
    
    @end
    
    ------------------------------------------------------------------------
    
    //
    //  MainViewController_PrivateInterface.h
    //
    
    #import "MainViewController.h"
    
    @interface MainViewController () {
        // NSMutableArray <NSArray *> *_myArray_1;
        // NSMutableArray <UIBezierPath *> *_myArray_2;
    }
    
    @property (strong, nonatomic) NSMutableArray <NSArray *> *myArray_1;
    @property (strong, nonatomic) NSMutableArray <UIBezierPath *> *myArray_2;
    
    @property (weak, nonatomic) IBOutlet MyView *myView;
    @property (weak, nonatomic) IBOutlet MyCollectionView *myCollectionView;
    
    @property (nonatomic) CGFloat myFloat;
    
    // etc.
    
    @end
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-01-14
      • 2013-09-04
      • 2010-09-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多