Objective-C中的@property

1.@property是什么

@Property是聲明屬性的語(yǔ)法,它可以快速方便的為實(shí)例變量創(chuàng)建存取器,并允許我們通過(guò)點(diǎn)語(yǔ)法使用存取器。

存取器(accessor):指用于獲取和設(shè)置實(shí)例變量的方法。用于獲取實(shí)例變量值的存取器是getter,用于設(shè)置實(shí)例變量值的存取器是setter

2.創(chuàng)建存取器

2.1 手工創(chuàng)建存取器

我們先看兩段代碼:

// Car.h

#import <Foundation/Foundation.h>

@interface Car : NSObject
{
    // 實(shí)例變量
    NSString *carName;
    NSString *carType;
}

// setter                                   
- (void)setCarName:(NSString *)newCarName; 
                                            
// getter
- (NSString *)carName;

// setter
- (void)setCarType:(NSString *)newCarType;

// getter
- (NSString *)carType;

@end  

上面的代碼中carNamecarType就是Car的實(shí)例變量,并且可以看到分別對(duì)這兩個(gè)實(shí)例變量聲明了get/set方法,即存取器。

#import "Car.h"

@implementation Car

// setter
- (void)setCarName:(NSString *)newCarName
{
    carName = newCarName;
}

// getter
- (NSString *)carName
{
    return carName;
}

// setter
- (void)setCarType:(NSString *)newCarType
{
    carType = newCarType;
}

// getter
- (NSString *)carType
{
    return carType;
}

@end

上面代碼是對(duì)實(shí)例變量存取器的實(shí)現(xiàn)。我們可以看到,存取器就是對(duì)實(shí)例變量進(jìn)行賦值和取值。按照約定賦值方法以set開(kāi)頭,取值方法以實(shí)例變量名命名。
我們看看如何使用:

// main.m

#import "Car.h"

int main(int argc, char * argv[])
{
    @autoreleasepool {
        Car *car = [[Car alloc] init];
        [car setCarName:@"Jeep Cherokee"];
        [car setCarType:@"SUV"];
        NSLog(@"The car name is %@ and the type is %@",[car carName],[car carType]);      
    }
    return 0;
}

上面的代碼中我們注意到,對(duì)象Car使用了消息語(yǔ)法,也就是使用方括號(hào)的語(yǔ)法給存取器發(fā)送消息。
返回結(jié)果為:

The car name is Jeep Cherokee and the type is SUV

2.2 使用@Property創(chuàng)建存取器

// Car.h

#import <Foundation/Foundation.h>

@interface Car : NSObject
{
    // 實(shí)例變量
    NSString *carName;
    NSString *carType;
}

@property(nonatomic,strong) NSString *carName;
@property(nonatomic,strong) NSString *carType;

@end

上面代碼中,我們使用@property聲明兩個(gè)屬性,名稱與實(shí)例變量名稱相同(讓我們先忽略nonatomicstrong)。

// Car.m

#import "Car.h"

@implementation Car

@synthesize carName;
@synthesize carType;

@end

在.m文件中我們使用@synthesize自動(dòng)生成這兩個(gè)實(shí)例變量的存取器,并且隱藏了存取器,雖然我們看不到存取器,但它們確實(shí)是存在的。

// main.m

int main(int argc, char * argv[])
{
    @autoreleasepool {
        Car *car = [[Car alloc] init];
        car.carName = @"Jeep Compass";
        car.carType = @"SUV";
        NSLog(@"The car name is %@ and the type is %@",car.carName,car.carType); 
    }
    return 0;
}

在上面的代碼中我們可以注意到,Car對(duì)象使用點(diǎn)語(yǔ)法給存取器發(fā)送消息,并且get與set的語(yǔ)法是相同的,所以這里的點(diǎn)語(yǔ)法可以根據(jù)語(yǔ)境判斷我們是要賦值還是取值。

當(dāng)然我們也依然可以使用消息語(yǔ)法來(lái)使用:

// main.m

