program tip

NSMutableArray-어레이가 특정 객체 유형 만 보유하도록합니다.

radiobox 2020. 11. 14. 10:06
반응형

NSMutableArray-어레이가 특정 객체 유형 만 보유하도록합니다.


NSMutableArray가 하나의 특정 객체 유형 만 보유하도록 강제하는 방법이 있습니까?

다음과 같이 클래스 정의가 있습니다.

@interface Wheel:NSObject  
{    
  int size;  
  float diameter;  
}  
@end  


@interface Car:NSObject  
{  
   NSString *model;  
   NSString *make;  
   NSMutableArray *wheels;  
}  
@end

배열 이 코드로만 Wheel 객체 를 유지 하도록 강제 할 수 있습니까? (그리고 절대적으로 다른 개체는 아님)


2015 년 업데이트

이 답변은 2011 년 초에 처음 작성되었으며 다음과 같이 시작되었습니다.

우리가 정말로 원하는 것은 다음과 같이 선언 할 수있는 파라 메트릭 다형성입니다 NSMutableArray<NSString>. 그러나 아아 그러한 것은 사용할 수 없습니다.

2015 년에 Apple은 Objective-C에 "경량 제네릭"을 도입하여이를 분명히 변경했으며 이제 다음과 같이 선언 할 수 있습니다.

NSMutableArray<NSString *> *onlyStrings = [NSMutableArray new];

그러나 모든 것이 보이는 것과는 다릅니다. "경량"에 주목하십시오 ... 그런 다음 위 선언의 초기화 부분에 일반 표기법이 포함되어 있지 않다는 점에 유의하십시오. Apple은 파라 메트릭 컬렉션을 도입 하고 위의 배열에 문자열이 아닌 것을 직접 추가했습니다 onlyStrings.

[onlyStrings addObject:@666]; // <- Warning: Incompatible pointer types...

표시된대로 경고를 불법화 할 것입니다. 유형 보안은 피부 깊이가 거의 없습니다. 방법을 고려하십시오.

- (void) push:(id)obj onto:(NSMutableArray *)array
{
   [array addObject:obj];
}

동일한 클래스의 다른 메서드에있는 코드 조각 :

NSMutableArray<NSString *> *oops = [NSMutableArray new];
[self push:@"asda" onto:oops]; // add a string, fine
[self push:@42 onto:oops];     // add a number, no warnings...

Apple이 구현 한 것은 본질적으로 유형이 안전한 제네릭의 풍미가있는 Swift와의 자동 상호 운용을 지원하는 힌팅 시스템입니다. 그러나 Objective-C 측에서는 컴파일러가 몇 가지 추가 힌트를 제공하는 반면, 시스템은 "경량"이며 유형 무결성은 여전히 ​​궁극적으로 프로그래머에게 달려 있습니다. Objective-C 방식이 그렇습니다.

그렇다면 어떤 것을 사용해야합니까? 새로운 경량 / 의사 제네릭 또는 코드에 대한 자체 패턴을 고안 하시겠습니까? 정답은 없습니다. 시나리오에서 의미가있는 것이 무엇인지 파악하고 사용하십시오.

예 : Swift와의 상호 운용을 목표로하는 경우 경량 제네릭을 사용해야합니다! 그러나 컬렉션의 유형 무결성이 시나리오에서 중요한 경우에는 Swift가 해당하는 유형 무결성을 적용하는 Objective-C 측의 자체 코드와 경량 제네릭을 결합 할 수 있습니다.

2011 년 해답의 나머지

여기에 또 다른 옵션은 NSMutableArray의 빠른 일반 하위 클래스로, 단형 배열에서 원하는 종류의 객체로 초기화합니다. 이 옵션은 정적 유형 검사를 제공하지 않으며 (Obj-C에서 얻은만큼) 잘못된 유형을 삽입 할 때 런타임 예외가 발생합니다.

이것은 철저히 테스트 되지 않았으며 NSMutableArray 재정의에 대한 문서가 정확하다고 가정합니다.

