[objective-c] NSArray, NSMutableArray 등에서 입력을 강제하는 방법이 있습니까?

모든 요소가 유형 인 NSMutableArray인스턴스를 만들 수 있습니까 ?SomeClass



답변

-addSomeClass:컴파일시 정적 유형 검사를 허용 하는 메서드로 범주를 만들 수 있습니다 (그러므로 컴파일러는 해당 메서드를 통해 다른 클래스라는 것을 알고있는 객체를 추가하려고하면 알려줄 수 있습니다). 배열에는 주어진 클래스의 객체 만 포함됩니다.

일반적으로 Objective-C에는 이러한 제약이 필요하지 않은 것 같습니다. 경험이 풍부한 Cocoa 프로그래머가 그 기능에 대한 소망을 들어 본 적이 없다고 생각합니다. 다른 언어의 프로그래머로 보이는 유일한 사람들은 여전히 ​​그 언어로 생각하고 있습니다. 주어진 클래스의 객체만을 배열로 원한다면 그 클래스의 객체 만 거기에 고정하십시오. 코드가 제대로 작동하는지 테스트하려면 테스트하십시오.


답변

아직 아무도 여기에 올려 놓지 않았으니 제가 할게요!

이제 Objective-C에서 공식적으로 지원됩니다. Xcode 7부터 다음 구문을 사용할 수 있습니다.

NSArray<MyClass *> *myArray = @[[MyClass new], [MyClass new]];

노트

이는 컴파일러 경고 일 뿐이며 기술적으로 모든 객체를 배열에 삽입 할 수 있다는 점에 유의해야합니다. 모든 경고를 빌드를 방해하는 오류로 바꾸는 스크립트가 있습니다.


답변

이것은 강력한 유형의 언어 (예 : C ++ 또는 Java)에서 Python, Ruby 또는 Objective-C와 같은 더 약하거나 동적으로 유형이 지정된 언어로 전환하는 사람들에게 비교적 일반적인 질문입니다. Objective-C에서 대부분의 객체는 NSObject(type id) 에서 상속되며 ( 나머지는 NSProxy및 같은 다른 루트 클래스에서 상속되며 type id일 수도 있음 ) 모든 메시지를 모든 객체로 보낼 수 있습니다. 물론 인식하지 못하는 인스턴스에 메시지를 보내면 런타임 오류가 발생할 수 있으며 컴파일러 경고가 발생할 수도 있습니다.적절한 -W 플래그 사용). 인스턴스가 보내는 메시지에 응답하는 한 어떤 클래스에 속하는지 신경 쓰지 않을 수 있습니다. 이것은 “오리처럼 꽥꽥 거리는 경우 (예 : 선택자에 응답하는 경우), 오리 (즉, 메시지를 처리 ​​할 수 ​​있고, 어떤 클래스인지 누가 신경 쓰는가)”이기 때문에 “오리 타이핑”이라고도합니다.

-(BOOL)respondsToSelector:(SEL)selector메서드를 사용하여 런타임에 인스턴스가 선택기에 응답하는지 여부를 테스트 할 수 있습니다 . 배열의 모든 인스턴스에서 메서드를 호출하고 싶지만 모든 인스턴스가 메시지를 처리 ​​할 수 ​​있는지 확실하지 않다고 가정합니다. 따라서 NSArray‘s 만 사용할 수는 없습니다 -[NSArray makeObjectsPerformSelector:]. 다음과 같이 작동합니다.

for(id o in myArray) {
  if([o respondsToSelector:@selector(myMethod)]) {
    [o myMethod];
  }
}

호출하려는 메서드를 구현하는 인스턴스의 소스 코드를 제어하는 ​​경우보다 일반적인 접근 방식은 @protocol해당 메서드를 포함하는를 정의하고 해당 클래스가 선언에서 해당 프로토콜을 구현한다고 선언하는 것입니다. 이 사용법에서 a @protocol는 Java 인터페이스 또는 C ++ 추상 기본 클래스와 유사합니다. 그런 다음 각 방법에 대한 응답이 아닌 전체 프로토콜에 대한 적합성을 테스트 할 수 있습니다. 이전 예제에서는 큰 차이가 없지만 여러 메서드를 호출하는 경우 작업을 단순화 할 수 있습니다. 예는 다음과 같습니다.

