iOS 앱과 객관적인 C 작업을 시작한 이래로 저는 변수를 선언하고 정의 할 수있는 여러 위치에 대해 정말 당황했습니다. 한편으로는 전통적인 C 접근 방식이 있고 다른 한편으로는 OO를 추가하는 새로운 ObjectiveC 지시문이 있습니다. 여러분은 제가이 위치를 변수에 사용하고 아마도 현재의 이해를 수정하고자하는 모범 사례와 상황을 이해하도록 도와 주실 수 있습니까?
다음은 샘플 클래스 (.h 및 .m)입니다.
#import <Foundation/Foundation.h>
// 1) What do I declare here?
@interface SampleClass : NSObject
{
// 2) ivar declarations
// Pretty much never used?
}
// 3) class-specific method / property declarations
@end
과
#import "SampleClass.h"
// 4) what goes here?
@interface SampleClass()
// 5) private interface, can define private methods and properties here
@end
@implementation SampleClass
{
// 6) define ivars
}
// 7) define methods and synthesize properties from both public and private
// interfaces
@end
- 1과 4에 대한 나의 이해는 그것들은 클래스의 개념을 전혀 이해하지 못하는 C 스타일의 파일 기반 선언과 정의이기 때문에 C에서 어떻게 사용되는지 정확히 사용해야한다는 것입니다. 이전에 정적 변수 기반 싱글 톤을 구현하는 데 사용되었습니다. 내가 놓친 다른 편리한 용도가 있습니까?
- iOS 작업에서 얻은 견해는 ivar가 @synthesize 지시문 외부에서 완전히 단계적으로 제거되었으므로 대부분 무시할 수 있다는 것입니다. 그럴까요?
- 5와 관련하여 : 왜 개인 인터페이스에서 메서드를 선언하고 싶습니까? 내 개인 클래스 메서드는 인터페이스에 선언없이 잘 컴파일되는 것 같습니다. 대부분 가독성을위한 것입니까?
감사합니다, 여러분!
답변
나는 당신의 혼란을 이해할 수 있습니다. 특히 최근 Xcode 및 새로운 LLVM 컴파일러 업데이트로 인해 ivar 및 속성을 선언 할 수있는 방식이 변경되었습니다.
“현대적인”Objective-C ( “오래된”Obj-C 2.0에서) 이전에는 선택의 여지가 많지 않았습니다. 중괄호 사이의 헤더에서 선언하는 데 사용되는 인스턴스 변수 { }
:
// MyClass.h
@interface MyClass : NSObject {
int myVar;
}
@end
구현에서만 이러한 변수에 액세스 할 수 있지만 다른 클래스에서는 액세스 할 수 없습니다. 이를 위해 다음과 같은 접근 자 메서드를 선언해야합니다.
// MyClass.h
@interface MyClass : NSObject {
int myVar;
}
- (int)myVar;
- (void)setMyVar:(int)newVar;
@end
// MyClass.m
@implementation MyClass
- (int)myVar {
return myVar;
}
- (void)setMyVar:(int)newVar {
if (newVar != myVar) {
myVar = newVar;
}
}
@end
이렇게하면 일반적인 대괄호 구문을 사용하여 메시지 (호출 메서드)를 사용하여 다른 클래스에서도이 인스턴스 변수를 가져오고 설정할 수 있습니다.
// OtherClass.m
int v = [myClass myVar]; // assuming myClass is an object of type MyClass.
[myClass setMyVar:v+1];
수동으로 선언하고 모든 접근 방법은 매우 짜증나 구현 때문에 @property
및 @synthesize
자동 접근 방법을 생성하기 위해 도입되었다 :
// MyClass.h
@interface MyClass : NSObject {
int myVar;
}
@property (nonatomic) int myVar;
@end
// MyClass.m
@implementation MyClass
@synthesize myVar;
@end
결과는 훨씬 더 명확하고 짧은 코드입니다. 접근 자 메서드가 구현되며 이전과 같이 대괄호 구문을 계속 사용할 수 있습니다. 그러나 또한 점 구문을 사용하여 속성에 액세스 할 수도 있습니다.
// OtherClass.m
int v = myClass.myVar; // assuming myClass is an object of type MyClass.
myClass.myVar = v+1;
Xcode 4.4부터는 더 이상 인스턴스 변수를 직접 선언 할 필요가 없으며 건너 뛸 수도 @synthesize
있습니다. ivar를 선언하지 않으면 컴파일러가이를 추가하고 .NET Framework를 사용하지 않고도 접근 자 메서드를 생성합니다 @synthesize
.
자동 생성 된 ivar의 기본 이름은 밑줄로 시작하는 이름 또는 속성입니다. 다음을 사용하여 생성 된 ivar의 이름을 변경할 수 있습니다.@synthesize myVar = iVarName;
// MyClass.h
@interface MyClass : NSObject
@property (nonatomic) int myVar;
@end
// MyClass.m
@implementation MyClass
@end
이것은 위의 코드와 똑같이 작동합니다. 호환성을 위해 헤더에서 ivars를 선언 할 수 있습니다. 그러나 속성을 선언하지 않고이를 수행하려는 유일한 이유는 개인 변수를 만드는 것이므로 이제 구현 파일에서도이를 수행 할 수 있으며 이것이 선호되는 방법입니다.
@interface
구현 파일 의 블록은 실제로 확장 이며 선언 메서드 (더 이상 필요하지 않음)를 전달하고 속성을 (재) 선언하는 데 사용할 수 있습니다. 예를 들어 readonly
헤더에 속성을 선언 할 수 있습니다.
@property (nonatomic, readonly) myReadOnlyVar;
readwrite
ivar에 대한 직접 액세스뿐만 아니라 속성 구문을 사용하여 설정할 수 있도록 구현 파일에서 다시 선언하십시오 .
변수를 any @interface
또는 @implementation
블록 외부에서 완전히 선언 하는 경우에는 예, 일반 C 변수이며 정확히 동일하게 작동합니다.
답변
먼저 @DrummerB의 답변을 읽으십시오. 이유와 일반적으로 수행해야하는 작업에 대한 좋은 개요입니다. 이를 염두에두고 구체적인 질문에 대해
#import <Foundation/Foundation.h>
// 1) What do I declare here?
여기에는 실제 변수 정의가 없습니다 (현재 수행중인 작업을 정확히 알고 있지만 절대로 수행하지 않는 경우 기술적으로 합법적입니다). 몇 가지 다른 종류를 정의 할 수 있습니다.
- typdefs
- 열거 형
- externs
Externs는 변수 선언처럼 보이지만 실제로 다른 곳에서 선언하겠다는 약속 일뿐입니다. ObjC에서는 상수를 선언하는 데만 사용해야하며 일반적으로 문자열 상수 만 사용해야합니다. 예를 들면 :
extern NSString * const MYSomethingHappenedNotification;
그런 다음 .m
파일에서 실제 상수를 선언합니다.
NSString * const MYSomethingHappenedNotification = @"MYSomethingHappenedNotification";
@interface SampleClass : NSObject
{
// 2) ivar declarations
// Pretty much never used?
}
DrummerB가 언급했듯이 이것은 유산입니다. 여기에 아무것도 넣지 마십시오.
// 3) class-specific method / property declarations
@end
네.
#import "SampleClass.h"
// 4) what goes here?
위에서 설명한 외부 상수. 또한 파일 정적 변수는 여기로 갈 수 있습니다. 이는 다른 언어의 클래스 변수와 동일합니다.
@interface SampleClass()
// 5) private interface, can define private methods and properties here
@end
네
@implementation SampleClass
{
// 6) define ivars
}
그러나 아주 드물게. 거의 항상 clang (Xcode)이 변수를 생성하도록 허용해야합니다. 예외는 일반적으로 ObjC가 아닌 ivar (Core Foundation 객체, 특히 ObjC ++ 클래스 인 경우 C ++ 객체) 또는 이상한 저장소 의미를 가진 ivar (예 : 어떤 이유로 속성과 일치하지 않는 ivar)에 관한 것입니다.
// 7) define methods and synthesize properties from both public and private
// interfaces
일반적으로 더 이상 @synthesize해서는 안됩니다. Clang (Xcode)이 당신을 위해 할 것입니다.
지난 몇 년 동안 상황은 훨씬 더 단순 해졌습니다. 부작용은 이제 세 가지 시대 (Fragile ABI, Non-Fragile ABI, Non-Fragile ABI + auto-syntheisze)가 있다는 것입니다. 따라서 이전 코드를 보면 약간 혼란 스러울 수 있습니다. 따라서 단순성에서 발생하는 혼란 : D
답변
나는 또한 꽤 새롭기 때문에 아무 것도 망치지 않기를 바랍니다.
1 & 4 : C 스타일 전역 변수 : 파일 전체 범위가 있습니다. 둘 사이의 차이점은 파일 전체가 넓기 때문에 첫 번째는 헤더를 가져 오는 모든 사람이 사용할 수 있지만 두 번째는 그렇지 않다는 것입니다.
2 : 인스턴스 변수. 대부분의 인스턴스 변수는 속성을 사용하여 접근자를 통해 합성되고 검색 / 설정됩니다. 이는 메모리 관리를 멋지고 간단하게 만들뿐만 아니라 이해하기 쉬운 점 표기법을 제공하기 때문입니다.
6 : 구현 ivar는 다소 새로운 것입니다. 공개 헤더에 필요한 것만 노출하고 싶지만 하위 클래스는 AFAIK를 상속하지 않기 때문에 비공개 ivar를 배치하는 것이 좋습니다.
3 & 7 : 공용 메서드 및 속성 선언 후 구현.
5 : 개인 인터페이스. 나는 물건을 깨끗하게 유지하고 일종의 블랙 박스 효과를 만들기 위해 항상 비공개 인터페이스를 사용합니다. 그들이 그것에 대해 알 필요가 없다면 거기에 두십시오. 나는 또한 가독성을 위해 그것을합니다. 다른 이유가 있는지 모르겠습니다.
답변
이것은 Objective-C에서 선언 된 모든 종류의 변수의 예입니다. 변수 이름은 액세스를 나타냅니다.
파일 : Animal.h
@interface Animal : NSObject
{
NSObject *iProtected;
@package
NSObject *iPackage;
@private
NSObject *iPrivate;
@protected
NSObject *iProtected2; // default access. Only visible to subclasses.
@public
NSObject *iPublic;
}
@property (nonatomic,strong) NSObject *iPublic2;
@end
파일 : Animal.m
#import "Animal.h"
// Same behaviour for categories (x) than for class extensions ().
@interface Animal(){
@public
NSString *iNotVisible;
}
@property (nonatomic,strong) NSObject *iNotVisible2;
@end
@implementation Animal {
@public
NSString *iNotVisible3;
}
-(id) init {
self = [super init];
if (self){
iProtected = @"iProtected";
iPackage = @"iPackage";
iPrivate = @"iPrivate";
iProtected2 = @"iProtected2";
iPublic = @"iPublic";
_iPublic2 = @"iPublic2";
iNotVisible = @"iNotVisible";
_iNotVisible2 = @"iNotVisible2";
iNotVisible3 = @"iNotVisible3";
}
return self;
}
@end
iNotVisible 변수는 다른 클래스에서 볼 수 없습니다. 이것은 가시성 문제이므로 선언 @property
하거나 @public
변경하지 않습니다.
생성자 내에서 부작용을 피하기 위해 @property
대신 밑줄 을 사용하여 선언 된 변수에 액세스하는 것이 좋습니다 self
.
변수에 액세스 해 보겠습니다.
파일 : Cow.h
#import "Animal.h"
@interface Cow : Animal
@end
파일 : Cow.m
#import "Cow.h"
#include <objc/runtime.h>
@implementation Cow
-(id)init {
self=[super init];
if (self){
iProtected = @"iProtected";
iPackage = @"iPackage";
//iPrivate = @"iPrivate"; // compiler error: variable is private
iProtected2 = @"iProtected2";
iPublic = @"iPublic";
self.iPublic2 = @"iPublic2"; // using self because the backing ivar is private
//iNotVisible = @"iNotVisible"; // compiler error: undeclared identifier
//_iNotVisible2 = @"iNotVisible2"; // compiler error: undeclared identifier
//iNotVisible3 = @"iNotVisible3"; // compiler error: undeclared identifier
}
return self;
}
@end
런타임을 사용하여 보이지 않는 변수에 계속 액세스 할 수 있습니다.
파일 : Cow.m (2 부)
@implementation Cow(blindAcess)
- (void) setIvar:(NSString*)name value:(id)value {
Ivar ivar = class_getInstanceVariable([self class], [name UTF8String]);
object_setIvar(self, ivar, value);
}
- (id) getIvar:(NSString*)name {
Ivar ivar = class_getInstanceVariable([self class], [name UTF8String]);
id thing = object_getIvar(self, ivar);
return thing;
}
-(void) blindAccess {
[self setIvar:@"iNotVisible" value:@"iMadeVisible"];
[self setIvar:@"_iNotVisible2" value:@"iMadeVisible2"];
[self setIvar:@"iNotVisible3" value:@"iMadeVisible3"];
NSLog(@"\n%@ \n%@ \n%@",
[self getIvar:@"iNotVisible"],
[self getIvar:@"_iNotVisible2"],
[self getIvar:@"iNotVisible3"]);
}
@end
보이지 않는 변수에 접근 해 봅시다.
파일 : main.m
#import "Cow.h"
#import <Foundation/Foundation.h>
int main(int argc, char *argv[]) {
@autoreleasepool {
Cow *cow = [Cow new];
[cow performSelector:@selector(blindAccess)];
}
}
이것은 인쇄
iMadeVisible
iMadeVisible2
iMadeVisible3
_iNotVisible2
하위 클래스 전용 인 백업 ivar에 액세스 할 수있었습니다 . Objective-C에서는 모든 변수를 읽거나 설정할 수 있으며,으로 표시된 변수 @private
도 예외없이 읽을 수 있습니다 .
나는 서로 다른 새이기 때문에 관련 객체 또는 C 변수를 포함하지 않았습니다. C 변수의 경우 외부에서 정의 된 모든 변수 @interface X{}
또는 @implementation X{}
파일 범위 및 정적 저장소가있는 C 변수입니다.
메모리 관리 속성, 읽기 전용 / 읽기 쓰기, getter / setter 속성에 대해서는 논의하지 않았습니다.