天天看點

Objective-C中的@property[email protected]是什麼2.建立存取器3.不必單獨聲明示例變量[email protected]的特性

[email protected]是什麼

@Property

是聲明屬性的文法,它可以快速友善的為執行個體變量建立存取器,并允許我們通過點文法使用存取器。

存取器(accessor):指用于擷取和設定執行個體變量的方法。用于擷取執行個體變量值的存取器是

getter

,用于設定執行個體變量值的存取器是

setter

2.建立存取器

2.1 手工建立存取器

我們先看兩段代碼:

// Car.h

#import <Foundation/Foundation.h>

@interface Car : NSObject
{
    // 執行個體變量
    NSString *carName;
    NSString *carType;
}

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

// getter
- (NSString *)carName;

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

// getter
- (NSString *)carType;

@end  
           

上面的代碼中

carName

carType

就是

Car

的執行個體變量,并且可以看到分别對這兩個執行個體變量聲明了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
           

上面代碼是對執行個體變量存取器的實作。我們可以看到,存取器就是對執行個體變量進行指派和取值。按照約定指派方法以set開頭,取值方法以執行個體變量名命名。

我們看看如何使用:

// 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;
}
           

上面的代碼中我們注意到,對象

Car

使用了消息文法,也就是使用方括号的文法給存取器發送消息。 傳回結果為:

The car name is Jeep Cherokee and the type is SUV
           

2.2 使用@Property建立存取器

// Car.h

#import <Foundation/Foundation.h>

@interface Car : NSObject
{
    // 執行個體變量
    NSString *carName;
    NSString *carType;
}

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

@end
           

上面代碼中,我們使用

@property

聲明兩個屬性,名稱與執行個體變量名稱相同(讓我們先忽略

nonatomic

strong

)。

// Car.m

#import "Car.h"

@implementation Car

@synthesize carName;
@synthesize carType;

@end
           

在.m檔案中我們使用

@synthesize

自動生成這兩個執行個體變量的存取器,并且隐藏了存取器,雖然我們看不到存取器,但它們确實是存在的。

// 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

對象使用點文法給存取器發送消息,并且get與set的文法是相同的,是以這裡的點文法可以根據語境判斷我們是要指派還是取值。

當然我們也依然可以使用消息文法來使用:

// 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);

        // 消息文法
        [car setCarName:@"Jeep Compass"];
        [car setCarType:@"SUV"];
        NSLog(@"The car name is %@ and the type is %@",[car carName],[car carType]);       
    }
    return 0;
}
           

上面兩段代碼的執行結果都是:

The car name is Jeep Compass and the type is SUV
           
總結:

@property

等同于在.h檔案中聲明執行個體變量的get/set方法,

@synthesize

等同于在.m檔案中實作執行個體變量的get/set方法。使用

@property

synthesize

建立存取器要比手動聲明兩個存取方法(

getter

setter

)更簡單。而且我們在使用屬性時可以使用點文法指派或取值,文法更簡單,更符合面向對象程式設計。

3.不必單獨聲明示例變量

如果使用

@Property

,就不必單獨聲明執行個體變量了。因為在沒有顯示提供示例變量聲明的前提下,系統會自動幫你生成執行個體變量。我們通過以下代碼來說明:

// Car.h

#import <Foundation/Foundation.h>

@interface Car : NSObject

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

- (NSString *)carInfo;

@end
           

在.h檔案中我們并沒有聲明執行個體變量,隻是聲明了

carName

carType

兩個屬性,以及一個

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

執行個體變量,這就是當我們沒有顯示聲明執行個體變量時,系統為我們自動生成的。命名規則是以

_

為字首,加上屬性名,即

_propertyName

其實在.m檔案中實際是存在

@synthesize

聲明語句的,隻是系統将其隐藏了:

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

那麼如果我們不喜歡預設的執行個體變量命名方法,或者我們希望使用更有語義的名稱,應該怎麼做呢。其實很簡單:

