objective c 클래스 목표 C 범주의 인스턴스 변수



자바 인스턴스 변수 초기화 (7)

카테고리에 인스턴스 변수를 추가해야하는 상황이 있지만 Apple의 문서에서 알 수없는 상황이 있습니다. 그래서 최선의 대안이나 해결 방법이 무엇인지 궁금합니다.

UIViewController에 기능을 추가하는 범주를 추가하고 싶습니다. 내 모든 다른 UIViewControllers 어떤 특정 UIViewController 하위 확장 할 상관없이 찾을 것이라고, 그래서 범주를 생각하는 가장 좋은 솔루션입니다. 이 기능을 구현하려면 여러 가지 다른 방법이 필요하며 그 둘 사이의 데이터를 추적해야하므로 인스턴스 메서드를 만들고 싶습니다.

도움이되는 경우, 여기 내가 특별히하고 싶은 것이 있습니다. 소프트웨어 키보드가 숨겨지고 표시 될 때 추적하기 쉽도록하고 싶기 때문에 내보기에서 내용의 크기를 조정할 수 있습니다. 확실하게 할 수있는 유일한 방법은 네 가지 UIViewController 메서드에 코드를 넣고 인스턴스 변수에서 추가 데이터를 추적하는 것입니다. 따라서 이러한 메서드와 인스턴스 변수를 범주에 넣으 려하므로 소프트웨어 키보드를 처리 할 때마다 복사하여 붙여 넣을 필요가 없습니다. (이 정확한 문제에 대한 간단한 해결책이 있다면 괜찮습니다.하지만 나중에 참조 할 수 있도록 카테고리 인스턴스 변수에 대한 대답을 알고 싶습니다.)


Answer #1

이것은 내장 ObjC 기능 인 Associated Objects (Associated References)를 사용하는 것이 가장 좋은데, 아래 예제에서 카테고리로 변경하고 associatedObject를 변수 이름으로 바꿉니다.

NSObject + AssociatedObject.h

@interface NSObject (AssociatedObject)
@property (nonatomic, strong) id associatedObject;
@end

NSObject + AssociatedObject.m

#import <objc/runtime.h>

@implementation NSObject (AssociatedObject)
@dynamic associatedObject;