int main(int argc, char * argv[])
{
    @autoreleasepool {
        Car *car = [[Car alloc] init];
        
        // 點(diǎn)語(yǔ)法
//        car.carName = @"Jeep Compass";
//        car.carType = @"SUV";
//        NSLog(@"The car name is %@ and the type is %@",car.carName,car.carType);
    
        // 消息語(yǔ)法
        [car setCarName:@"Jeep Compass"];
        [car setCarType:@"SUV"];
        NSLog(@"The car name is %@ and the type is %@",[car carName],[car carType]);       
    }
    return 0;
}

上面兩段代碼的執(zhí)行結(jié)果都是:

The car name is Jeep Compass and the type is SUV

總結(jié):@property等同于在.h文件中聲明實(shí)例變量的get/set方法,@synthesize等同于在.m文件中實(shí)現(xiàn)實(shí)例變量的get/set方法。使用@propertysynthesize創(chuàng)建存取器要比手動(dòng)聲明兩個(gè)存取方法(gettersetter)更簡(jiǎn)單。而且我們?cè)谑褂脤傩詴r(shí)可以使用點(diǎn)語(yǔ)法賦值或取值,語(yǔ)法更簡(jiǎn)單,更符合面向?qū)ο缶幊獭?/p>

3.不必單獨(dú)聲明示例變量

如果使用@Property,就不必單獨(dú)聲明實(shí)例變量了。因?yàn)樵跊](méi)有顯示提供示例變量聲明的前提下,系統(tǒng)會(huì)自動(dòng)幫你生成實(shí)例變量。我們通過(guò)以下代碼來(lái)說(shuō)明:

// Car.h

#import <Foundation/Foundation.h>

@interface Car : NSObject

@property(nonatomic,strong) NSString *carName;
@property(nonatomic,strong) NSString *carType;

- (NSString *)carInfo;

@end

在.h文件中我們并沒(méi)有聲明實(shí)例變量,只是聲明了carNamecarType兩個(gè)屬性,以及一個(gè)carInfo方法,返回值為NSString *

// Car.m

#import "Car.h"

@implementation Car

- (NSString *)carInfo
{
    return [NSString stringWithFormat:@"The car name is %@ and the type is %@",_carName,_carType];
}

@end

在.m文件中我們可以注意到,在carInfo方法中我們使用了_carName_carType實(shí)例變量,這就是當(dāng)我們沒(méi)有顯示聲明實(shí)例變量時(shí),系統(tǒng)為我們自動(dòng)生成的。命名規(guī)則是以_為前綴,加上屬性名,即_propertyName

其實(shí)在.m文件中實(shí)際是存在@synthesize聲明語(yǔ)句的,只是系統(tǒng)將其隱藏了:

@synthesize carName = _carName;
@synthesize carType = _carType;

那么如果我們不喜歡默認(rèn)的實(shí)例變量命名方法,或者我們希望使用更有語(yǔ)義的名稱,應(yīng)該怎么做呢。其實(shí)很簡(jiǎn)單:

// Car.m

#import "Car.h"

@implementation Car

@synthesize carName = i_am_car_name;
@synthesize carType = i_am_car_type;

- (NSString *)carInfo
{
    return [NSString stringWithFormat:@"The car name is %@ and the type is %@",i_am_car_name,i_am_car_type];
}

@end

通過(guò)上述代碼可以看到,我們只需要通過(guò)@synthesize來(lái)聲明我們希望的實(shí)例變量名。

總結(jié):如果我們希望使用默認(rèn)的實(shí)例變量命名方式,那么我們?cè)?m文件中就不需要使用@synthesize聲明,系統(tǒng)會(huì)幫我們自動(dòng)完成。如果我們希望自己命名實(shí)例變量命,那么我們就使用@synthesize顯示聲明我們希望的實(shí)例變量名。

4.@property的特性

@property還有一些關(guān)鍵字,它們都是有特殊作用的,比如上述代碼中的nonatomicstrong

@property(nonatomic,strong) NSString *carName;
@property(nonatomic,strong) NSString *carType;

我把它們分為三類,分別是:原子性,存取器控制,內(nèi)存管理。

