سؤال

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.

هل كانت مفيدة؟

المحلول

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.

نصائح أخرى

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.

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
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top