- (void)setAssociatedObject:(id)object {
     objc_setAssociatedObject(self, @selector(associatedObject), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (id)associatedObject {
    return objc_getAssociatedObject(self, @selector(associatedObject));
}

전체 자습서는 다음을 참조하십시오.

http://nshipster.com/associated-objects/


Answer #2

최근에이 작업을 수행해야했습니다 (카테고리에 상태 추가). @Dave DeLong은 이에 대해 정확한 시각을 가지고 있습니다. 최고의 접근 방식을 연구하면서 Tom Harrington의 훌륭한 블로그 게시물 을 발견했습니다. @ JeremyP의 @property 선언을 Category에서 사용하는 것이 좋지만 특정 구현 (글로벌 싱글 톤 팬이 아니거나 글로벌 참조를 보유하지는 않음)이 아닙니다. 연관 참조는 갈 길이 멀다.

카테고리에 ivars를 추가하는 코드는 다음과 같습니다. 나는 이것에 대해 here 에 대해 자세히 블로깅 here .

File.h에서 호출자는 깨끗하고 높은 수준의 추상화 만 볼 수 있습니다.

@interface UIViewController (MyCategory)
@property (retain,nonatomic) NSUInteger someObject;
@end

File.m에서는 @property를 구현할 수 있습니다 (참고 : @ synthesize'd는 사용할 수 없습니다).

@implementation UIViewController (MyCategory)

- (NSUInteger)someObject
{
  return [MyCategoryIVars fetch:self].someObject;
}

- (void)setSomeObject:(NSUInteger)obj
{
  [MyCategoryIVars fetch:self].someObject = obj;
}

또한 MyCategoryIVars 클래스를 선언하고 정의해야합니다. 이해를 쉽게하기 위해 적절한 컴파일 순서로 설명했습니다. @interface는 Category @implementation 앞에 위치해야합니다.

@interface MyCategoryIVars : NSObject
@property (retain,nonatomic) NSUInteger someObject;
+ (MyCategoryIVars*)fetch:(id)targetInstance;
@end

@implementation MyCategoryIVars

@synthesize someObject;

+ (MyCategoryIVars*)fetch:(id)targetInstance
{
  static void *compactFetchIVarKey = &compactFetchIVarKey;
  MyCategoryIVars *ivars = objc_getAssociatedObject(targetInstance, &compactFetchIVarKey);
  if (ivars == nil) {
    ivars = [[MyCategoryIVars alloc] init];
    objc_setAssociatedObject(targetInstance, &compactFetchIVarKey, ivars, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    [ivars release];
  } 
  return ivars;
}

- (id)init
{
  self = [super init];
  return self;
}

- (void)dealloc
{
  self.someObject = nil;
  [super dealloc];
}

@end

위의 코드는 ivars (someObject)를 보유하고있는 클래스를 선언하고 구현합니다. UIViewController를 실제로 확장 할 수 없기 때문에이 작업을 수행해야합니다.


Answer #3

그것은 많은 문서의 온라인에서 당신이 카테고리에서 새로운 변수를 만들 수는 없다고 언급했으나 그것을 달성하는 아주 간단한 방법을 발견했습니다. 다음은 카테고리에 새로운 변수를 선언하는 방법입니다.

.h 파일에서

@interface UIButton (Default)

   @property(nonatomic) UIColor *borderColor;

@end  

.m 파일에서

#import <objc/runtime.h>
static char borderColorKey;

@implementation UIButton (Default)

- (UIColor *)borderColor
{
    return objc_getAssociatedObject(self, &borderColorKey);
}

- (void)setBorderColor:(UIColor *)borderColor
{
    objc_setAssociatedObject(self, &borderColorKey,
                             borderColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    self.layer.borderColor=borderColor.CGColor;
}

@end

이제 새로운 변수가 생겼습니다.


Answer #4

단순히 UIViewController 의 하위 클래스를 만들고 그 기능을 추가 한 다음 그 클래스 (또는 하위 클래스)를 사용하는 것이 좋습니다.


Answer #5

Obj-C 런타임을 사용하여 클래스에 변수를 추가 할 수 있다고 생각합니다.
나는 이 토론 도 발견했다.


Answer #6

나는 지금 카테고리에 합성 된 속성을 추가하는 것이 가능하다고 믿고 인스턴스 변수는 자동적으로 생성된다. 그러나 결코 시도하지 않았으므로 그것이 작동 할 지 확신 할 수 없다.

더 해킹 된 솔루션 :

UIViewController를 키 (또는 NSValue로 래핑 된 주소)와 속성 값을 값으로 가지는 싱글 톤 NSDictionary를 만듭니다.

속성을 가져 오거나 설정하기 위해 사전에 실제로가는 속성에 대한 getter 및 setter를 만듭니다.

@interface UIViewController(MyProperty)

@property (nonatomic, retain) id myProperty;
@property (nonatomic, readonly, retain) NSMutableDcitionary* propertyDictionary;

@end

@implementation  UIViewController(MyProperty)

-(NSMutableDictionary*) propertyDictionary
{
    static NSMutableDictionary* theDictionary = nil;
    if (theDictionary == nil)
    {
        theDictioanry = [[NSMutableDictionary alloc] init];
    }
    return theDictionary;
}


-(id) myProperty
{
    NSValue* key = [NSValue valueWithPointer: self];
    return [[self propertyDictionary] objectForKey: key];
}

-(void) setMyProperty: (id) newValue
{
    NSValue* key = [NSValue valueWithPointer: self];
    [[self propertyDictionary] setObject: newValue forKey: key];    
}

@end

위의 접근법에 대한 두 가지 잠재적 인 문제점 :

  • 할당 해제 된 뷰 컨트롤러의 키를 제거 할 방법이 없습니다. 당신이 한줌 만 추적하는 한, 그것은 문제가되어서는 안됩니다. 또는 사전 작업을 마쳤 으면 사전에서 키를 삭제하는 메소드를 추가 할 수 있습니다.
  • NSValue의 isEqual : 메서드가 내용을 비교 (즉, 래핑 된 포인터)하여 평등을 판별하지 않거나 비교 대상이 정확히 동일한 NSValue인지 확인합니다. 후자의 경우 NSValue 대신 NSNumber를 키에 사용해야합니다 ( NSNumber numberWithUnsignedLong: 32 비트 및 64 비트 플랫폼 모두에서 트릭을 수행합니다).

Answer #7

예, 당신이 할 수 있지만, 당신이 묻고 있기 때문에, 나는 물어야한다 : 당신은 절대적으로 당신이 필요하다고 확신합니까? ( "예"라고 대답 한 다음 돌아가서 원하는 것을 파악하고 다른 방법이 있는지 확인하십시오)

그러나 실제로 제어하지 않는 클래스에 저장소를 삽입하려는 경우 연관 참조를 사용 하십시오 .





categories