// 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
           

通過上述代碼可以看到,我們隻需要通過

@synthesize

來聲明我們希望的執行個體變量名。

總結:如果我們希望使用預設的執行個體變量命名方式,那麼我們在.m檔案中就不需要使用

@synthesize

聲明,系統會幫我們自動完成。如果我們希望自己命名執行個體變量命,那麼我們就使用

@synthesize

顯示聲明我們希望的執行個體變量名。

[email protected]的特性

@property

還有一些關鍵字,它們都是有特殊作用的,比如上述代碼中的

nonatomic

strong

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

我把它們分為三類,分别是:原子性,存取器控制,記憶體管理。

4.1 原子性

  • atomic

    (預設):

    atomic

    意為操作是原子的,意味着隻有一個線程通路執行個體變量。atomic是線程安全的,至少在目前的存取器上是安全的。它是一個預設的特性,但是很少使用,因為比較影響效率,這跟ARM平台和内部鎖機制有關。
  • nonatomic

    nonatomic

    atomic

    剛好相反。表示非原子的,可以被多個線程通路。它的效率比atomic快。但不能保證在多線程環境下的安全性,在單線程和明确隻有一個線程通路的情況下廣泛使用。

4.2 存取器控制

  • readwrite

    (預設):

    readwrite

    是預設值,表示該屬性同時擁有

    setter

    getter

  • readonly

    : 

    readonly

    表示隻有

    getter

    沒有

    setter

有時候為了語意更明确可能需要自定義通路器的名字:

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

最常見的是BOOL類型,比如辨別View是否隐藏的屬性hidden。可以這樣聲明:

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

4.3 記憶體管理

@property

有顯示的記憶體管理政策。這使得我們隻需要看一眼

@property

聲明就明白它會怎樣對待傳入的值。

  • assign

    (預設):

    assign

    用于值類型,如

    int

    float

    double

    NSInteger

    CGFloat

    等表示單純的複制。還包括不存在所有權關系的對象,比如常見的delegate。
@property(nonatomic) int running;  
           
@property(nonatomic,assign) int running;
           

以上兩段代碼是相同的。

setter

方法中,采用直接指派來實作設值操作:

-(void)setRunning:(int)newRunning{  
    _running = newRunning;  
} 
           
  • retian

    :在

    setter

    方法中,需要對傳入的對象進行引用計數加1的操作。

    簡單來說,就是對傳入的對象擁有所有權,隻要對該對象擁有所有權,該對象就不會被釋放。如下代碼所示:

-(void)setName:(NSString*)_name{  
     //首先判斷是否與舊對象一緻,如果不一緻進行指派。  
     //因為如果是一個對象的話,進行if内的代碼會造成一個極端的情況:當此name的retain為1時,使此次的set操作讓執行個體name提前釋放,而達不到指派目的。  
     if ( name != _name){  
          [name release];  
          name = [_name retain];  
     }  
} 
           
  • strong

    strong

    是在IOS引入ARC的時候引入的關鍵字,是retain的一個可選的替代。表示執行個體變量對傳入的對象要有所有權關系,即強引用。strong跟retain的意思相同并産生相同的代碼,但是語意上更好更能展現對象的關系。
  • weak

    :在

    setter

    方法中,需要對傳入的對象不進行引用計數加1的操作。

    簡單來說,就是對傳入的對象沒有所有權,當該對象引用計數為0時,即該對象被釋放後,用

    weak

    聲明的執行個體變量指向

    nil

    ,即執行個體變量的值為0。
    注:

    weak

    關鍵字是IOS5引入的,IOS5之前是不能使用該關鍵字的。delegate 和 Outlet 一般用

    weak

    來聲明。
  • copy

    :與

    strong

    類似,但差別在于執行個體變量是對傳入對象的副本擁有所有權,而非對象本身。
  • 原文位址:http://www.devtalking.com/articles/you-should-to-know-property/
iOS