4.1 原子性

  • atomic(默認(rèn)):atomic意為操作是原子的,意味著只有一個(gè)線程訪問(wèn)實(shí)例變量。atomic是線程安全的,至少在當(dāng)前的存取器上是安全的。它是一個(gè)默認(rèn)的特性,但是很少使用,因?yàn)楸容^影響效率,這跟ARM平臺(tái)和內(nèi)部鎖機(jī)制有關(guān)。
  • nonatomicnonatomicatomic剛好相反。表示非原子的,可以被多個(gè)線程訪問(wèn)。它的效率比atomic快。但不能保證在多線程環(huán)境下的安全性,在單線程和明確只有一個(gè)線程訪問(wèn)的情況下廣泛使用。

4.2 存取器控制

  • readwrite(默認(rèn)):readwrite是默認(rèn)值,表示該屬性同時(shí)擁有settergetter
  • readonlyreadonly表示只有getter沒(méi)有setter

有時(shí)候?yàn)榱苏Z(yǔ)意更明確可能需要自定義訪問(wèn)器的名字:

@property (nonatomic, setter = mySetter:,getter = myGetter ) NSString *name;

最常見(jiàn)的是BOOL類型,比如標(biāo)識(shí)View是否隱藏的屬性hidden。可以這樣聲明:

@property (nonatomic,getter = isHidden ) BOOL hidden;

4.3 內(nèi)存管理

@property有顯示的內(nèi)存管理策略。這使得我們只需要看一眼@property聲明就明白它會(huì)怎樣對(duì)待傳入的值。

  • assign(默認(rèn)):assign用于值類型,如intfloatdoubleNSIntegerCGFloat等表示單純的復(fù)制。還包括不存在所有權(quán)關(guān)系的對(duì)象,比如常見(jiàn)的delegate。
@property(nonatomic) int running;  
@property(nonatomic,assign) int running;

以上兩段代碼是相同的。

setter方法中,采用直接賦值來(lái)實(shí)現(xiàn)設(shè)值操作:

-(void)setRunning:(int)newRunning{  
    _running = newRunning;  
} 
  • retian:在setter方法中,需要對(duì)傳入的對(duì)象進(jìn)行引用計(jì)數(shù)加1的操作。
    簡(jiǎn)單來(lái)說(shuō),就是對(duì)傳入的對(duì)象擁有所有權(quán),只要對(duì)該對(duì)象擁有所有權(quán),該對(duì)象就不會(huì)被釋放。如下代碼所示:
-(void)setName:(NSString*)_name{  
     //首先判斷是否與舊對(duì)象一致,如果不一致進(jìn)行賦值。  
     //因?yàn)槿绻且粋€(gè)對(duì)象的話,進(jìn)行if內(nèi)的代碼會(huì)造成一個(gè)極端的情況:當(dāng)此name的retain為1時(shí),使此次的set操作讓實(shí)例name提前釋放,而達(dá)不到賦值目的。  
     if ( name != _name){  
          [name release];  
          name = [_name retain];  
     }  
} 
  • strongstrong是在IOS引入ARC的時(shí)候引入的關(guān)鍵字,是retain的一個(gè)可選的替代。表示實(shí)例變量對(duì)傳入的對(duì)象要有所有權(quán)關(guān)系,即強(qiáng)引用。strong跟retain的意思相同并產(chǎn)生相同的代碼,但是語(yǔ)意上更好更能體現(xiàn)對(duì)象的關(guān)系。
  • weak:在setter方法中,需要對(duì)傳入的對(duì)象不進(jìn)行引用計(jì)數(shù)加1的操作。
    簡(jiǎn)單來(lái)說(shuō),就是對(duì)傳入的對(duì)象沒(méi)有所有權(quán),當(dāng)該對(duì)象引用計(jì)數(shù)為0時(shí),即該對(duì)象被釋放后,用weak聲明的實(shí)例變量指向nil,即實(shí)例變量的值為0。

注:weak關(guān)鍵字是IOS5引入的,IOS5之前是不能使用該關(guān)鍵字的。delegate 和 Outlet 一般用weak來(lái)聲明。

  • copy:與strong類似,但區(qū)別在于實(shí)例變量是對(duì)傳入對(duì)象的副本擁有所有權(quán),而非對(duì)象本身。

本文首發(fā)地址:Objective-C中的@property

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容