簡體   English   中英

Objective-C:類別中的屬性/實例變量

[英]Objective-C: Property / instance variable in category

由於無法在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。 如何優化上面的代碼?

.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

@lorean的方法將起作用(注意:答案現在已刪除) ,但是您只有一個存儲插槽。 因此,如果您想在多個實例上使用它,並讓每個實例計算一個不同的值,那么它將無法正常工作。

幸運的是,Objective-C運行時具有稱為關聯對象的東西,它可以完全滿足您的需求:

#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

給定的答案非常有效,我的建議只是對其的擴展,避免了編寫過多的樣板代碼。

為了避免為類別屬性重復編寫getter和setter方法,此答案引入了宏。 此外,這些宏還簡化了基本類型屬性(例如intBOOL

沒有宏的傳統方法

傳統上,您定義類別屬性,例如

@interface MyClass (Category)
@property (strong, nonatomic) NSString *text;
@end

然后,您需要使用關聯的對象get選擇器作為鍵來實現getter和setter方法( 請參閱原始答案 ):

#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

我建議的方法

現在,您將使用宏來編寫:

@implementation MyClass (Category)

CATEGORY_PROPERTY_GET_SET(NSString*, text, setText:)

@end

宏定義如下:

#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)

CATEGORY_PROPERTY_GET_SET為給定屬性添加吸氣劑和吸氣劑。 只讀或只寫屬性將分別使用CATEGORY_PROPERTY_GETCATEGORY_PROPERTY_SET宏。

原始類型需要更多注意

由於原始類型不是對象,因此上述宏包含使用unsigned int作為屬性類型的示例。 通過將整數值包裝到NSNumber對象中來實現。 因此,它的用法類似於前面的示例:

@interface ...
@property unsigned int value;
@end

@implementation ...
CATEGORY_PROPERTY_GET_SET_UINT(value, setValue:)
@end

按照這種模式,您可以簡單地添加更多宏以支持signed intBOOL等。

局限性

  1. 默認情況下,所有宏都使用OBJC_ASSOCIATION_RETAIN_NONATOMIC

  2. 重構屬性名稱時,像App Code這樣的IDE當前無法識別設置者的名稱。 您需要自己重命名。

只需使用libextobjc庫:

h文件:

@interface MyClass (Variant)
@property (nonatomic, strong) NSString *test;
@end

m文件:

#import <extobjc.h>
@implementation MyClass (Variant)

@synthesizeAssociation (MyClass, test);

@end

有關@synthesizeAssociation的更多信息

僅在iOS 9上進行過測試示例:將UIView屬性添加到UINavigationBar(類別)

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

另一個可能不使用Associated Objects解決方案,也許更容易,它是在類別實現文件中聲明一個變量,如下所示:

@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

這種實現的缺點是該對象不用作實例變量,而用作類變量。 另外,無法分配屬性屬性(例如在關聯對象(如OBJC_ASSOCIATION_RETAIN_NONATOMIC)中使用)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM