KVC 집합 접근자/변경자 메소드 작성방법

키밸류 코딩의 집합 접근자/변경자 메소드

to-many 관계의 프로퍼티에 대한 조정은 키밸류 코딩에서 배열 프록시를 통해서 이루어진다고 했다. 이 때 개별 원소를 추가/삭제/교체하는 작업의 효율을 높이고, 각 동작에 대해서도 KVO 지원을 가능하게 하기 위해서 배열 프록시와 연계하여 동작할 수 있는 집합 메소드를 추가로 정의하는 것이 강력하게 권장된다. 이들 메소드들은 기본적으로 NSMutableArray의 기본적인 액세스 메소드들에 키 이름이 혼합된 형태로, 일정한 규칙에 의해 이름 지어진다. 단, 키 이름이 메소드 이름에 들어가기 때문에 메소드명이 고정되지 않았고, 따라서 NSKeyValueCoding 레퍼런스 상에서는 소개되지 않는다.
집합 접근자 및 변경자들은 기본적으로 NSMuatbleArray의 몇몇 메소드들의 이름에 키 이름을 추가하여 변형한 것이다. 집합메소드에서 키 이름은 <key>로 쓰여진다. 키 이름이 대문자로  시작해야하는 경우는 <Key>로 쓰였으니 잘 구분해서 사용해야 한다.

구분 Array 메소드 KVC 집합 메소드 비고
접근자 -count -countOf<Key> 필수
-objectAtIndex: -objectIn<Key>AtIndex: 둘 중 하나 필수
-objectsAtIndexes: -<key>AtIndexes:
-getObjects:range: -getObjectsIn<Key>:range: 선택적
변경자 -insertObject:atIndex: -insertObject:in<Key>AtIndex: 배열 변경이 필요한 경우에는 필수
-removeObjectAtIndex: -removeObjectFrom<Key>AtIndex:
-replaceObjectAtIndex:withObject: -replaceObjectIn<Key>AtIndex:withObject: 선택적

 

Swift 버전을 생각하기

Swift에서는 몇가지 고려해야 하는 부분이 있다.  (Swift4, Xcode9 버전 대응)

  1. KVC는 Objective-C 런타임의 기능이므로 이름 패턴 검색 정책 자체는 달라지는 점이 없다.
  2. 집합 메소드 프로퍼티는 모두 런타임에 이름을 통해서 액세스될 수 있어야 하므로 @objc를 통해서 런타임에 노출되어야 한다.
  3. insert... 를 제외하고는 메소드 이름을 지을 때,  ...AtIndex(_:)...(atIndex:)나 차이가 없다. 예를 들어 Swift 메소드명, objectInEmployeesAtIndex(_ index:)objectInEmployees(atIndex:) 모두 Objective-C에서는 -objectInEmployeesAtIndex:로 해석된다.
  4. 하지만 insertObject(_:...)insert(object:...)로 바꿔썼을 때 제대로 찾지 못한다.[^1] 즉, 파라미터 개수가 2개 이상인 경우는 명시적으로 밖으로 빼서 첫번째 파라미터를 숨겨야 한다. 이것은 Swift2 버전의 기본 메소드 이름 규칙인데, Objective-C 런타임에 적용된 이름 규칙이 오래전 버전인 것 같다.
  5. 이름 해석 규칙이 애매할 수도 있고, Xcode 버전이 올라감에 따라 다시 다른 규칙이 적용될지 모른다. 따라서 가장 좋은 방법은 @objc() 변경자에서 Objective-C 런타임상의 이름을 같이 써주는게 좋다. (그러면 Swift 이름은 뭐 어떻게 짓든 상관없다.)
  6. 그외 타입규칙
    1. 인덱스를 가리키는 NSUIntegerInt로 번역된다.
    2. Element의 타입은 Any, AnyObject가 아닌 소속 클래스 타입을 그대로 쓰면 된다. 다만 어떤 접근자이든 옵셔널타입을 쓰지는 않는다. 옵셔널 타입으로 파라미터나 리턴타입을 정의하면 올바른 메소드 이름이 아닌 것으로 간주되니 주의할 것.

다음 예에서 Foo 클래스의 words프로퍼티에 대한 집합 접근자/변경자는 다음과 같이 작성할 수 있다.

/// 프로퍼티 선언
/// Objective-C
@property (strong, nonatmoic) NSMutableArray<NSString *>* words;
/// Swift
/// setter에 의한 교체를 추적하려면 @objc dynamic var... 로 선언해야 하지만
/// 배열 프록시를 통해서 제어하는 경우, 굳이 해당 프로퍼티가 노출되지 않아도 된다.
var words:[String] = []
/// 집합 액세스 메소드
/// 꼭 필요하지는 않다.
/// Swift의 경우 @objc 를 붙이지 않았다면 이들을 구현해서
/// 배열 프록시로 동작하게 할 수 있다.
/// Objective-C
- (NSUInteger)countOfWords {
    return [_words count];
}
- (NSString*)objectInWordsAtIndex:(NSUInteger)index
{
    return [_words objectAtIndex:index];
}
/// Swift
@objc func countOfWords() -> Int {
  return words.count
}
@objc func objectInWord(atIndex index: Int) -> String
{
  return words[index]
}
/// 변경자
/// Objective-C
- (void)insertObject:(NSString*)object inWordsAtIndex:(NSUInteger)index
{
    [_words insertObject:object atIndex:index];
}
- (void)removeObjectFromWordsAtIndex:(NSUInteger)index
{
   [_words removeAtIndex:index];
}
/// swift
@objc(insertObject:inWordsAtIndex:)
func insert(words: String, at index:Int) {
  words.insert(words, at:index)
}
@objc(removeFromWordsAtIndex:)
func remove(at index:Int) {
  words.remove(at:index)
}

참고자료

Read more

워드프레스에서 고스트로 이전

워드프레스에서 고스트로 이전

이 글을 쓰면서도 믿기 힘든 사실인데, 블로그라는 걸 처음 시작한지가 20년이 되었습니다. 이글루스에서 처음 시작했다가, SK컴즈가 인수한다고 발표함과 동시에 워드프레스로 플랫폼을 옮겼죠. 워드프레스오 옮긴 이후에는 호스팅 환경을 이리 저리 옮기긴 했지만 거의 18년 가까이 워드프레스를 사용해온 것 같습니다. 그 동안 워드프레스는 블로깅 툴에서 명실상부한 범용CMS로 발전했습니다. 사실 웬만한 홈페이지들은 이제

By sooop
띄어쓰기에 대한 생각

띄어쓰기에 대한 생각

업무 메일을 쓸 때 가장 많이 쓰는 말 중에 하나가 메일 말미에 ‘업무에 참고 부탁 드립니다.‘인데요, 어느 날부터 아웃룩에서 이 ‘부탁 드립니다’가 틀렸다고 맞춤법 지적을 하기 시작했습니다. 맞는 말은 ‘부탁드립니다’라고 붙여 쓰는 거라고. 사실 아래아한글 시절부터 이전의 MS워드까지, 워드프로세서들의 한국어 맞춤법 검사 실력은 거의 있으나 마나 한

By sooop

구글 포토에서 아이클라우드로 탈출한 후기

한 때 구글 포토가 백업 용량을 무제한으로 제공해 주겠다고해서, 구글 포토를 사용해서 사진을 백업해왔습니다. 물론 이 이야기의 결말은 저나 이 글을 읽고 있는 여러분이나 모두 알고 있습니다. 사실 AI에게 학습 시킬 이미지 데이터를 모으기 위한 것일 뿐이라거나 하는 이야기는 그 당시에도 있었습니다만, 에이 그래도 구글인데 용량은 넉넉하게 주겠지…하는 순진한

By sooop

Julia의 함수 사용팁

연산자의 함수적 표기 Julia의 연산자는 기본적으로 함수이며, 함수 호출 표기와 같은 방식으로 호출하는 것이 가능합니다. 또한 그 자체로 함수이기 때문에 filter(), map() 과 같이 함수를 인자로 받는 함수에도 연산자를 그대로 적용하는 것이 가능합니다. 특히 + 연산자는 sum() 함수와 같이 여러 인자를 받아 인자들의 합을 구할 수 있습니다. 2 + 3 # = 5 +(2,

By sooop