programing

"현대적인"Objective-C에서 iVar를 어디에 배치해야합니까?

nasanasas 2020. 10. 16. 07:30
반응형

"현대적인"Objective-C에서 iVar를 어디에 배치해야합니까?


Ray Wenderlich의 "iOS6 by Tutorials"책은보다 "현대적인"Objective-C 코드를 작성하는 것에 대한 아주 좋은 장을 가지고 있습니다. 한 섹션에서 책은 클래스의 헤더에서 구현 파일로 iVars를 이동하는 방법을 설명합니다. 모든 iVar는 비공개 여야하므로 이것이 올바른 방법 인 것 같습니다.

하지만 지금까지 3 가지 방법을 찾았습니다. 모두가 다르게하고 있습니다.

1.) 중괄호 블록 안에 @implementantion 아래에 iVars를 넣으십시오 (책에서 수행되는 방법입니다).

2.) 중괄호 블록없이 iVars를 @implementantion 아래에 둡니다.

3.) @implementantion (클래스 확장) 위의 개인 인터페이스 안에 iVars를 넣습니다.

이 모든 솔루션이 잘 작동하는 것 같고 지금까지 내 응용 프로그램의 동작에서 차이를 발견하지 못했습니다. "올바른"방법은 없다고 생각하지만 몇 가지 자습서를 작성해야하고 코드에 대해 한 가지 방법 만 선택하고 싶습니다.

어느쪽으로 가야하나요?

편집 : 여기서는 iVars에 대해서만 이야기하고 있습니다. 속성이 아닙니다. 개체에 필요한 추가 변수 만 외부에 노출되지 않아야합니다.

코드 샘플

1)

#import "Person.h"

@implementation Person
{
    int age;
    NSString *name;
}

- (id)init
{
    self = [super init];
    if (self)
    {
        age = 40;
        name = @"Holli";
    }
    return self;
}
@end

2)

#import "Person.h"

@implementation Person

int age;
NSString *name;


- (id)init
{
    self = [super init];
    if (self)
    {
        age = 40;
        name = @"Holli";
    }
    return self;
}
@end

삼)

#import "Person.h"

@interface Person()
{
    int age;
    NSString *name;
}
@end

@implementation Person

- (id)init
{
    self = [super init];
    if (self)
    {
        age = 40;
        name = @"Holli";
    }
    return self;
}
@end

인스턴스 변수를 @implementation블록 또는 클래스 확장 에 넣는 기능은 모든 버전의 iOS 및 64 비트 Mac OS X 프로그램에서 사용되는 "현대적인 Objective-C 런타임"의 기능입니다.

32 비트 Mac OS X 앱을 작성하려면 @interface선언에 인스턴스 변수를 넣어야합니다 . 하지만 앱의 32 비트 버전을 지원할 필요는 없습니다. OS X은 5 년 전에 출시 된 버전 10.5 (Leopard) 이후 64 비트 앱을 지원했습니다.

따라서 최신 런타임을 사용할 앱만 작성한다고 가정 해 보겠습니다. ivar를 어디에 두어야합니까?

옵션 0 : @interface(하지 마세요)

먼저 인스턴스 변수를 선언 에 넣지 않는 이유를 살펴 보겠습니다 @interface.

  1. 인스턴스 변수를 배치 @interface하면 클래스 사용자에게 구현 세부 정보가 노출됩니다. 이로 인해 해당 사용자 (자신의 클래스를 사용하는 경우에도 자신도 해당)가 사용해서는 안되는 구현 세부 정보에 의존하게 될 수 있습니다. (이것은 우리가 ivars 선언 여부와 무관합니다 @private.)

  2. 인스턴스 변수를 삽입 @interface하면 컴파일 시간이 더 오래 걸립니다. ivar 선언을 추가, 변경 또는 제거 할 때마다 .m인터페이스를 가져 오는 모든 파일 을 다시 컴파일해야하기 때문 입니다.

따라서 인스턴스 변수를 @interface. 어디에 두어야합니까?

옵션 2 : @implementation중괄호없이 (하지 마십시오)

다음으로, 옵션 2, "중괄호 블록없이 @implementantion 아래에 iVars 배치"에 대해 논의 해 보겠습니다. 이것은 인스턴스 변수를 선언 하지 않습니다 ! 당신은 이것에 대해 이야기하고 있습니다.

@implementation Person

int age;
NSString *name;

...

이 코드는 두 개의 전역 변수를 정의합니다. 인스턴스 변수를 선언하지 않습니다.

예를 들어 모든 인스턴스가 캐시와 같은 일부 상태를 공유하기를 원하기 때문에 전역 변수가 필요한 경우 .m파일에서도 전역 변수를 정의하는 @implementation것이 좋습니다. 그러나이 옵션은 ivar를 선언하지 않기 때문에 ivar를 선언하는 데 사용할 수 없습니다. (또한, 구현에 전용 전역 변수는 일반적으로 static전역 네임 스페이스를 오염시키고 링크 타임 오류를 방지하기 위해 선언해야 합니다.)

