类别可以访问它扩展的类中定义的实例变量吗?

Posted

技术标签:

【中文标题】类别可以访问它扩展的类中定义的实例变量吗?【英文标题】: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。请这样做,而不是将其添加到问题正文中。如果您认为这是最好的,您也可以将您的答案标记为已接受(更改复选标记不会伤害我的感受或任何事情)。 【参考方案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

【讨论】:

以上是关于类别可以访问它扩展的类中定义的实例变量吗?的主要内容,如果未能解决你的问题,请参考以下文章

类方法和实例方法

将受保护的 Objective-C 实例变量公开给子类

JAVA中类中的实例方法可以操作类变量(static变量)吗?类方法(static方法)可以操作实例变量吗?

通过对象引用访问实例变量的静态嵌套类的 Java 示例 [重复]

Swift无法使用类型(实例)访问类中的静态变量

类总结