Can a category access instance variables defined in the class it extends?

拥有回忆 提交于 2019-12-10 14:56:14

问题


I know it's not a great idea to try and place properties in a category. Can I access a class' instance variables from within a category that extends it? Or is it necessary to expose an accessor on the class being extended?

For example, let's say I have a class called "Person" and its implementation looks like this:

#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

Notice the ivar _friends. Let's say (for some reason or other) I wanted to segregate all operations dealing with a person's friends into a category, like so:

#import "Person.h"

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

In the category, Person(Friends), the compiler will not know about Person's ivar _friends.

i.e.

//Person.h 

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

It would be preferable to not expose this.


回答1:


In general, categories can't access ivars; synthesized ivars and ivars from class extensions are private and invisible outside the main implementation.

You can, however, do what you want by declaring the ivar in an extension which is in its own private header, and importing that header into the category's implmentation file. Be sure to also import the private header into the class's main implementation file.




回答2:


Who have told you that the compiler will not know about Person's _friends? It knows. Just declare _friends in the class @interface, not in an extension.

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

With @protected _friends will not be accessible for other objects.




回答3:


If you've got a lot of protocols, delegates, dataSources etc. on your e.g. MainViewController and you wanna outsource their callbacks to separate files (categories) like

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

but at the same time still wanna be able to access all the controller's private @properties from these categories without having to expose them in the public interface

"MainViewController.h"

the most elegant solution is still to create a private interface (extension) in a separate header file like

"MainViewController_PrivateInterface.h"

BUT - instead of the ivars - like Josh Caswell's already explained above, put all the @properties (that these outsourced delegates need to access) in that extension, too. That way you keep them all quasi-private hidden and nobody else gets to see them. Above all not in your public interface! And you do even have the choice to access your @properties' backing store ivars directly in code (instead of the convenience dot notation) just by manually creating the corresponding backing store ivars in this private external interface file. Just don't forget to import your private's interface header everywhere you wanna access these ivars (including your 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


来源:https://stackoverflow.com/questions/22441511/can-a-category-access-instance-variables-defined-in-the-class-it-extends

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!