저는 Mac / iPhone 프로그래밍과 Objective-C를 처음 사용합니다. C #과 Java에는 "generics"가 있는데,이 클래스의 멤버는 선언 된 유형 만 가능합니다. 예를 들어 C #에서
Dictionary<int, MyCustomObject>
정수 키와 MyCustomObject 유형의 값만 포함 할 수 있습니다. Objective-C에도 비슷한 메커니즘이 있습니까?
저는 Mac / iPhone 프로그래밍과 Objective-C를 처음 사용합니다. C #과 Java에는 "generics"가 있는데,이 클래스의 멤버는 선언 된 유형 만 가능합니다. 예를 들어 C #에서
Dictionary<int, MyCustomObject>
정수 키와 MyCustomObject 유형의 값만 포함 할 수 있습니다. Objective-C에도 비슷한 메커니즘이 있습니까?
답변:
Xcode 7에서 Apple은 Objective-C에 'Lightweight Generics'를 도입했습니다. Objective-C에서는 유형이 일치하지 않으면 컴파일러 경고를 생성합니다.
NSArray<NSString*>* arr = @[@"str"];
NSString* string = [arr objectAtIndex:0];
NSNumber* number = [arr objectAtIndex:0]; // Warning: Incompatible pointer types initializing 'NSNumber *' with an expression of type 'NSString *'
그리고 Swift 코드에서 컴파일러 오류가 발생합니다.
var str: String = arr[0]
var num: Int = arr[0] //Error 'String' is not convertible to 'Int'
경량 제네릭은 NSArray, NSDictionary 및 NSSet과 함께 사용하도록 만들어졌지만 자신의 클래스에 추가 할 수도 있습니다.
@interface GenericsTest<__covariant T> : NSObject
-(void)genericMethod:(T)object;
@end
@implementation GenericsTest
-(void)genericMethod:(id)object {}
@end
Objective-C는 컴파일러 경고와 함께 이전처럼 작동합니다.
GenericsTest<NSString*>* test = [GenericsTest new];
[test genericMethod:@"string"];
[test genericMethod:@1]; // Warning: Incompatible pointer types sending 'NSNumber *' to parameter of type 'NSString *'
그러나 Swift는 일반 정보를 완전히 무시합니다. (Swift 3+에서는 더 이상 적용되지 않습니다.)
var test = GenericsTest<String>() //Error: Cannot specialize non-generic type 'GenericsTest'
이러한 Foundation 컬렉션 클래스 외에도 Objective-C 경량 제네릭은 Swift에서 무시됩니다. 경량 제네릭을 사용하는 다른 유형은 마치 매개 변수가없는 것처럼 Swift로 가져옵니다.
MyClass <Foo: id<Bar>>
, 제약 조건을 지정하면 Swift 코드는 값이 제약 조건의 유형이라고 가정 하여 작업 할 무언가 를 제공합니다 . 그러나의 특수 서브 클래스에서는 MyClass
특수 유형이 무시됩니다 (일반적으로와 동일하게 MyClass
표시됨). github.com/bgerstle/LightweightGenerics를
이 답변은 구식이지만 역사적 가치가 있습니다. Xcode 7부터 6 월 '15의 Connor의 답변이 더 정확합니다.
아니요, Objective-C에는 고유 한 사용자 지정 컬렉션 클래스에서 C ++ 템플릿을 사용하지 않는 한 제네릭이 없습니다 (강력하지 않습니다).
Objective-C에는 기능으로 동적 입력이 있습니다. 즉, 모든 객체가 메시지를 수신 할 수 있으므로 런타임에서 객체 유형에 신경 쓰지 않습니다. 내장 컬렉션에 객체를 추가하면 마치 마치 마치 마치 객체처럼 취급됩니다 id
. 그러나 걱정하지 마십시오. 보통과 같은 객체에 메시지를 보내십시오. 컬렉션의 하나 이상의 객체가 보내는 메시지에 응답하지 않는 한 제대로 작동합니다 .
제네릭은 강력한 정적 형식 언어이므로 Java 및 C #과 같은 언어에서 필요합니다. Objective-C의 다이나믹 한 타이핑 기능과는 완전히 다른 볼 게임입니다.
아니요, 그러나 저장하려는 객체 유형으로 주석을 달 수 있음을 분명히하기 위해 요즘 Java 1.4로 무언가를 작성해야 할 때이 작업을 몇 번 수행했습니다. 예 :
NSMutableArray* /*<TypeA>*/ arrayName = ....
또는
NSDictionary* /*<TypeA, TypeB>*/ dictionaryName = ...
Apple은 XCode 7에서 ObjC에 제네릭을 추가했습니다.
@property NSArray<NSDate *>* dates;
- (NSArray<NSDate *> *)datesBeforeDate:(NSDate *)date;
- (void)addDatesParsedFromTimestamps:(NSArray<NSString *> *)timestamps;
이것은 Xcode 7에서 출시되었습니다 (최종!)
Objective C 코드에서는 컴파일 타임 검사일뿐입니다. 컬렉션에 잘못된 유형을 넣거나 유형이 지정된 속성에 할당하는 경우 런타임 오류가 발생하지 않습니다.
알리다:
@interface FooClass <T> : NSObject
@property (nonatomic) T prop;
@end
사용하다:
FooClass<NSString *> *foo = [[FooClass alloc] init];
NSArray<FooClass<NSString *> *> *fooAry = [NSArray array];
그것들에주의하십시오 *
.
일반 NSArray는 서브 클래 싱 NSArray
을 수행하고 제공된 모든 메소드를보다 제한적인 메소드로 재정 의하여 실현할 수 있습니다 . 예를 들어
- (id)objectAtIndex:(NSUInteger)index
에서 재정의해야 할 것입니다
@interface NSStringArray : NSArray
같이
- (NSString *)objectAtIndex:(NSUInteger)index
NSArray가 NSString 만 포함하도록합니다.
작성된 서브 클래스는 드롭 인 대체품으로 사용될 수 있으며 컴파일러 경고, 특성 액세스, 더 나은 코드 작성 및 Xcode의 완성 기능과 같은 많은 유용한 기능을 제공합니다. 이 모든 것이 컴파일 타임 기능이므로 실제 구현을 재정의 할 필요가 없습니다. NSArray의 메소드를 계속 사용할 수 있습니다.
이것을 자동화하고 그것을 두 개의 문장으로 만 요약 할 수 있습니다. 템플릿이 C 전 처리기 매크로로 제공되는 WMGenericCollection을 사용 하여 자동화를 만들었습니다 .
매크로가 포함 된 헤더 파일을 가져온 후에는 인터페이스 용과 구현 용의 두 가지 문을 사용하여 일반 NSArray를 만들 수 있습니다. 저장하려는 데이터 유형과 서브 클래스의 이름 만 제공하면됩니다. WMGenericCollection은 NSArray
, NSDictionary
및에 대한 템플릿 과 NSSet
변경 가능한 해당 템플릿을 제공합니다 .
예 : List<int>
는이라는 사용자 정의 클래스로 실현 될 수 있으며 NumberArray
다음 명령문으로 작성됩니다.
WMGENERICARRAY_INTERFACE(NSNumber *, // type of the value class
// generated class names
NumberArray, MutableNumberArray)
를 만든 후에는 NumberArray
프로젝트의 어느 곳에서나 사용할 수 있습니다. 의 구문은 <int>
없지만 템플릿으로 클래스로 레이블을 지정하기 위해 고유 한 명명 체계를 선택할 수 있습니다.
Apple 및 GNUStep 프레임 워크에서 제공하는 Collections 클래스는 객체, 일부는 정렬 가능하고 일부는 특정 메시지에 응답한다고 가정한다는 점에서 반 제네릭입니다. float, int 등과 같은 프리미티브의 경우 모든 C 배열 구조가 손상되지 않고 사용될 수 있으며 일반 컬렉션 클래스 (예 : NSNumber)에 사용할 특수 래퍼 객체가 있습니다. 또한 Collection 클래스는 모든 유형의 객체를 허용하도록 하위 클래스로 분류되거나 범주를 통해 수정 될 수 있지만 모든 유형 처리 코드를 직접 작성해야합니다. 메시지는 객체로 전송 될 수 있지만 객체에 부적합하거나 메시지가 적절한 객체로 전달되어야하는 경우 null을 반환해야합니다. 실제 유형 오류는 런타임이 아닌 컴파일 타임에 포착되어야합니다. 런타임시 처리되거나 무시되어야합니다. 마지막으로 Objc는 까다로운 사례를 처리 할 수있는 런타임 리플렉션 기능을 제공하며 메시지를 보내거나 부적절한 컬렉션에 넣기 전에 개체에서 메시지 응답, 특정 유형 및 서비스를 확인할 수 있습니다. 서로 다른 라이브러리와 프레임 워크는 코드 응답이없는 메시지를 보낼 때 RTFM과 같이 객체가 작동하는 방식에 대해 다른 규칙을 채택합니다. 장난감 프로그램 및 디버깅 빌드 이외에, 대부분의 프로그램은 실제로 나사를 조여서 잘못된 데이터를 메모리 나 디스크에 쓰거나, 불법적 인 작업을 수행하거나 (예 : 0으로 나누지 만, 잡을 수도 있음) 액세스하지 않으면 충돌하지 않아도됩니다. 시스템 자원을 제한하지 않습니다. Objective-C의 역동 성과 런타임은 일이 정상적으로 실패하고 코드에 내장되어야합니다. (힌트) 당신의 기능에서 일반성에 문제가 있다면, 구체적으로 시도하십시오. 특정 유형으로 함수를 작성하고 런타임시 런타임에서 적절한 멤버 함수를 선택하십시오 (따라서 선택기라고 함).
Example:
-(id) sort (id) obj; // too generic. catches all.
// better
-(id) sort: (EasilySortableCollection*) esc;
-(id) sort: (HardToSortCollection*) hsc;
...
[Sorter sort: MyEasyColl];
[Sorter sort: MyHardColl];