iOS 앱 개발
Lecture 02 - 오브젝티브-C
동의대학교 게임공학과 서진석
jsseo@deu.ac.kr
목차
•
‣ 프로퍼티오브젝티브-C
‣ strong과 weak
‣ nil과 BOOL
‣ 인스턴스 메소드, 클래스 메소드
‣ 동적 바인딩
‣ 인트로스펙션
• Foundation 프레임워크
‣ NSObject, NSString, NSMutableString
‣ NSNumber, NSValue, NSData, NSDate
‣ NSArray, NSDictionary, NSSet, ...
‣ 열거
‣ 프로퍼티 리스트
프로퍼티(Property)
• 왜 프로퍼티를 사용하는가?
‣ 인스턴스 변수에 대한 안전성과 확장성 지원
‣ Lazy Instantiation, UI 업데이트, 일관성 체크 (예, speed < 1)
• 프로퍼티를 위한 인스턴스 변수는?
‣ 항상 필요한 것은 아님
‣ 어떤 계산에 의해서 값을 반환하는 getter 메소드 구현 가능
프로퍼티(Property)
• 닷 표기법 (dot notation)은 왜 사용하는가?
‣ 코드가 깔끔해짐
‣ 다른 메소드와 프로퍼티 접근을 서로 구분
‣ C 구조체 문법과 동일
‣ C 구조체와의 차이점 - 메시지 전송이 불가능
- 거의 대부분 정적 할당: 스택(stack)
닷 표기법
typedef struct { float x;
float y;
} CGPoint; 클래스 이름처럼 대문자로 시작하는 구조체 “CGPoint”
닷 표기법
typedef struct { float x;
float y;
} CGPoint;
@interface Bomb : NSObject
@property CGPoint position;
@end
@interface Ship : Vehicle
@property float width;
@property float height;
@property CGPoint center;
- (BOOL)getHitByBomb:(Bomb *)bomb;
@end
인자로 전달된 bomb과 리시버 객체가 충돌하였는지를 검사하여 BOOL 값 반환
닷 표기법
typedef struct { float x;
float y;
} CGPoint;
@interface Bomb : NSObject
@property CGPoint position;
@end
@interface Ship : Vehicle
@property float width;
@property float height;
@property CGPoint center;
- (BOOL)getHitByBomb:(Bomb *)bomb;
@end
@implementation Ship
@synthesize width, height, center;
- (BOOL)getsHitByBomb:(Bomb *)bomb {
float leftEdge =
self.center.x - self.width/2;
float rightEdge = ...;
return ((bomb.position.x >= leftEdge) &&
! ! ! (bomb.position.x <= rightEdge) &&
! ! ! (bomb.position.y >= topEdge) &&
! ! ! (bomb.position.y <= bottomEdge));
}
@end 객체의 프로퍼티에
접근하기 위한 닷 표기법 구조체의 멤버에
접근하기 위한 닷 표기법
strong vs weak
• strong
‣ 소유하는 클래스가 더이상 참조하지 않을 때까지 메모리(heap)에 유지
‣ 만약, 소유하는 클래스가 이 포인터에 nil을 대입하면 메모리 해제
‣ 즉, 아무도 strong 프로퍼티로 참조하지 않으면 메모리에서 해제
‣ iOS 4, Xcode 4.2 이후 지원
• weak
‣ 누군가 strong 프로퍼티로 참조할 때까지만 메모리에 유지
‣ 만약 아무도 strong 프로퍼티로 참조하지 않으면, 자동으로 nil을 대입
‣ iOS 5 이후 지원
strong vs weak
• 가비지 콜렉션 (garbage collection)은 아님!
‣ 보다 좋은 방법
‣ ARC(Automatic Reference Counting)라고 함
‣ retain, release, autorelease 등을 사용할 수 없으며, 컴파일러가 자동 으로 코드 생성
strong vs weak
• 객체가 소멸될 때 무엇인가를 하고 싶다면?
‣ dealloc 메소드가 호출됨
‣ 하지만, 직접 구현할 일은 거의 없음
‣ 이유: 무엇을 하기엔 이미 너무 늦은 시간
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
nil
• 아무 것도 가리키지 않는 포인터 변수 값
id obj = nil;
NSString *hello = nil;
• 기본 데이터 타입에서의 “0(영)”과 유사
• 객체 생성 시 모든 인스턴스 변수는 0으로 설정됨
‣ 객체의 경우 nil로 설정
‣ 실제 내부에서는 nil 도 0으로 정의되어 있음
nil
• 암묵적인 테스트 가능
if (obj) { // if (obj != nil) 과 동일 // ...
}
if (!obj) { // if (obj == nil) 과 동일 // ...
}
• nil에 메시지 전송 가능
‣ 아무 코드도 실행하지 않음
int i = [obj methodWhichReturnsAnInt]; // i는 0 id a = [obj methodWhichReturnsAnObject];! // a는 nil
CGPoint p = [obj getLocation]; // p의 값은 보장 안됨 (이유: C 구조체)
BOOL
• 오브젝티브 C의 부울리언 타입
‣ 실제로는 unsigned char 타입을 typedef
‣ 암묵적인 테스트 가능
if (flag) { // if (flag == YES) 와 동일 // ...
}
if (!flag) { // if (flag == NO) 와 동일 // ...
}
‣ 0은 NO, 그 밖의 값은 YES
‣
YES와 NO 사용
인스턴스 vs 클래스 메소드
• - 기호로 시작 • + 기호로 시작
- (BOOL)dropBomb:(Bomb *)bomb
at:(CGPoint)position from:(double)altitude;
+ (id) alloc;
+ (Ship *)motherShip;
+ (NSString *)stringWithFormat:...
Bomb : 클래스
힙에 할당된 객체 포인터를 넘김
CGPoint : C 구조체
포인터가 아니므로, 값이(value) 직접 전달 스택에 생성
인스턴스 vs 클래스 메소드
• - 기호로 시작
• 보통의 메소드
• 호출 문법
• + 기호로 시작
• 생성 & 유틸리티 메소드
• 호출 문법
- (BOOL)dropBomb:(Bomb *)bomb
at:(CGPoint)position from:(double)altitude;
+ (id) alloc;
+ (Ship *)motherShip;
+ (NSString *)stringWithFormat:...
[<인스턴스 포인터> 메소드]
Ship *ship = ...; // Ship 인스턴스 died = [ship dropBomb:cracker at:dropPoint from:300.0];
[클래스 메소드]
Ship *ship = [Ship motherShip];
NSString *str =
[NSString StringWithFormat:@"%g",r];
[[ship class] doSomething];
ship은 인스턴스
인스턴스 메소드 “class”는 클래스를 반환
“doSomething”은 클래스 메소드
인스턴스 vs 클래스 메소드
• - 기호로 시작
• 보통의 메소드
• 호출 문법
• self/super는 인스턴스
‣
self : 자신의 인스턴스 메소드
호출‣
super : 수퍼클래스의 인스턴스
메소드 호출• + 기호로 시작
• 생성 & 유틸리티 메소드
• 호출 문법
• self/super는 클래스
‣
self : 자신의 클래스 메소드 호
출‣
super : 수퍼클래스의 클래스 메
소드 호출- (BOOL)dropBomb:(Bomb *)bomb
at:(CGPoint)position from:(double)altitude;
+ (id) alloc;
+ (Ship *)motherShip;
+ (NSString *)stringWithFormat:...
[<인스턴스 포인터> 메소드]
Ship *ship = ...; // Ship 인스턴스 died = [ship dropBomb:cracker at:dropPoint from:300.0];
[클래스 메소드]
Ship *ship = [Ship motherShip];
NSString *str =
[NSString StringWithFormat:@"%g",r];
[[ship class] doSomething];
메소드
• 다양한 메소드의 예
- (double)performOperation:(NSString *)operation;
- (NSMutableArray *)operandStack;
- (NSString *)stringByAppendingString:(NSString *)otherString;
- (void)doSomething;
- (void)doSomethingWithThingOne:(Thing *)one andThingTwo:(Thing *)two;
- (NSArray *)collectButterfliesWithSpotCount:(int)spots;
- (NSComparisonResult)compare:(NSString *)aString
options:(NSStringCompareOptions)mask range:(NSRange)range;
+ (double)yieldForPhotonTorpedoOfType:(PhotonTorpedoType)type;
인스턴스 생성 (Instantiation)
•
다른 객체로부터 새로운 객체 생성‣ NSString: - (NSString *)stringByAppendingString:(NSString *)otherString;
‣ NSString & NSArray: - (id)mutableCopy;
‣ NSArray: -(NSString *)componentsJoinedByString:(NSString *)separator;
•
항상 새로운 객체를 생성하지는 않음‣ NSArray: - (id)lastObject;
‣ NSArray: - (id)objectAtIndex:(int)index;
‣ 이미 존재하는 객체의 경우 포인터만 반환 (새로 생성하지 않음)
•
클래스 메소드를 이용한 객체 생성‣ NSString: + (id)stringWithFormat:(NSString *)format, ...;
‣ UIButton: + (id)buttonWithType:(UIButtonType)buttonType;
‣ NSMutableArray: + (id)arrayWithCapacity:(int)count;
‣ NSArray: + (id)arrayWithObject:(id)anObject;
인스턴스 생성 (Instantiation)
• 객체를 직접 생성하기
‣ 2 단계 필요 : 메모리 할당(allocation)과 초기화(initialization)
‣ 1단계 후에 2단계를 위한 메소드 바로 호출
NSMutableArray *stack = [[NSMutableArray alloc] init];
CalculatorBrain *brain = [[CalculatorBrain alloc] init];
인스턴스 생성 (Instantiation)
• 메모리 할당
‣ NSObject의 클래스 메소드 + (id)alloc 이 힙에 메모리 할당
‣ 모든 인스턴스 변수를 위한 공간 할당
• 초기화
‣ 기본적인 인스턴스 메소드인 - (id)init 메소드만 정의하거나
‣ 좀 더 복잡하고 다양한 - (id)init* 메소드 정의
‣ 초기자, 초기화 메소드, 이니셜라이저, ...
인스턴스 생성 (Instantiation)
• 좀 더 복잡한 init 메소드
‣ 객체 초기화에 인자가 필요하면 init으로 시작하는 이름으로 메소드 정의
• 다양한 초기자를 갖는 NSString의 예
예) - (id)initWithFrame:(CGRect)aRect // UIView를 위한 초기화 메소드 UIView *myView = [[UIView alloc] initWithFrame:thePerfectFrame];
- (id)initWithCharacters:(const unichar *)characters length:(int)length;
- (id)initWithFormat:(NSString *)format, ...;
- (id)initWithData:(NSData *)data encoding:(NSStringEncoding)encoding;
인스턴스 생성 (Instantiation)
• 지정된 초기자 (designated initializer)
‣ 다수의 초기자가 있는 경우, 대표적인 메소드를 하나 지정
‣ 다른 초기자(편의 초기자)는 지정된 초기자를 호출하여 초기화
‣ 서브클래스의 지정된 초기자는 수퍼클래스의 지정된 초기자를 호출 하여 초기화
‣ 서브클래스의 편의 초기자는 서브클래스의 지정된 초기자를 호출하 여 초기화
인스턴스 생성 (Instantiation)
• 초기자가 반환하는 객체의 타입
‣ 초기자는 서브클래스에서 호출될 수 있으므로, id 타입을 반환하도록 함
‣ id : 모든 클래스 객체를 가리킬 수 있는 포인터
‣ 반환된 객체 포인터를 원하는 객체 포인터로 변환 (정적 타이핑의 예)
MyObject *obj = [[MyObject alloc] init];
인스턴스 생성 (Instantiation)
• 지정된 초기자 만들기
@implementation MyObject - (id)init
{
self = [super init]; // 수퍼클래스의 지정된 초기자 if (self) {
// 서브클래스를 위한 초기화 작업 }
return self; // [super init]이 실패면 서브클래스 초기화도 실패 }
@end
인스턴스 생성 (Instantiation)
• 편의 초기자 만들기
@implementation CalculatorBrain
- (id)initWithValidOperations:(NSArray *)anArray {
self = [self init];
self.validOperations = anArray; // self가 nil이면 아무일도 하지 않음 }
@end
수퍼클래스가 아닌 현재 클래스의 지정된 초기자 호출 초기화 작업을 변경하고 싶으면 지정된 초기자만 변경 지정된 초기자에서만 수퍼클래스의 지정된 초기자 호출
동적 바인딩
• 모든 객체는 힙(heap)에 할당
‣ 즉, 포인터 변수만 사용 가능
NSString *s = ...; // 정적 타이핑 (컴파일 타임에 타입 결정) id obj = s; // 동적 타이핑 (런타임에 타입 결정)
‣ “id” 자체가 포인터, “id *”는 이중 포인터
• 메시지 전송에 실행할 코드는 런타임에 결정
‣ 메소드가 없으면, 컴파일 타임에는 경고만 발생
‣ 메소드가 없으면, 런 타임에 예외 발생, 프로그램 종료
• 포인터 변수의 타입 변환(캐스팅)
id obj = ...;
NSString *s = (NSString *)obj;
‣ 위험한 코드가 될 수 있음 (인트로스펙션 사용 !)
객체 타이핑
@interface Vehicle : NSObject - (void)move;
@end
@interface Ship : Vehicle - (void)shoot;
@end
Ship *s = [[Ship alloc] init];
[s shoot];
[s move]; 컴파일러 경고 없음
s는 Vehicle이기도 하므로 문제 없음
s “isA” Vehicle (s는 Vehicle에 isA 관계 성립)
객체 타이핑
@interface Vehicle : NSObject - (void)move;
@end
@interface Ship : Vehicle - (void)shoot;
@end
Ship *s = [[Ship alloc] init];
[s shoot];
[s move];
Vehicle *v = s;
컴파일러 경고 없음
s는 Vehicle이기도 하므로 문제 없음
객체 타이핑
@interface Vehicle : NSObject - (void)move;
@end
@interface Ship : Vehicle - (void)shoot;
@end
Ship *s = [[Ship alloc] init];
[s shoot];
[s move];
Vehicle *v = s;
[v shoot];
컴파일러 경고 발생 런타임에는 문제 없음
우리는 s가 원래 Ship 객체임을 알지만, 컴파일러는 s를 Vehicle 객체로 간주
객체 타이핑
@interface Vehicle : NSObject - (void)move;
@end
@interface Ship : Vehicle - (void)shoot;
@end
Ship *s = [[Ship alloc] init];
[s shoot];
[s move];
Vehicle *v = s;
[v shoot];
id obj = ...;
[obj shoot];
컴파일러 경고 없음
런타임에는 예외 발생 가능, 프로그램 종료 가능 컴파일러는 shoot이라는 메소드를 알고 있음 id는 어떤 객체도 가리킬 수 있으므로,
shoot 메시지에 반응을 할 수도 있다고 생각함 만약, NSObject *obj 라면 컴파일러 경고 발생
객체 타이핑
@interface Vehicle : NSObject - (void)move;
@end
@interface Ship : Vehicle - (void)shoot;
@end
Ship *s = [[Ship alloc] init];
[s shoot];
[s move];
Vehicle *v = s;
[v shoot];
id obj = ...;
[obj shoot];
[obj someMethodNameThatNoObjectAnywhereRespondsTo];
컴파일러 경고 발생
런타임에도 예외 발생, 프로그램 종료 컴파일러는 처음 본 메소드
객체 타이핑
@interface Vehicle : NSObject - (void)move;
@end
@interface Ship : Vehicle - (void)shoot;
@end
Ship *s = [[Ship alloc] init];
[s shoot];
[s move];
Vehicle *v = s;
[v shoot];
id obj = ...;
[obj shoot];
[obj someMethodNameThatNoObjectAnywhereRespondsTo];
NSString *hello = @”hello”;
[hello shoot];
컴파일러 경고 발생
런타임에도 예외 발생, 프로그램 종료
컴파일러는 NSString 클래스에 shoot 메소드가 없는 것을 알고 있음
객체 타이핑
@interface Vehicle : NSObject - (void)move;
@end
@interface Ship : Vehicle - (void)shoot;
@end
Ship *s = [[Ship alloc] init];
[s shoot];
[s move];
Vehicle *v = s;
[v shoot];
id obj = ...;
[obj shoot];
[obj someMethodNameThatNoObjectAnywhereRespondsTo];
NSString *hello = @”hello”;
[hello shoot];
Ship *helloShip = (Ship *)hello;
컴파일러 경고 없음 런타임에도 문제 없음
객체 타이핑
@interface Vehicle : NSObject - (void)move;
@end
@interface Ship : Vehicle - (void)shoot;
@end
Ship *s = [[Ship alloc] init];
[s shoot];
[s move];
Vehicle *v = s;
[v shoot];
id obj = ...;
[obj shoot];
[obj someMethodNameThatNoObjectAnywhereRespondsTo];
NSString *hello = @”hello”;
[hello shoot];
Ship *helloShip = (Ship *)hello;
[helloShip shoot];
컴파일러 경고 없음
런타임에 예외 발생, 프로그램 종료
객체 타이핑
@interface Vehicle : NSObject - (void)move;
@end
@interface Ship : Vehicle - (void)shoot;
@end
Ship *s = [[Ship alloc] init];
[s shoot];
[s move];
Vehicle *v = s;
[v shoot];
id obj = ...;
[obj shoot];
[obj someMethodNameThatNoObjectAnywhereRespondsTo];
NSString *hello = @”hello”;
[hello shoot];
Ship *helloShip = (Ship *)hello;
컴파일러 경고 없음
런타임에 예외 발생, 프로그램 종료
인트로스펙션 (Introspection)
• 언제 id를 사용하는가?
‣ NSArray 배열은 서로 다른 종류의 객체 보관이 가능
‣ 배열의 원소에 메시지를 보내기 전에 클래스를 알아야 함
• 모든 NSObject 서브클래스의 메소드
‣ isKindOfClass: 해당 클래스인지 조사 (서브클래스 포함)
‣ isMemberOfClass: 해당 클래스인지만 조사
‣ respondToSelector: 해당 메소드가 있는지 조사
인트로스펙션 (Introspection)
• 인트로스펙션 메소드의 인자
‣ 클래스의 경우 class라는 클래스 메소드 사용
‣ 메소드의 경우 셀렉터(SEL) 타입 사용
‣ @selector() 키워드로 SEL 타입 생성
if ([obj isKindOfClass:[NSString class]]) {
NSString *s = [(NSString *)obj stringByAppendingString:@"xyzzy"];
}
if ([obj respondsToSelector:@selector(shoot)]) { [obj shoot];
}
인트로스펙션 (Introspection)
• SEL은 셀렉터를 위한 오브젝티브 C 데이터 타입
SEL shootSelector = @selector(shoot);
SEL moveToSelector = @selector(moveTo:);
‣ 타겟/액션에서 주로 사용
[button addTarget:self action:@selector(digitPressed:)];
• 셀렉터를 이용하여 메소드 수행 가능
‣ NSObject의 performSelector:
performSelector:withObject: 메소드 사용
[obj performSelector:shootSelector];
[obj performSelector:moveToSelector withObject:coordinate];
Foundation 프레임워크
• NSObject
‣ iOS SDK의 모든 객체의 베이스 클래스
‣ 메모리 관리 메소드 구현
‣ 인트로스펙션 메소드 구현
‣ -(NSString *)description 메소드 구현
- 주로 오버라이딩 하여 사용
- NSLog() 함수의 %@ 부분에 사용 - 객체를 설명하기 위한 메소드
Foundation 프레임워크
• NSString
‣ 유니코드 기반의 문자열 객체 (다국어 지원)
‣ iOS SDK에서는 C의 char* 대신 NSString 사용
‣ 문자열 상수는 @”foo”와 같이 문자열 앞에 @문자 사용
‣ NSString 객체는 변경 불가능 → 이뮤터블 데이터 타입
‣ 주로 NSString 객체에 메시지를 보내어 새로운 객체 생성
[display setText:[[display text] stringByAppendingString:digit]];
// display의 text 프로퍼티 문자열 뒤에 digit을 붙인 후 다시 설정 display.text = [display.text stringByAppendingString:digit];
// 위와 동일한 작동 (프로퍼티의 게터와 세터 실행)
display.text = [NSString stringWithFormat:@"%g", brain.operand];
// 클래스 메소드 사용: 팩토리 메소드 패턴의 예
‣ 많은 유틸리티메소드 제공
Foundation 프레임워크
• NSMutableString
‣ 변경 가능한 문자열 객체
‣ NSString의 서브 클래스
‣ 새로운 객체를 생성하지 않고 변경 가능
NSMutableString *mutString = [[NSMutableString alloc] initWithString:@“0.”];
[mutString appendString:digit];
Foundation 프레임워크
• NSNumber
‣ int, float, double, BOOL등과 같은 기본 데이터 타입의 객체화를 위한 래퍼(wrapper) 클래스
NSNumber *num = [NSNumber numberWithFloat:36.5];
float f = [num floatValue];
‣ 객체만 담을 수 있는 컬렉션 데이터 타입에 유용 ( NSArray, NSDictionary, NSSet)
• NSValue
‣ 구조체나 포인터 타입의 객체화
CGPoint point = CGPointMake(25.0, 15.0);
NSValue *val = [NSValue valueWithCGPoint:point];
CGPoint point2 = [val CGPointValue];
Foundation 프레임워크
• NSData
‣ 바이트 단위의 raw 데이터
‣ 저장, 읽기, 전송 등에 사용
• NSDate
‣ 날짜와 시간을 위한 클래스
‣ 현재 시간을 알아오거나, 시간에 대한 정보 저장
‣ NSCalendar, NSDateFormatter, NSDateComponent 등과 함께 사용
Foundation 프레임워크
• NSArray
‣ 순서가 있는 컬렉션 타입
‣ 이뮤터블(변경 불가능), 생성할 때만 멤버 추가 가능
+ (id)arrayWithObjects:(id)firstObject, ...; // nil로 끝나는 다수의 객체 NSArray *primaryColors =
[NSArray arrayWithObjects:@"red", @"yellow", @"blue", nil];
+ (id)arrayWithObject:(id)soleObjectInTheArray; // 생각보다 유용!
- (int)count;
- (id)objectAtIndex:(int)index;
- (id)lastObject; // 원소가 하나도 없으면 nil 반환, 매우 유용!
- (NSArray *)sortedArrayUsingSelector:(SEL)aSelector;
- (void)makeObjectsPerformSelector:(SEL)aSelector
withObject:(id)selectorArgument;
- (NSString *)componentsJoinedByString:(NSString *)separator;
- (BOOL)containsObject:(id)anObject; // 느림, 대신에 NSOrderedSet 사용
Foundation 프레임워크
• NSMutableArray
‣ 원소를 바꿀 수 있는 배열
+ (id)arrayWithCapacity:(int)initialSpace; // initialSpace: 성능에 도움 + (id)array;
- (void)addObject:(id)anObject; // 배열 끝에 추가
- (void)insertObject:(id)anObject atIndex:(int)index;
- (void)removeObjectAtIndex:(int)index;
- (void)removeLastObject;
- (id)copy; // 이뮤터블 NSArray 반환
// 이뮤터블에서 뮤터블 생성: mutableCopy 사용
‣ 위 외에도 NSArray의 모든 메소드 사용 가능 (NSMutableArray는 NSArray의 서브클래스)
Foundation 프레임워크
• NSDictionary
‣ 키(key)로 원소(value)에 한번에 접근 (해시 테이블, 맵)
‣ 이뮤터블(변경 불가능), 생성할 때만 멤버 추가 가능
‣ 키는 다음 메소드를 구현하는 객체만 가능
- (NSUInteger)hash;
- (BOOL)isEqual:(NSObject *)obj;
‣ NSObject는 포인터 값을 hash 값으로 사용
‣ NSObject는 포인터 값이 같으면 isEqual:의 결과가 YES
‣ 주로 NSString을 키로 사용
Foundation 프레임워크
• NSDictionary
‣ 주요 메소드
+ (id)dictionaryWithObjects:(NSArray *)values forKeys:(NSArray *)keys;
+ (id)dictionaryWithObjectsAndKeys:(id)firstObject, ...;
NSDictionary *base = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:2], @"binary",
[NSNumber numberWithInt:16], @"hexadecimal", nil];
- (int)count;
- (id)objectForKey:(id)key;
- (NSArray *)allKeys;
- (NSArray *)allValues;
Foundation 프레임워크
• NSMutableDictionary
‣ 변경가능한 NSDictionary
+ (id)dictionary; // 빈 디렉토리 생성
// 이 외에도 NSDictionary의 다양한 초기자 사용 가능 - (void)setObject:(id)anObject forKey:(id)key;
- (void)removeObjectForKey:(id)key;
- (void)removeAllObjects;
- (void)addEntriesFromDictionary:(NSDictionary *)otherDictionary;
Foundation 프레임워크
• NSSet
‣ 순서가 없는 컬렉션 타입
‣ 이뮤터블(변경 불가능), 생성할 때만 멤버 추가 가능
‣ 주요 메소드
- (int)count;
- (BOOL)containsObject:(id)anObject;
// anObject가 있는지 조사 ( == 비교) - (id)anyObject;
// 순서가 없으므로, 아무 객체나 반환 - (void)makeObjectsPerformSelector:(SEL)aSelector;
- (id)member:(id)anObject;
// isEqual 메소드 사용
// isEqual 메소드에 따라 결과가 다름
‣ 모든 원소는 유일 (NSCountedSet는 중복된 원소도 가능)
Foundation 프레임워크
• NSMutableSet
‣ 변경이 가능한 NSSet
- (void)addObject:(id)anObject; // 같은 원소가(isEqual: 메소드 사용) // 이미 있으면 아무 일도 안함
- (void)removeObject:(id)anObject;
- (void)unionSet:(NSSet *)otherSet;
- (void)minusSet:(NSSet *)otherSet;
- (void)intersectSet:(NSSet *)otherSet;
Foundation 프레임워크
• NSOrderedSet
‣ 순서가 있는 컬렉션 타입
‣ 이뮤터블(변경 불가능), 생성할 때만 멤버 추가 가능
‣ 모든 원소는 유일
‣ NSArray와 NSSet의 중간 형태
‣ NSArray보다 “contains” 메소드가 빠름
‣ NSSet 과는 달리 순서가 있음
‣ NSSet의 서브클래스는 아니지만 거의 유사한 메소드 제공
‣ NSMutableOrderedSet 도 사용 가능
열거 (Enumeration)
• 컬렉션의 멤버를 for 루프에서 열거
‣
for in 키워드 사용
‣ 예) NSString의 NSArray
NSArray *myArray = ...;
for (NSString *string in myArray) {
double value = [string doubleValue];
} // 만약 string이 NSString이 아니면 오류 발생
‣ id의 NSSet
NSSet *mySet = ...;
for (id obj in mySet) {
if ([obj isKindOfClass:[NSString class]]) { // NSString 메소드를 안전하게 실행
} }
Foundation 프레임워크
• 딕셔너리 타입에서의 열거
NSDictionary *myDictionary = ...;
for (id key in myDictionary) {
! // key에 대한 처리
! id value = [myDictionary objectForKey:key];
! // value에 대한 처리 }
프로퍼티 리스트
• 계층 구조로 이루어진 컬렉션 데이터 타입
‣ 다음 클래스 객체로만 이루어진 트리 형태의 자료 구조
- NSArray, NSDictionary, NSNumber, NSString, NSDate, NSData
‣ 멤버가 모두 프로퍼티 리스트인 NSArray도 프로퍼티 리스트 - NSString의 NSArray도 프로퍼티 리스트
- 위 NSArray를 멤버로 갖는 NSArray도 프로퍼티 리스트
‣ 모든 키와 밸류가 프로퍼티 리스트인 NSDictionary도 프로퍼티 리스트
- 키가 NSString이고 밸류가 NSNumber인 NSDictionary를 멤버로 갖는 NSArray도 프로퍼티 리스트
프로퍼티 리스트
• 용도
‣ 파일로 저장, 읽어 오기 가능
‣ 인터넷으로 부터 전송 받은 데이터로 객체 생성 가능
[plist writeToFile:(NSString *)path atomically:(BOOL)];
plist = [NSArray arrayWithContentsOfFile:@"path"];
프로퍼티 리스트
• NSUserDefaults
‣ 프로퍼티의 응용 예
‣ 프로그램이 종료되어도 항상 저장되어 있는 NSDictionary
‣ 간단한 환경 설정 데이터를 저장할 때 적합
‣ 주로 standardUserDefaults라는 클래스 메소드로 객체 얻어옴
[[NSUserDefaults standardUserDefaults] setArray:rvArray
forKey:@"RecentlyViewed"];
프로퍼티 리스트
• NSUserDefaults
‣ 주요 메소드
- (void)setDouble:(double)aDouble forKey:(NSString *)key;
- (NSInteger)integerForKey:(NSString*)key;
- (void)setObject:(id)obj forKey:(NSString *)key;
// obj도 프로퍼티 리스트 - (NSArray *)arrayForKey:(NSString *)key;
// 만약 밸류가 NSArray가 아니면 nil 반환
‣ 저장
[[NSUserDefaults standardUserDefaults] synchronize];