@interface MonomorphicArray : NSMutableArray
{
    Class elementClass;
    NSMutableArray *realArray;
}

- (id) initWithClass:(Class)element andCapacity:(NSUInteger)numItems;
- (id) initWithClass:(Class)element;

@end

그리고 구현 :

@implementation MonomorphicArray

- (id) initWithClass:(Class)element andCapacity:(NSUInteger)numItems
{
    elementClass = element;
    realArray = [NSMutableArray arrayWithCapacity:numItems];
    return self;
}

- (id) initWithClass:(Class)element
{
    elementClass = element;
    realArray = [NSMutableArray new];
    return self;
}

// override primitive NSMutableArray methods and enforce monomorphism

- (void) insertObject:(id)anObject atIndex:(NSUInteger)index
{
    if ([anObject isKindOfClass:elementClass]) // allows subclasses, use isMemeberOfClass for exact match
    {
        [realArray insertObject:anObject atIndex:index];
    }
    else
    {
        NSException* myException = [NSException
            exceptionWithName:@"InvalidAddObject"
            reason:@"Added object has wrong type"
            userInfo:nil];
        @throw myException;
    }
}

- (void) removeObjectAtIndex:(NSUInteger)index
{
    [realArray removeObjectAtIndex:index];
}

// override primitive NSArray methods

- (NSUInteger) count
{
    return [realArray count];
}

- (id) objectAtIndex:(NSUInteger)index
{
    return [realArray objectAtIndex:index];
}


// block all the other init's (some could be supported)

static id NotSupported()
{
    NSException* myException = [NSException
        exceptionWithName:@"InvalidInitializer"
        reason:@"Only initWithClass: and initWithClass:andCapacity: supported"
        userInfo:nil];
    @throw myException;
}

- (id)initWithArray:(NSArray *)anArray { return NotSupported(); }
- (id)initWithArray:(NSArray *)array copyItems:(BOOL)flag { return NotSupported(); }
- (id)initWithContentsOfFile:(NSString *)aPath { return NotSupported(); }
- (id)initWithContentsOfURL:(NSURL *)aURL { return NotSupported(); }
- (id)initWithObjects:(id)firstObj, ... { return NotSupported(); }
- (id)initWithObjects:(const id *)objects count:(NSUInteger)count { return NotSupported(); }

@end

로 사용:

MonomorphicArray *monoString = [[MonomorphicArray alloc] initWithClass:[NSString class] andCapacity:3];

[monoString addObject:@"A string"];
[monoString addObject:[NSNumber numberWithInt:42]]; // will throw
[monoString addObject:@"Another string"];

XCode 7 제네릭을 이제 Objective-C에서 사용할 수 있습니다!

따라서 다음 NSMutableArray과 같이 선언 할 수 있습니다 .

NSMutableArray <Wheel*> *wheels = [[NSMutableArray alloc] initWithArray:@[[Wheel new],[Wheel new]];

배열에 휠이 아닌 개체를 넣으려고하면 컴파일러에서 경고를 표시합니다.


나는 틀릴 수 있지만 (저는 멍청한 일입니다), 사용자 정의 프로토콜을 만들고 배열에 추가하는 객체가 동일한 프로토콜을 따르는 지 확인하면 배열을 선언 할 때 사용합니다.

NSArray<Protocol Name>

이 프로토콜을 따르지 않는 개체가 추가되는 것을 방지해야합니다.


내가 아는대로 .. 바퀴 mutableArray에 개체를 추가하기 전에 확인 표시를 추가해야합니다. 내가 추가하는 객체는 "휠"클래스입니다. 그렇다면 추가하면 안됩니다.

예:

if([id isClassOf:"Wheel"] == YES)
{
[array addObject:id) 
}

이 같은. 정확한 구문이 기억 나지 않습니다.


나는 이것이 도움이되기를 바랍니다 (그리고 작동 ... : P)

Wheel.h 파일 :

@protocol Wheel
@end

@interface Wheel : NSObject
@property ...
@end

Car.h 파일 :

#import "Wheel.h"
@interface Car:NSObject  

{  
   NSString *model;  
   NSString *make;  
   NSMutableArray<Wheel, Optional> *wheels;  
}  
@end

Car.m 파일 :

#import "Car.h"
@implementation Car

-(id)init{
   if (self=[super init]){
   self.wheels = (NSMutableArray<Wheel,Optional>*)[NSMutableArray alloc]init];
   }
return self;
}
@end