그러면 옵션 1과 3이 남습니다.

옵션 1 : @implementation중괄호 포함 (Do It)

Usually we want to use option 1: put them in your main @implementation block, in braces, like this:

@implementation Person {
    int age;
    NSString *name;
}

We put them here because it keeps their existence private, preventing the problems I described earlier, and because there's usually no reason to put them in a class extension.

So when do we want to use your option 3, putting them in a class extension?

Option 3: In a class extension (Do It Only When Necessary)

There's almost never a reason to put them in a class extension in the same file as the class's @implementation. We might as well just put them in the @implementation in that case.

But occasionally we might write a class that's big enough that we want to divide up its source code into multiple files. We can do that using categories. For example, if we were implementing UICollectionView (a rather big class), we might decide that we want to put the code that manages the queues of reusable views (cells and supplementary views) in a separate source file. We could do that by separating out those messages into a category:

// UICollectionView.h

@interface UICollectionView : UIScrollView

- (id)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout;
@property (nonatomic, retain) UICollectionView *collectionViewLayout;
// etc.

@end

@interface UICollectionView (ReusableViews)

- (void)registerClass:(Class)cellClass forCellWithReuseIdentifier:(NSString *)identifier;
- (void)registerNib:(UINib *)nib forCellWithReuseIdentifier:(NSString *)identifier;

- (void)registerClass:(Class)viewClass forSupplementaryViewOfKind:(NSString *)elementKind withReuseIdentifier:(NSString *)identifier;
- (void)registerNib:(UINib *)nib forSupplementaryViewOfKind:(NSString *)kind withReuseIdentifier:(NSString *)identifier;

- (id)dequeueReusableCellWithReuseIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath*)indexPath;
- (id)dequeueReusableSupplementaryViewOfKind:(NSString*)elementKind withReuseIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath*)indexPath;

@end

OK, now we can implement the main UICollectionView methods in UICollectionView.m and we can implement the methods that manage reusable views in UICollectionView+ReusableViews.m, which makes our source code a little more manageable.

But our reusable view management code needs some instance variables. Those variables have to be exposed to the main class @implementation in UICollectionView.m, so the compiler will emit them in the .o file. And we also need to expose those instance variables to the code in UICollectionView+ReusableViews.m, so those methods can use the ivars.

This is where we need a class extension. We can put the reusable-view-management ivars in a class extension in a private header file:

// UICollectionView_ReusableViewsSupport.h

@interface UICollectionView () {
    NSMutableDictionary *registeredCellSources;
    NSMutableDictionary *spareCellsByIdentifier;

    NSMutableDictionary *registeredSupplementaryViewSources;
    NSMutableDictionary *spareSupplementaryViewsByIdentifier;
}

- (void)initReusableViewSupport;

@end

We won't ship this header file to users of our library. We'll just import it in UICollectionView.m and in UICollectionView+ReusableViews.m, so that everything that needs to see these ivars can see them. We've also thrown in a method that we want the main init method to call to initialize the reusable-view-management code. We'll call that method from -[UICollectionView initWithFrame:collectionViewLayout:] in UICollectionView.m, and we'll implement it in UICollectionView+ReusableViews.m.


Option 2 is flat out wrong. Those are global variables, not instance variables.

Options 1 and 3 are essentially identical. It makes absolutely no difference.

The choice is whether to put instance variables in the header file or the implementation file. The advantage of using the header file is that you have a quick and easy keyboard shortcut (Command + Control + Up in Xcode) to view and edit your instance variables and interface declaration.

The disadvantage is that you expose the private details of your class in a public header. That's not desirable is some cases, particularly if you're writing code for others to use. Another potential problem is that if you're using Objective-C++, it's good to avoid putting any C++ data types in your header file.

Implementation instance variables are great option for certain situations, but for most of my code I still put the instance variables in the header simply because it's more convenient for me as a coder working in Xcode. My advice is to do whatever you feel is more convenient for you.


Largely it has to do with the visibility of the ivar to subclasses. Subclasses will not be able to access instance variables defined in the @implementation block.

For reusable code that I plan to distribute (e.g. library or framework code) where I prefer not expose instance variables for public inspection, then I'm inclined to place the ivars in the implementation block (your option 1).


You should put instance variables in a private interface above the implementation. Option 3.

The documentation to read on this is the Programming in Objective-C guide.

From the documentation:

You Can Define Instance Variables without Properties

It’s best practice to use a property on an object any time you need to keep track of a value or another object.

If you do need to define your own instance variables without declaring a property, you can add them inside braces at the top of the class interface or implementation, like this:


Public ivars should really be declared properties in the @interface (likely what you're thinking of in 1). Private ivars, if you're running the latest Xcode and using the modern runtime (64-bit OS X or iOS), can be declared in the @implementation (2), rather than in a class extension, which is likely what you're thinking of in 3.

참고URL : https://stackoverflow.com/questions/13566862/where-to-put-ivars-in-modern-objective-c

반응형