안녕하세요 소들입니다.. 😱
오늘은 Objective-C의 문법 중에서도 인스턴수 변수와 @property!!
또 @synthesize 와 @dynamic 사용법에 대해 공부하려고 해요 :)
ㅎr.... 옵젝씨에 대해
이렇게 모르면서 개발을 해왔단 게 넘 충격이에요ㅠㅠㅠㅠ
아무리 바빴어도 그렇지... 과거의 나야...;;;
이제 스위프트를 제대로 시작하는 사람으로서,
옵젝씨를 제대로 다져놓고 가고 싶어서 옵젝씨 포스팅을 좀 하려합니다!!!!!!!!11
모든 포스팅은 편의 말투로 합니다~!!
1. 인스턴스 변수
자, 먼저 우리가 아무렇지 않게 사용하던
인스턴수 변수에 대해 먼저 다시 짚고 갈 것임
우리가 만약 Human이라는 클래스를 만들고,
name이라는 변수를 추가하려면 다음과 같이
#import <UIKit/UIKit.h>
@interface Human: NSObject {
NSString *name
}
@end
|
이렇게 추가해줄 수 있었음!
이렇게 @interface 중괄호 안에 추가된 변수를 인스턴스 변수 라고 부른는데,
이 인스턴스 변수들은 기본으로 접근 제어자가 @protected임!!!
따라서, Human 클래스 내부 혹은 서브 클래스에서만 인스턴스 변수에 접근할 수있음!
근데, 만약 외부에서 이 age라는 변수에 접근하고 싶으면
먼저, 접근 제어자를 다음과 같이 @public으로 직접 바꿔줄 수도 있음
#import <UIKit/UIKit.h>
@interface Human: NSObject {
@public
NSString *name;
}
@end
|
이렇게! (지금은 @public으로 했지만, @protected, @private도 가능)
근데 이런 방식으로 사용하는 건 Objective-C에선 거의(아예) 사용하지 않는 방법임!!
왜냐? 문제가 있다함
무슨 문제냐 보면, 위와 같이 접근 제어자를 통해 선언했을 경우
name이라는 변수에 직접 접근하므로, 배치가 컴파일 시점에 결정되어,
name이란 인스턴스 변수에 접근하는 객체가 있을 경우, 그 객체의 메모리의 오프셋에 하드코딩 됨
이.. 라.. 고 .. 한 .. 다 ....
내가 생각한 바로는 어떤 느낌이냐면
메모리 오프셋이라는 것은 찾아보니까 상대적이어서
기준점에서 얼마나 떨어져 있고..? 이런 걸 나타내는 것 같음
근데 이런 경우에 언제 문제가 되냐면,
Human이라는 클래스에 새로운 인스턴스 변수를 추가했을 때 문제가 됨
이렇게 새로운 인스턴스 변수를 추가해서 name의 오프셋이 바꼈지만,
재컴파일을 하기 전까지는 instance1에 반영이 되지 않아 깨진 상태가 된다고 한다..........
(과거의 라이브러리를 쓸 경우 새로운 클래스 정의를 사용할 경우 문제가 된다는데..)
아....... 맞는걸까 ㅎ.. 아무리 찾아봐도 확신이 설만한 자료가 없네ㅠㅠ
내가 이해한게 틀리거나 부가 설명할 수 있는 날이 오면 추가 할게요... 🥵
쨌든.. 많은 언어가 이런 문제를 해결하기 위해 다양한 기술을 발명하는데,
위 문제를 해결할 수 있는 방법 중 하나는 접근자 메서드를 이용하는 것임
쉽게 우리가 알고 있는 getter, setter를 이용해
인스턴스 변수에 직접 접근하는 것이 아닌, getter & setter를 이용해 접근하여,
이렇게 오프셋을 실행 시간(Runtime)에 찾는 것임
따라서 다른 인스턴스 변수 age를 추가하더라도,
instance1 입장에선 런타임에 getter, setter를 이용해 name에 접근하니
오프셋이 깨질 문제가 없음...!
위 그림이 맞는 그림인 거 같진 않으나 ;ㅁ;
이해를 돕기 위한 것이니 참조용으로만 봐주셈.. 😱
자, 그럼 이 getter, setter를 어떻게 만드냐
//Human.h 파일
#import <UIKit/UIKit.h>
@interface Human: NSObject {
NSString *name;
}
- (NSString *)name;
- (void)setName: (NSString *)name;
@end
|
//Human.m 파일
#import "Human.h"
@implementation Human
- (NSString *)name {
return name;
}
- (void)setName: (NSString *)name {
name = name;
}
@end
|
이런 식으로 만들어서 사용함!!
이때 getter, setter 이름 또한 나름 규칙이 있는데
getter는 메서드 이름이 인스턴스 변수 이름이고,
setter는 메서드 이름이 set + 인스턴스 변수 이름임
(왜냐면 프로퍼티 자동 생성 메서드 이름이 이렇거든)
근데, 이렇게 직접 작성해도 좋지만, 만약 인스턴스 변수가 100개일 경우
getter, setter 메서드만 해도 200개임 후덜덜
따라서 Objective-C 2.0에서
이 getter, setter를 자동으로 생성해주는 프로퍼티라는 놈이 나옴!!!
물론 getter, setter는 자동으로 생성해주기에 우리가 에디터로 메서드를 볼 수는 없음!!
이제 프로퍼티를 알아보자 :
2. 프로퍼티
이 프로퍼티는 어떻게 생성하냐면,
인스턴스 변수와 다르게 @interface 밑에 작성함
#import <UIKit/UIKit.h>
@interface Human: NSObject
@property NSString *name;
@end
|
이런 식으로 @property하고 작성함!!
이 프로퍼티는 선언해줄 때 속성(copy, retain) 등등이 있는데 이는 다음편에서 다루겠음!!
이렇게 프로퍼티를 생성하면,
컴파일러는 내부적으로 해당 타입(NSString)에 해당하는 인스턴스 변수를 하나 만드는데,
이때 이 인스턴스 변수의 이름은 해당 이름(name)을 따르지만, 앞에 _(underscore)를 접두사로 붙임!!
따라서 프로퍼티를 생성하면, 클래스에 내부적으로 _name 이라는 인스턴스 변수를 추가되는 것임
때문에, 내부 클래스에서 이 프로퍼티를 사용하고 싶으면,
이런 식으로, _name으로 접근할 수 있음!!!
이 getter, setter에 대해 좀 더 알아보자 :)
2-1. @synthesize
자 위에서 프로퍼티의 경우 내부적으로 접두어로 _를 붙인 이름으로 인스턴스 변수가 생성된다고 했음
그리고 이 인스턴스 변수에 대한 getter, setter가 생성이 되는 것임!!!
근데, 프로퍼티 이름은 name이고, 자체 생성된 인스턴스 변수 이름은 _name인데
이 둘이 어떻게 호환될까?
따라서 원래는 synthesize를 다음과 같이 사용해서 이 둘 이름의 싱크를 맞춰줘야 했음
아, 참고로 선언 위치는 @implementation field, 즉 구현부 안이어야 함
#import "Human.h"
@implementation Human
@synthesize name = _name;
@end
|
이렇게!!! 그래야 getter, setter가 자동으로 생성이 되었다 함
왜 되었음이 아니고 되었다 함인줄 앎?
왜냐면 난 그동안 synthesize를 안해줘도 getter, setter를 잘 사용 했거든..
근데 찾아보니까, 2012년 중반 이후로 Modern Objective-C가 나오면서,
synthesize를 해주지 않아도 내부적으로 synthesize를 해줘서 사용 안 해도 됐던 것임 ㅎㅎ;
따라서 이제는 synthesize를 사용하지 않아도 됨!!!! 언어는 발전한다 ~_~
(옛날에는 프로퍼티 생성 시 synthesize가 필수였던듯)
근데 @synthesize를 굳~~~이 이용하고 싶다 하면 언제 쓰냐면,
나는 name 앞에 _가 들어간 게 마음에 안들어.... 원래 프로퍼티 이름으로 사용할래..
라고 할 때
@synthesize name;
|
이렇게 해주면 앞으로 _name이 아닌, name으로 접근하겠다! 라는 말이 됨 :)
따라서 다음과 같이
_를 없앤 프로퍼티 이름 그대로 사용 가능!
대신 이럴 경우 이젠 _name으로는 접근 못함!
(근데 굳이 쓰지 말란 사람도 있고 .... 써도 된단 사람도 있고 알아서 판단하시길)
아, 참고로 @synthesize를 해주든 안해주든
프로퍼티는 내부 클래스, 서브 클래스에서 self.을 이용해서 접근 가능함!
self.name = @"Sodeul";
|
쨔잔 👀
2-2. @dynamic
자, 이제 이 dynamic에 대해 배울 건데,
먼저, 내가 프로퍼티를 생성 했지만 getter는 내가 직접 작성하고 싶어!
하면 어떻게 하면 될까
getter 네이밍 규칙에 맞춰서 직접 메서드를 작성해 주면 됨
- (NSString *)name {
return @"이름 안줄거다";
}
|
이렇게 name에 대한 getter를 직접 작성할 경우,
name이란 프로퍼티에 getter로 접근하면,
내가 지정한 getter가 호출 됨!!
이런 식으로 getter, setter 중 원하는 메서드를 직접 작성할 수도 있음
(getter만 작성 시 setter는 자동 생성되는 그런 원리)
근데 만약, 나는 getter, setter를 내 클래스에서 작성하기 싫어!
서브 클래스에서 작성하거나, 런타임 시에 제공해줄 거야!
라고 하고 싶을 때 사용하는 것이 바로
@dynamic
dynamic을 사용할 경우 인스턴스 변수도 자동으로 생성되지 않음
사용 법은 다음과 같고
#import "Human.h"
@implementation Human
@dynamic name;
@end
|
이렇게 해줄경우 컴파일러는
어~ 그래~ 너 어디선가 작성될 거라 믿는다~ 해버림
따라서 컴파일 당시에는
여전히 접근 가능하지만, (컴파일러는 다른 곳에서 작성 됐을 거라 믿음)
따라서, 만약 작성 안하고 실행 하면
런타임 오류가 발생한답니다 ㅎㅎ;
.
.
.
이번 포스팅에선 인스턴스 변수와 프로퍼티의 차이점,
또 @synthesize와 @dynamic의 차이점에 대해 봤어요!
옵젝씨가 오래된 언어다 보니까 자료도 많이 없고 ㅜㅜ 그래서
틀린 설명이 있을 수 있으니 피드백 대환영 합니다!!!!!!!!!!!!!!!!!!!!!!!!!!!!
출처
'iOS > Objective-C' 카테고리의 다른 글
Objective-C) 프로퍼티 (2/2) - 속성(Attribute) (6) | 2020.12.06 |
---|---|
Objective-C) Category와 Extension (4) | 2020.12.06 |
Objective-C) JSON을 Decoding 해보자 (4) | 2020.12.05 |
Objective-C) JSON을 Encoding 해보자 (0) | 2020.12.04 |
Objective-C) C++ 사용하기 (0) | 2020.12.02 |