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

时间:2014-03-16 19:10:09

标签: objective-c visibility categories ivar

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

例如,假设我有一个名为“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

最好不要暴露这个。

3 个答案:

答案 0 :(得分:5)

一般来说,类别无法访问ivars;从类扩展中合成的ivars和ivars在主要实现之外是私有的和不可见的。

但是,您可以通过在其自己的私有标头中的扩展名中声明ivar并将该标头导入类别的文件中来执行您想要的操作。请务必将私有标头导入到类的主要实现文件中。

答案 1 :(得分:3)

谁告诉过你编译器不会知道Person _friends? 它知道。只需在班级_friends中声明@interface,而不是在扩展程序中声明。

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

其他对象无法访问@protected _friends

答案 2 :(得分:0)

如果你有很多协议,代理人,数据源等等。 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