你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> 【知識梳理向】Objective

【知識梳理向】Objective

編輯:IOS開發基礎

1.@property是什麼

@Property是聲明屬性的語法,它可以快速方便的為實例變量創建存取器,並允許我們通過點語法使用存取器。

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

2.創建存取器

2.1 手工創建存取器

我們先看兩段代碼:

// Car.h
#import 
@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 
@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 
@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顯示聲明我們希望的實例變量名。

4.@property的特性

@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類似,但區別在於實例變量是對傳入對象的副本擁有所有權,而非對象本身。

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved