类别可以访问它扩展的类中定义的实例变量吗?
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
【讨论】:
以上是关于类别可以访问它扩展的类中定义的实例变量吗?的主要内容,如果未能解决你的问题,请参考以下文章
JAVA中类中的实例方法可以操作类变量(static变量)吗?类方法(static方法)可以操作实例变量吗?