[Objective-C] Foundation 프레임워크의 중요 클래스 - NSData, NSArray |
9.3. 데이터 클래스
* 9.3.1. NSData
-
NSData 는 임의의 바이트 배열을 객체로 다루기 위한 랩퍼(wrapper) 이다.
-
일반적인 C 배열을 사용할 때와 비교했을 때의 장점은 객체로 추상화해서 다룰 수 있는 점,
메모리 관리가 쉬운 점, Cocoa API 에서 바이트 배열을 다루는 표준이라는 점.
-
NSData 인터페이스는 한 번 작성되면 데이터 내용을 변경할 수 없다.
데이터 내용을 나중에 변경하려면 NSMutableData 클래스를 사용해야 한다.
-
NSData 는 클래스 클래스터로 제공되므로 NSData 가 인스턴스의 직접 클래스가 아니라는 점, 일반 방법으로는 서브 클래스를 작성할 수 없다는 점에 주의해야 한다.
** 데이터 객체 초기화 작성
-
- (id) initWithBytes: (const void*) bytes length: (NSUinteger) length
bytes 로 지정한 위치에서 시작하는 길이가 length 인 데이터를 복사해서 데이터 객체 내용으로 초기화한다.
편의 생성자 : dataWithBytes:length:
- (id) initWithBytesNoCopy: (void*) bytes length: (unsigned) length freeWhenDoen: (BOOL) flag
복사본은 만들지 않는다.
flag 가 YES 이면 생성한 데이터 객체가 bytes 의 소유권을 가지고, 객체가 해제될 때 동시에 해제된다.
flag 가 NO 이면 bytes 는 자동으로 해제되지 않지만 데이터 객체가 있는 한 해제하면 안 된다.
편의 생성자 : dataWithBytesNoCopy:length:freeWhenDone:
- (id) initWithData: (NSData*) aData
편의 생성자 : dataWithData:
+ (id) data
데이터 길이가 0인 빈 데이터를 임시 객체로 반환한다.
NSMutableData 에서 사용할 때가 많으며 대응하는 초기자는 init 이다.
** 데이터에 접근
-
- (NSUInteger) length
- (const void *) bytes
바이트 배열의 첫 위치 포인터를 return
- (void) getBytes: (void*) buffer length: (NSUInteger) length
buffer 가 가리키는 버퍼에 복사한다.
지정한 범위의 데이터를 얻으려면 메서드 getBytes:range: 를 사용한다.
- (NSData*) subdataWithRange: (NSRange) range
- (NSRange) rangeOfData: (NSData*) dataToFind options: (NSDataSearchOptions) mask range: (NSRange) searchRange
** 비교
-
- (BOOL) isEqualToData: (id) anObject
** 파일 입,출력
-
- (NSString*) description
- (id) initWithContentsOfFile: (NSString*) path options: (NSUInteger) mask error: (NSError**) error
편의생성자 : dataWithContentsOfFile:options:error:
- (id) initWithContentsOfFile: (NSString*) path
편의생성자 : dataWithContentsOfFile:
- (BOOL) writeToFile: (NSString*) path atomically: (BOOL) flag
* 9.3.2. NSMutableData
** 데이터 객체 초기화 생성
-
- (id) initWithCapacity: (NSUInteger) capacity
리시버를 바이트 길이 0으로 초기화
편의생성자 : dataWithCapacity:
- (id) initWithLength: (NSUInteger) capacity
인수에서 지정한 길이만큼 바이트 배열을 가지도록 리시버를 초기화한다.
바이트 배열은 0으로 채운다.
편의 생성자 : dataWithLength:
** 데이터에 접근
-
- (void*) mutableBytes
리시버에 저장된 바이트 배열의 첫 부분 포인터를 돌려준다.
포인터가 가리키는 위치에 쓸 수 있다는 점이 NSData 의 bytes 메서드와 다르다.
** 데이터 추가
-
- (void) appendData: (NSData*) otherData
- (void) appendBytes: (const void*)bytes length: (NSUInteger)length
** 데이터 변경
-
- (void) replaceBytesInRange: (NSRange) range withBytes: (const void*) replacementBytes length: (NSUInteger) replacementLength
- (void) replaceBytesInRange: (NSRange) range withBytes: (const void*) bytes
- (void) setData:(NSData*) aData
리시버 내용 모두를 인수로 지정한 데이터 객체 내용으로 바꾼다.
- (void) resetBytesInRange: (NSRange) range
리시버의 바이트 배열 중 range 로 지정한 범위를 0 으로 채운다.
** 바이트 배열 길이 변경
-
- (void) increaseLengthBy: (NSUInteger) extraLength
리시버 바이트 배열 길이를 인수에서 지정한 만큼 늘린다.
추가된 배열은 0으로 채워진다.
- (void) setLength: (NSUInteger) length
리시버 바이트 배열이 인수에서 지정한 길이가 되도록 배로 늘리거나 잘라낸다.
배로 늘릴 때 추가된 바이트 배열은 0으로 채운다.
9.4. 배열 클래스
* 9.4.1. NSArray
-
카운터 관리 방식을 이용할 때 배열 객체는 저장된 객체에 retain 메서드를 보내어 유지한다.
객체가 해제될 떼에는 저장된 객체 모두에 release 메시지를 보낸다.
-
NSArray 는 클래스 클러스터로 제공되므로 NSArray 가 인스턴스 직접 클래스가 아니라는 점과 일반적인 방법으로는 서브 클래스를 만들 수 없다는 점에 주의해야 한다.
** 배열 객체 초기화, 작성
-
+ (id) array
+ (id) arrayWithObject: (id) anObject
- (id) initWithObjects: (id) firstObj, ...
인수 목록 끝은 nil 을 붙여주어야 한다.
편의 생성자 : arrayWithObjects:
- (id) initWithObjects: (const id*) objects count: (NSUInteger) count
인수 objects 는 C 언어 배열. 배열의 앞부분부터 count 수만큼 객체를 저장하도록 배열객체를 초기화해서 돌려준다.
편의 생성자 : arrayWithObjects:count:
- (id) initWithArray: (NSArray*) anArray
편의 생성자 : arrayWithArray:
- (id) initWithArray: (NSArray*) array copyItems: (BOOL) flag
flag 가 참이라면 배열의 각 요소를 복사해서 새로운 배열에 저장한다.
** 배열 객체에 접근
-
- (NSUInteger) count
- (NSUInteger) indexOfObject: (id) anObject
포함되지 않았으면 NSNotFound
포함되었는지 확인하려면 메서드 containsObject: 를 사용
- (id) objectAtIndex: (NSUInteger) index
인덱스가 배열 크기를 벗어날 때는 예외 NSRangeException 이 발생
- (id) lastObject
빈 배열이라면 nil
- (void) getObjects: (id __unsafe_unretained []) aBuffer range: (NSRange) aRange
aRange 가 나타내는 범위에 든 객체를 aBuffer 로 지정한 C 언어 배열에 복사한다.
객체 포인터가 복사될 뿐이므로 참조 카운터 값은 변하지 않는다.
ARC 를 사용할 때 배열 안의 객체를 유지하려면 다른 강한 참조 변수에 대입해야 한다.
aBuffer 는 지정한 개수가 들어갈 만큼 충분한 크기를 확보해야 한다.
- (NSArray*) subarrayWithRange: (NSRange) range
** 비교
-
- (BOOL) isEuqlaToArray: (id) anObject
리시버와 인수 객체가 저장된 요소의 개수가 같고 동일한 인덱스 위치에 있는 요소의 값이 서로 같을 때 YES
- (id) firstObjectCommonWithArray: (NSArray*) otherArray
리시버와 인수 배열에 공통으로 들어 있는 객체 중에서 처음으로 찾은 것이 return
** 새로운 요소 추가
-
- (NSArray*) arrayByAddingObject: (id) anObject
- (NSArray*) arrayByAddingObjectsFromArray: (NSArray*) anArray
** 정렬
-
- (NSArray*) sortedArrayUsingSelector: (SEL) comparator
요소 비교 메서드는 인수를 하나 받아서 결과로 NSComparisonResult 형으로 정해진 값을 돌려줘야 한다.
- (NSArray*) sortedArrayUsingFunction: (NSInteger(*) (id, id, void *)) comparator context: (void*) context
세번째 인수 context 에는 임의로 값을 지정할 수 있으므로 이것을 사용해 비교 동작 변경(대소문자 구별 등) 을 추가할 수 있다.
** 요소 메시지 송신
-
배열 객체 요소에 일제히 메시지를 보낼 수 있다.
송신은 배열 앞에서부터 끝가지 순서대로 하나씩 이뤄진다.
요소에 보내는 메시지 송신의 파급 효과로 배열 객체 자체가 변경된다던지 하면 바르게 동작하지 않을 수 있다.
-
- (void) makeObjectsPerformSelector: (SEL) aSelector
- (void) makeObjectsPerformSelector: (SEL) aSelector withObject: (id) anObj
** 파일 입,출력
-
- (NSString*) description
- (id) initWithContentsOfFile: (NSString*) aPath
편의 생성자: arrayWithContentsOfFile:
- (BOOL) writeToFile: (NSString*) path atomically: (BOOL) flag
- (NSString*) componentsJoinedByString: (NSString*) separator
- (NSArray*) pathsMatchingExtensions: (NSArray*) filterTypes
리시버의 각 요소는 파일명을 나타내는 문자열일 때 유효하다.
인수 배열은 파일 확장자를 나타내는 문자열을 요소로 가진다.
인수 배열에 든 확장자를 가진 리시버 요소만 뽑아내어 return 한다.
* 9.4.2. NSMutableArray
** 배열 객체 초기화
-
- (id) initWithCapacity: (NSUInteger) numItems
편의생성자 : arrayWithCapacity:
** 배열에 추가, 치환
-
- (void) addObject: (id) anObject
- (void) addObjectsFromArray: (NSArray*) otherArray
- (void) insertObject: (id) anObject atIndex: (NSUInteger) index
- (void) replaceObjectAtIndex: (NSUinteger) index withObject: (id) anObject
- (void) replaceObjectsInRange: (NSRange) aRange withObjectsFromArray: (NSArray*) otherArray
- (void) setArray: (NSArray*) otherArray
- (void) exchangeObjectAtIndex: (NSUInteger) idx1 withObjectAtIndex: (NSUInteger) idx2
** 객체 삭제
-
- (void) removeAllObjects
- (void) removeLastObject
- (void) removeObjectAtIndex: (NSUInteger) index
- (void) removeObjectsInRange: (NSRange) aRange
- (void) removeObject: (id) anObject
- (void) removeObjectsInArray: (NSArray*) otherArray
** 정렬
-
- (void) sortUsingSelector: (SEL) comparator
* 9.4.3. 배열 객체와 소유권
-
카운터 관리 방식을 이용할 때 배열 객체에 저장된 객체 소유권에 주의해야 한다.
-
배열 객체는 저장된 객체에 retain 메시지를 보내서 유지한다.
또한 배열 객체가 해제될 때는 저장된 객체 모두에 release 메시지를 보낸다.
배열 객체에 저장된 객체를 삭제할 때도 그 객체에 release 메시지를 보낸다.
-
수동 카운터 관리 방식은 다음과 같은 코드를 사용하면 안 된다.
NSArray* arr = [[NSArray alloc] initWithObjects:[[Card alloc] init], [[Player alloc] init], nil];
이 코드를 실행한 객체를 obj 라고 하면 obj 는 배열에 있는 두 요소의 소유자가 된다.
동시에 배열 객체에 저장되어 있으므로 배열 arr 도 요소의 소유권을 가진다.
하지만 나중에 arr 가 해제되더라도 arr 가 가진 소유권은 파기되지만 obj 소유권은 그대로 남아 있어 요소를 해제시킬 수 없다.
ARC 에서는 문제가 없다.
MRC 에서는 아래와 같이 코드가 바뀌어야 한다.
NSArray* arr = [[NSArray alloc] initWithObjects:[[[Card alloc] init] autoRelease], [[[Player alloc] init] autoRelease], nil];
-
마찬가지로 제거할 때도 문제가 된다.
아래와 같이 retain 을 먼저 한 후에 배열에서 remove 를 해야 문제가 없다.
id obj = [[arr objectAtIndex: index] retain] autorelease];
[arr removeObjectAtIndex:index];
-
컬렉션 객체들이 요소를 저장할 때는 retain 메시지를 보내고, 삭제될 때 release 메시지를 보내기 때문에,
MRC 에서는 위와 같은 상황에 항상 주의해야 한다.
** 9.4.4. 고속 열거
-
Objective-C 2.0 에는 배열과 집합, 사전 같은 컬렉션에 저장되는 요소 객체를 반복해서 순서대로 추출하기 위한 구문을 사용할 수 있다.
이 기능을 고속 열거(fast enumeration) 라고 부른다.
-
for in 문의 실행은 컬렉션이 변경 가능하든지 변경 불가능하든지 관계 없으나 변경 가능한 컬렉션은 반복문을 실행하는 사이에 내용을 바꿀 수가 없다.
내용을 바꾸면 예외가 발생해서 실행이 중단된다.
-
컬렉션 배열일 때는 앞에서부터 요소를 추출하고 집합일 때는 요소를 추출하는 순서가 내부 구현에 의존한다.
컬렉션 사전 객체일 때 for..in 문에서 추출하는 것 키이며, 추출하는 순서는 내부 구현에 의존한다.
-
반복문은 중첩이 가능하다.
-
for 괄호 속에서 변수형을 지정할수도 안 할수도 있다.
* 9.4.5. 열거자 NSEnumerator
-
열거자를 나타내는 추상 클래스는 NSEnumerator 로 컬렉션 클래스 인스턴스 메서드에 따라 구체적인 인스턴스가 돌아온다.
이 인스턴스는 임시 객체이며, NSEnumerator 자체에 alloc 등을 송신하는 일은 없다.
-
- (id) nextObject
열거된 다음 요소를 추출한다.
더 이상 추출할 요소가 없으면 nil 을 돌려준다.
- (NSArray*) allObjects
아직 추출하지 않은 모든 요소를 NSArray 형으로 모아 임시 배열 객체로 돌려준다.
다만, 이 메서드를 구현하지 않은 열거자도 있다.
-
열거자를 돌려주는 메서드명은 컬렉션의 클래스에 따라 다르다.
배열의 경우 다음 두가지이다.
- (NSEnumerator*) objectEnumerator
- (NSEnumerator*) reverseObjectEnumerator
-
Idiom
NSEnumerator* enumerator = [array objectEnumerator];
id obj;
while( (obj = [enumerator nextObject]) != nil ){
// do sth..
}
-
변경 가능한 컬렉션 클래스 인스턴스에 열거자를 사용해서 처리할 경우 열거 반복문 안에서 저장된 객체를 삭제하거나 새로운 객체를 추가하는 건 위험하다.
예상하지 못한 결과를 얻게 된다.
-
카운터 관리 방식을 사용할 때 NSEnumerator 인스턴스는 요소를 열거하는 동안 그 컬렉션 객체를 유지한다.
마지막 요소를 추출한 후에 컬렉션 객체에 대한 소유권은 파기된다.
* 9.4.6. 고속 열거자와 열거자
-
고속 열거는 C 언어를 사용해서 효율화를 꾀하기 때문에 매번 메서드를 호출하는 열거자 방법보다 훨씬 빠르게 실행된다.
즉, 고속 열거란 기존 방법인 열거자보다 빠르다는 의미로 그렇게 부른다.
-
열거자는 더 이상 사용되지 않는 걸까?
그렇지 않다.
반복문 속의 처리가 복잡하고 조건에 따라 요소를 건너뛰어야 할 때는 열거자를 사용하는 쪽이 작성하기 쉽다.
또한 열거자 자체도 for..in 문에 사용될 수 있어, 반대 순서 처리 등에 좋다. ( reverseObjectEnumerator 를 in 뒤에 넣는 방법 )
-
고속 열거가 가능한 클래스는 NSFastEnumerator 프로토콜을 구현한다.
열거자 클래스 NSEnumerator 도 이 프로토콜을 구현하므로 for..in 문을 사용할 수 있다.
-
고속 열거를 스스로 만든 클래스에 구현할 수도 있지만 조금 복잡하다.
또한 실제 프로그램에서는 배열 같은 기존 컬렉션으로 충분하다.
** 9.4.7. 집합 클래스
-
배열 객체와는 달리 요소 사이에 순서가 없는, 같은 요소가 여러 번 포함되지 않는 컬렉션을 집합 객체 또는 단순히 집합(set) 이라고 부른다.
-
같은 객체를 여러번 포함될 수 있는 집합을 카운터 포함 집합 (counted set) 또는 백(bag) 이라고 부른다.
NSCountedSet 이 그것이다.
-
+ (id) set
- (id) initWithArray: (NSArray*) array
- (NSUInteger) count
- (NSArray*) allObjects
- (BOOL) containsObject: (id) anObject
- (BOOL) isEqualToSet: (NSSet*) otherSet
- (BOOL) isSubsetOfSet: (NSSet*) otherSet
- (BOOL) intersetcSet: (NSSet*) otherSet
-
NSMutableSet
- (id) initWithCapacity: (NSUInteger) numItems
편의 생성자 : setWithCapacity:
- (void) addObject: (id) anObject
- (void) removeObject: (id) anObject
- (void) unionSet: (NSSet*) otherSet
합집합을 구성한다.
minusSet: 은 공통 요소를 제거한다.
intersectSet: 은 리시버 집합과 인수 집합의 공통 요소만 남긴 교집합을 구성한다.
'프로그래밍 놀이터 > iOS' 카테고리의 다른 글
[Objective-C] 카테고리 (0) | 2018.01.02 |
---|---|
[Objective-C] Foundation 프레임워크의 중요 클래스 - NSDictionary, NSValue, NSNumber, NSURL (0) | 2018.01.01 |
[Objective-C] Foundation 프레임워크의 중요 클래스 - NSString, NSMutableString (0) | 2017.12.30 |
[Objective-C] NSObject 클래스와 런타임 시스템 (0) | 2017.12.29 |
[Objective-C] 선언 프로퍼티 (0) | 2017.12.28 |
댓글