Objective-C : 카테고리의 특성 / 인스턴스 변수
Objective-C의 카테고리에서 합성 된 특성을 작성할 수 없으므로 다음 코드를 최적화하는 방법을 모르겠습니다.
@interface MyClass (Variant)
@property (nonatomic, strong) NSString *test;
@end
@implementation MyClass (Variant)
@dynamic test;
- (NSString *)test {
NSString *res;
//do a lot of stuff
return res;
}
@end
시험 방법은 런타임에 여러 번 호출하고 난 결과를 계산하기 위해 물건을 많이하고 있어요. 일반적으로 합성 속성을 사용하면 메서드를 처음 호출 할 때 IVar _test에 값을 저장하고 다음에이 IVar를 반환합니다. 위의 코드를 어떻게 최적화 할 수 있습니까?
@lorean의 방법이 작동하지만 (참고 : 답변이 삭제되었습니다) 스토리지 슬롯이 하나뿐입니다. 따라서 여러 인스턴스에서 이것을 사용하고 각 인스턴스가 고유 한 값을 계산하게하려면 작동하지 않습니다.
다행히도 Objective-C 런타임에는 원하는 것을 정확하게 수행 할 수있는 Associated Objects 라는 것이 있습니다.
#import <objc/runtime.h>
static void *MyClassResultKey;
@implementation MyClass
- (NSString *)test {
NSString *result = objc_getAssociatedObject(self, &MyClassResultKey);
if (result == nil) {
// do a lot of stuff
result = ...;
objc_setAssociatedObject(self, &MyClassResultKey, result, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return result;
}
@end
.h 파일
@interface NSObject (LaserUnicorn)
@property (nonatomic, strong) LaserUnicorn *laserUnicorn;
@end
.m 파일
#import <objc/runtime.h>
static void * LaserUnicornPropertyKey = &LaserUnicornPropertyKey;
@implementation NSObject (LaserUnicorn)
- (LaserUnicorn *)laserUnicorn {
return objc_getAssociatedObject(self, LaserUnicornPropertyKey);
}
- (void)setLaserUnicorn:(LaserUnicorn *)unicorn {
objc_setAssociatedObject(self, LaserUnicornPropertyKey, unicorn, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
점 표기법으로 액세스 할 수있는 일반 속성처럼
NSObject *myObject = [NSObject new];
myObject.laserUnicorn = [LaserUnicorn new];
NSLog(@"Laser unicorn: %@", myObject.laserUnicorn);
더 쉬운 구문
또는 다음 @selector(nameOfGetter)
과 같이 정적 포인터 키를 만드는 대신 사용할 수 있습니다 .
- (LaserUnicorn *)laserUnicorn {
return objc_getAssociatedObject(self, @selector(laserUnicorn));
}
- (void)setLaserUnicorn:(LaserUnicorn *)unicorn {
objc_setAssociatedObject(self, @selector(laserUnicorn), unicorn, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
자세한 내용은 https://stackoverflow.com/a/16020927/202451을 참조 하십시오.
주어진 대답은 훌륭하게 작동하며 내 제안은 너무 많은 상용구 코드를 작성하지 않는 확장입니다.
카테고리 속성에 대해 getter 및 setter 메소드를 반복적으로 작성하지 않으려면이 답변에 매크로가 도입됩니다. 또한이 매크로는 다음과 같은 원시 형 속성의 사용 용이성 int
또는 BOOL
.
매크로가없는 전통적인 접근 방식
전통적으로 다음과 같은 카테고리 속성을 정의합니다.
@interface MyClass (Category)
@property (strong, nonatomic) NSString *text;
@end
Then you need to implement a getter and setter method using an associated object and the get selector as the key (see original answer):
#import <objc/runtime.h>
@implementation MyClass (Category)
- (NSString *)text{
return objc_getAssociatedObject(self, @selector(text));
}
- (void)setText:(NSString *)text{
objc_setAssociatedObject(self, @selector(text), text, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
My suggested approach
Now, using a macro you will write instead:
@implementation MyClass (Category)
CATEGORY_PROPERTY_GET_SET(NSString*, text, setText:)
@end
The macros are defined as following:
#import <objc/runtime.h>
#define CATEGORY_PROPERTY_GET(type, property) - (type) property { return objc_getAssociatedObject(self, @selector(property)); }
#define CATEGORY_PROPERTY_SET(type, property, setter) - (void) setter (type) property { objc_setAssociatedObject(self, @selector(property), property, OBJC_ASSOCIATION_RETAIN_NONATOMIC); }
#define CATEGORY_PROPERTY_GET_SET(type, property, setter) CATEGORY_PROPERTY_GET(type, property) CATEGORY_PROPERTY_SET(type, property, setter)
#define CATEGORY_PROPERTY_GET_NSNUMBER_PRIMITIVE(type, property, valueSelector) - (type) property { return [objc_getAssociatedObject(self, @selector(property)) valueSelector]; }
#define CATEGORY_PROPERTY_SET_NSNUMBER_PRIMITIVE(type, property, setter, numberSelector) - (void) setter (type) property { objc_setAssociatedObject(self, @selector(property), [NSNumber numberSelector: property], OBJC_ASSOCIATION_RETAIN_NONATOMIC); }
#define CATEGORY_PROPERTY_GET_UINT(property) CATEGORY_PROPERTY_GET_NSNUMBER_PRIMITIVE(unsigned int, property, unsignedIntValue)
#define CATEGORY_PROPERTY_SET_UINT(property, setter) CATEGORY_PROPERTY_SET_NSNUMBER_PRIMITIVE(unsigned int, property, setter, numberWithUnsignedInt)
#define CATEGORY_PROPERTY_GET_SET_UINT(property, setter) CATEGORY_PROPERTY_GET_UINT(property) CATEGORY_PROPERTY_SET_UINT(property, setter)
The macro CATEGORY_PROPERTY_GET_SET
adds a getter and setter for the given property. Read-only or write-only properties will use the CATEGORY_PROPERTY_GET
and CATEGORY_PROPERTY_SET
macro respectively.
Primitive types need a little more attention
As primitive types are no objects the above macros contain an example for using unsigned int
as the property's type. It does so by wrapping the integer value into a NSNumber
object. So its usage is analog to the previous example:
@interface ...
@property unsigned int value;
@end
@implementation ...
CATEGORY_PROPERTY_GET_SET_UINT(value, setValue:)
@end
Following this pattern, you can simply add more macros to also support signed int
, BOOL
, etc...
Limitations
All macros are using
OBJC_ASSOCIATION_RETAIN_NONATOMIC
by default.IDEs like App Code do currently not recognize the setter's name when refactoring the property's name. You would need to rename it by yourself.
Just use libextobjc library:
h-file:
@interface MyClass (Variant)
@property (nonatomic, strong) NSString *test;
@end
m-file:
#import <extobjc.h>
@implementation MyClass (Variant)
@synthesizeAssociation (MyClass, test);
@end
More about @synthesizeAssociation
Tested only with iOS 9 Example: Adding an UIView property to UINavigationBar (Category)
UINavigationBar+Helper.h
#import <UIKit/UIKit.h>
@interface UINavigationBar (Helper)
@property (nonatomic, strong) UIView *tkLogoView;
@end
UINavigationBar+Helper.m
#import "UINavigationBar+Helper.h"
#import <objc/runtime.h>
#define kTKLogoViewKey @"tkLogoView"
@implementation UINavigationBar (Helper)
- (void)setTkLogoView:(UIView *)tkLogoView {
objc_setAssociatedObject(self, kTKLogoViewKey, tkLogoView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIView *)tkLogoView {
return objc_getAssociatedObject(self, kTKLogoViewKey);
}
@end
Another possible solution, perhaps easier, which doesn't use Associated Objects
is to declare a variable in the category implementation file as follows:
@interface UIAlertView (UIAlertViewAdditions)
- (void)setObject:(id)anObject;
- (id)object;
@end
@implementation UIAlertView (UIAlertViewAdditions)
id _object = nil;
- (id)object
{
return _object;
}
- (void)setObject:(id)anObject
{
_object = anObject;
}
@end
The downside of this sort of implementation is that the object doesn't function as an instance variable, but rather as a class variable. Also, property attributes can't be assigned(such as used in Associated Objects like OBJC_ASSOCIATION_RETAIN_NONATOMIC)
참고URL : https://stackoverflow.com/questions/8733104/objective-c-property-instance-variable-in-category
'Programming' 카테고리의 다른 글
C #에서 사전을 JSON 문자열로 어떻게 변환합니까? (0) | 2020.07.16 |
---|---|
vim에서 창을 뒤집는 방법? (0) | 2020.07.16 |
입력 초점에서 텍스트 선택 (0) | 2020.07.16 |
컨트롤러에서 각도 변환에 대한 올바른 사용 (0) | 2020.07.16 |
한 필드에서 두 필드로 값 나누기 (0) | 2020.07.16 |