for(id o in myArray) {
  if([o conformsToProtocol:@protocol(MyProtocol)]) {
    [o myMethod];
  }
}

MyProtocol선언 한다고 가정 합니다 myMethod. 이 두 번째 접근 방식은 첫 번째 방법보다 코드의 의도를 더 명확하게하기 때문에 선호됩니다.

종종 이러한 접근 방식 중 하나를 사용하면 배열의 모든 개체가 지정된 유형인지 여부를 걱정할 필요가 없습니다. 그래도 관심이 있다면 표준 동적 언어 접근 방식은 단위 테스트, 단위 테스트, 단위 테스트입니다. 이 요구 사항의 회귀는 (복구 불가능한) 런타임 (컴파일 시간이 아님) 오류를 생성하기 때문에 크래셔를 야생으로 출시하지 않도록 동작을 확인하기위한 테스트 적용 범위가 있어야합니다. 이 경우 배열을 수정하는 작업을 수행 한 다음 배열의 모든 인스턴스가 지정된 클래스에 속하는지 확인합니다. 적절한 테스트 범위를 사용하면 인스턴스 ID를 확인하는 추가 런타임 오버 헤드도 필요하지 않습니다. 당신은 단위 테스트 커버리지가 훌륭하지 않습니까?


답변

NSMutableArray유형 안전성을 강화하기 위해 하위 클래스 를 만들 수 있습니다.

NSMutableArrayA는 클래스 클러스터는 , 그래서 서브 클래스는 사소한 없습니다. 나는 결국 NSArray그 클래스 내부의 배열 에서 상속을 받고 호출을 전달했습니다. 그 결과 하위 클래스 쉽게 호출 ConcreteMutableArray되는 클래스가 생성됩니다. 내가 생각 해낸 것은 다음과 같습니다.

업데이트 : 클래스 클러스터 서브 클래 싱에 대한 Mike Ash 의이 블로그 게시물을 확인하십시오 .

프로젝트에 해당 파일을 포함시킨 다음 매크로를 사용하여 원하는 유형을 생성하십시오.

MyArrayTypes.h

CUSTOM_ARRAY_INTERFACE(NSString)
CUSTOM_ARRAY_INTERFACE(User)

MyArrayTypes.m

CUSTOM_ARRAY_IMPLEMENTATION(NSString)
CUSTOM_ARRAY_IMPLEMENTATION(User)

용법:

NSStringArray* strings = [NSStringArray array];
[strings add:@"Hello"];
NSString* str = [strings get:0];

[strings add:[User new]];  //compiler error
User* user = [strings get:0];  //compiler error

다른 생각들

  • NSArray직렬화 / 역 직렬화를 지원하기 위해 에서 상 속됨
  • 취향에 따라 다음과 같은 일반적인 방법을 재정의 / 숨기기를 원할 수 있습니다.

    - (void) addObject:(id)anObject


답변

Objective-C에 대한 컴파일 타임 (전 처리기 구현) 제네릭 구현 인 https://github.com/tomersh/Objective-C-Generics를 살펴보십시오 . 블로그 게시물에는 멋진 개요가 있습니다. 기본적으로 컴파일 타임 검사 (경고 또는 오류)를 받지만 제네릭에 대한 런타임 패널티는 없습니다.


답변

이 Github 프로젝트 는 정확히 그 기능을 구현합니다.

그런 다음 <> C # 에서처럼 대괄호 .

그들의 예에서 :

NSArray<MyClass>* classArray = [NSArray array];
NSString *name = [classArray lastObject].name; // No cast needed


답변

가능한 방법은 NSArray를 서브 클래 싱하는 것이지만 Apple은이를 권장하지 않습니다. 형식화 된 NSArray에 대한 실제 필요성을 두 번 생각하는 것이 더 간단합니다.