Xcode 7을 사용하면 배열, 사전 및 자신의 클래스를 제네릭으로 정의 할 수 있습니다. 배열 구문은 다음과 같습니다.

NSArray<NSString*>* array = @[@"hello world"];

나는 그것을 NSMutableArray상자 에서 꺼내는 방법이 없다고 믿습니다 . 모든 생성자와 삽입 메서드를 서브 클래 싱하고 재정 의하여이를 시행 할 수 있지만 그럴 가치가 없을 것입니다. 이것으로 무엇을 성취하고 싶습니까?


그건 불가능하다; NSArray (변경 가능 여부에 관계없이)는 모든 객체 유형을 보유합니다. 할 수있는 일은 이미 Jim이 제안한대로 사용자 정의 하위 클래스를 만드는 것입니다. 또는 원하는 유형이 아닌 객체를 제거하기 위해 배열을 필터링하려면 다음을 수행 할 수 있습니다.

- (void)removeObjectsFromArray:(NSMutableArray *)array otherThanOfType:(Class)type
{
    int c = 0;
    while(c < [array length])
    {
        NSObject *object = [array objectAtIndex:c];
        if([object isKindOfClass:type])
          c++;
        else
          [array removeObjectAtIndex:c];
    }
}

...
[self removeObjectsFromArray:array otherThanOfType:[Car class]];

Or make other judgments based on the result of isKindOfClass:, e.g. to divide an array containing a mixture of Cars and Wheels into two arrays, each containing only one kind of object.


You can use the nsexception if you dont have the specific object.

for (int i = 0; i<items.count;i++) {
 if([[items objectAtIndex:i] isKindOfClass:[Wheel class]])
 {
  // do something..!
 }else{
  [NSException raise:@"Invalid value" format:@"Format of %@ is invalid", items];
  // do whatever to handle or raise your exception.
 }
}

Here's something I've done to avoid subclassing NSMutableArray: use a category. This way you can have the argument and return types you want. Note the naming convention: replace the word "object" in each of the methods you will use with the name of the element class. "objectAtIndex" becomes "wheelAtIndex" and so on. This way there's no name conflict. Very tidy.

typedef NSMutableArray WheelList;
@interface NSMutableArray (WheelList) 
- (wheel *) wheelAtIndex: (NSUInteger) index;
- (void) addWheel: (wheel *) w;
@end

@implementation NSMutableArray (WheelList)

- (wheel *) wheelAtIndex: (NSUInteger) index 
{  
    return (wheel *) [self objectAtIndex: index];  
}

- (void) addWheel: (wheel *) w 
{  
    [self addObject: w];  
} 
@end


@interface Car : NSObject
@property WheelList *wheels;
@end;


@implementation Car
@synthesize wheels;

- (id) init 
{
    if (self = [super init]) {
        wheels = [[WheelList alloc] initWithCapacity: 4];
    }
    return self;
}

@end

protocol maybe a good idea:

@protocol Person <NSObject>
@end

@interface Person : NSObject <Person>
@end

to use:

NSArray<Person>*  personArray;

There is one-header file project which allows this: Objective-C-Generics

Usage:

Copy ObjectiveCGenerics.h to your project. When defining a new class use the GENERICSABLE macro.

#import "ObjectiveCGenerics.h"

GENERICSABLE(MyClass)

@interface MyClass : NSObject<MyClass>

@property (nonatomic, strong) NSString* name;

@end

Now you can use generics with arrays and sets just as you normally do in Java, C#, etc.

Code: enter image description here

참고URL : https://stackoverflow.com/questions/5197446/nsmutablearray-force-the-array-to-hold-specific-object-type-only

반응형