Objective-C tips

Objective-C tips

nil

下面兩種OC語法是等價的:

if (venue == nil) {
    [organizer remindToFindVenueForParty];
}
if (!venue) {  //類似JS的語法,判斷對象為空或非空
    [organizer remindToFindVenueForParty];
}

其他語言經常要加非空判斷,像JAVA JS都需要,避免出現空指針異常,OC中不需要像下面這樣判斷,如果對象為nil,OC會直接忽略被調用的方法。

// Is venue non-nil?
if (venue) { //例如JS,判斷對象不為空
    [venue sendConfirmation];
}

@

創建字符串對象:

NSString *myString = @"Hello, World!";

NSLog格式化字符串,不同類型使用不同占位符:

int a = 1;
float b = 2.5;
char c = 'A';
NSLog(@"Integer: %d Float: %f Char: %c", a, b, c);

        
NSString *str = @"hello oc";
NSLog(@"print %@", str);

%@代表“a pointer to any object”,當對象被Log時,會發送給給對象description消息,返回字符串。就像java中的toString()方法。

實例變量 set&get方法

在頭文件中聲明實例變量,ANDROID中實例變量以m開頭,OC中實例變量以 _ 開頭,約定而已,不是必須的。
get方法不需要以get開頭。

#import <Foundation/Foundation.h>

@interface BKItem : NSObject
{
    NSString *_itemName; //*號說明變量是個指針
    NSString *_serialNumber;
    int _valueInDollars;
    NSDate *_dateCreated;
}

//在頭文件中聲明set get方法,像是java中聲明接口的抽象方法
- (void)setItemName:(NSString *)str;
- (NSString *)itemName;

- (void)setSerialNumber:(NSString *)str;
- (NSString *)serialNumber;

- (void)setValueInDollars:(int)v;
- (int)valueInDollars;

- (NSDate *)dateCreated; //只有get方法

@end

類的實現中實現set get方法:

#import "BKItem.h"

@implementation BKItem

- (void)setItemName:(NSString *)str
{
    _itemName = str;
}
- (NSString *)itemName
{
    return _itemName;
}

- (void)setSerialNumber:(NSString *)str
{
    _serialNumber = str;
}
- (NSString *)serialNumber
{
    return _serialNumber;
}

- (void)setValueInDollars:(int)v
{
    _valueInDollars = v;
}
- (int)valueInDollars
{
    return _valueInDollars;
}

- (NSDate *)dateCreated
{
    return _dateCreated;
}

@end

set get的兩種訪問語法,使用點語法最終會被編譯器轉成第一種方法,但是使用點語法更方便。

        BKItem *item = [[BKItem alloc] init];
        [item setItemName:@"Red Sofa"];
        [item setSerialNumber:@"A1B2C"];
        [item setValueInDollars:100];
        
        NSLog(@"%@ %@ %@ %d", [item itemName], [item dateCreated],
              [item serialNumber], [item valueInDollars]);
        
       // 推薦使用下面的點語法 dot syntax:等號左邊調用set方法,等號右邊調用get方法
        item.itemName=@"Red Sofa";
        item.serialNumber=@"A1B2C";
        item.valueInDollars = 100;
        NSLog(@"%@ %@ %@ %d", item.itemName, item.dateCreated,
              item.serialNumber, item.valueInDollars);

重寫方法

重寫父類方法,只需在子類的實現文件中重寫,頭文件中不需要聲明,因為被重寫的方法已經被父類的頭文件聲明過了。

Initializers

構造器,類默認只有init一個初始化方法,可以添加自定義的初始化方法。

#import <Foundation/Foundation.h>

@interface BKItem : NSObject
{
    NSString *_itemName; //*號說明變量是個指針
    NSString *_serialNumber;
    int _valueInDollars;
    NSDate *_dateCreated;
}

// 聲明自定義的初始化方法
- (instancetype)initWithItemName:(NSString *)name valueInDollars:(int) value serialNumber:(NSString *)sNumber;
- (instancetype)initWithItemName:(NSString *)name;

@end

實現在頭文件中聲明的初始化方法,通常參數最多的初始化方法是指定初始化方法(designated initializer)
instancetype 關鍵字是初始化方法的返回類型,誰來調用初始化方法,instancetype 就是誰(an instance of the receiving object)。

#import "BKItem.h"

@implementation BKItem

// Designated initializer (指定的構造器,通常是參數最多的那個init方法)
- (instancetype)initWithItemName:(NSString *)name valueInDollars:(int) value serialNumber:(NSString *)sNumber{
    // Call the superclass's designated initializer
    self = [super init];
    // Did the superclass's designated initializer succeed?
    if (self) { // 判斷對象非空,類似JS語法
        // Give the instance variables initial values
        _itemName = name;
        _serialNumber = sNumber;
        _valueInDollars = value;
        // Set _dateCreated to the current date and time
        _dateCreated = [[NSDate alloc] init];
    }
    // Return the address of the newly initialized object
    return self;
}
- (instancetype)initWithItemName:(NSString *)name{
    return [self initWithItemName:name
                   valueInDollars:0
                     serialNumber:@""];
}

// 重寫父類的init方法,調用子類的designated initializer
- (instancetype)init
{
    return [self initWithItemName:@"Item"];
}

// 重寫父類的description方法
- (NSString *)description
{
    NSString *descriptionString =
    [[NSString alloc] initWithFormat:@"%@ (%@): Worth $%d, recorded on %@",
     self.itemName,
     self.serialNumber,
     self.valueInDollars,
     self.dateCreated];
    return descriptionString;
}

@end

id - a pointer to any object

Because id is defined as “a pointer to any object,” you do not include an * when declaring avariable or method parameter of this type.

用id來聲明變量時,由于他已經是指針了,所以不需要添加 * 號。

Class method 類方法(靜態方法)

類方法常被用來創建對象實例(類似JAVA中獲取單例類對象的方法)或是獲取全局屬性。類方法不能訪問實例變量(instance variables)。
通過 + 號來聲明類方法。

在頭文件中聲明類方法,注意頭文件內容的順序:instance variable, class method, initializer, instance method.

#import <Foundation/Foundation.h>

@interface BKItem : NSObject
{
    NSString *_itemName; //*號說明變量是個指針
    NSString *_serialNumber;
    int _valueInDollars;
    NSDate *_dateCreated;
}

+ (instancetype)randomItem; //類方法

- (instancetype)initWithItemName:(NSString *)name valueInDollars:(int) value serialNumber:(NSString *)sNumber;
- (instancetype)initWithItemName:(NSString *)name;

@end

在實現文件中實現類方法:

#import "BKItem.h"

@implementation BKItem

+ (instancetype)randomItem
{
    // Create an immutable array of three adjectives
    NSArray *randomAdjectiveList = @[@"Fluffy", @"Rusty", @"Shiny"];
    // Create an immutable array of three nouns
    NSArray *randomNounList = @[@"Bear", @"Spork", @"Mac"];
    // Get the index of a random adjective/noun from the lists
    // Note: The % operator, called the modulo operator, gives
    // you the remainder. So adjectiveIndex is a random number
    // from 0 to 2 inclusive.
    NSInteger adjectiveIndex = arc4random() % [randomAdjectiveList count];
    NSInteger nounIndex = arc4random() % [randomNounList count];
    // Note that NSInteger is not an object, but a type definition
    // for "long"
    NSString *randomName = [NSString stringWithFormat:@"%@ %@",
                            [randomAdjectiveList objectAtIndex:adjectiveIndex],
                            [randomNounList objectAtIndex:nounIndex]];
    int randomValue = arc4random() % 100;
    NSString *randomSerialNumber = [NSString stringWithFormat:@"%c%c%c%c%c",
                                    '0' + arc4random() % 10,
                                    'A' + arc4random() % 26,
                                    '0' + arc4random() % 10,
                                    'A' + arc4random() % 26,
                                    '0' + arc4random() % 10];
    BKItem *newItem = [[self alloc] initWithItemName:randomName
                                       valueInDollars:randomValue
                                         serialNumber:randomSerialNumber];
    return newItem;
}

@end

NSArray NSMutableArray

在Objective-C中,同一數組可以包含任意類型的對象,但是必須是Objective-C的對象,不能是基本類型和C struct。

不能將 nil 添加到數組中,但是 NSNull 可以。

NSMutableArray *items = [[NSMutableArray alloc] init];
[items addObject:nil]; //ERROR -[__NSArrayM insertObject:atIndex:]: object cannot be nil
[items addObject:[NSNull null]]; // this is OK

數組下標訪問,以下兩個方法等價:

NSString *str = [items objectAtIndex:0];
NSString *rts = items[0];

isa instance variable

每個對象都有一個 isa 實例變量指針,指向對象所屬的類。

Exception

 // id 可以代表任何類型,就像JAVA中的Object
id lastObj = [items lastObject];
[lastObj count];

如果lastObj沒有count方法,會報出以下錯誤:

2015-07-07 19:31:16.787 RandomItems[3647:303] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[BKItem count]: unrecognized selector sent to instance 0x7fd1a2c0c650'

unrecognized selector,selector就是message,就是類中的方法。
-[BKItem count],- 表示receiver是BKItem的實例,如果是 + 則receiver是BKItem class.

類名前綴

OC中沒有包或命名空間的概念(這點也真是醉了)...
所以OC的類名都有前綴來區分,可以是公司名的縮寫或是項目名的縮寫,通常使用三個字母,因為兩個字母被蘋果使用了,防止命名沖突。
NS -- NeXTSTEP,蘋果收購的公司

Strong and Weak References

如果兩個對象都引用了對方,并且都是強引用,那么會造成內存泄漏。可以通過弱引用來解決這個問題,通常兩個對象互相引用對方時,這兩個對象存在父子關系,父對象會強引用子對象,子對象只需弱引用父對象。

//聲明一個弱引用變量
__weak BNRItem *_container;

@property

之前每聲明一個實例變量,都需要寫對應的set get方法,通過property來聲明可以不用寫set get方法,編譯器會幫你將實例變量和set get訪問方法聲明好。

@property NSString *itemName;

下表為是否使用@property的區別:

file Without properties With properties
BNRThing.h @interface BNRThing : NSObject
{
NSString *_name;
}
- (void)setName:(NSString *)n;
- (NSString *)name;
@end
@interface BNRThing : NSObject
@property NSString *name;
@end
BNRThing.m @implementation BNRThing
- (void)setName:(NSString *)n
{
_name = n;
}
- (NSString *)name
{
return _name;
}
@end
@implementation BNRThing
@end

注意property屬性名字不需要下劃線前綴

property attribute (這兩個單詞有意思,都能翻譯成屬性...)

@property (nonatomic, readwrite, strong) NSString *itemName;

nonatomic, atomic,默認值是atomic,和多線程相關的attribute(原子的,即線程安全的;非原子,即非線程安全,性能更高),iOS中通常用nonatomic

readwrite, readonly,默認值是readwrite,告訴編譯器是否生成set方法,只讀不生成set方法。

strong, weak, copy, unsafe_unretained,針對OC的對象,其默認值是strong。如果是非OC的對象(如基本類型),其只有一個可選值unsafe-unretained,也是非OC對象的默認值,所以可以不用聲明。

什么時候用copy?當要聲明的變量類型有可變的子類時,如NSString/NSMutableString or NSArray/NSMutableArray,這時要用copy,其生成的最終代碼如下,copy會復制對象,并將變量強引用到復制生成的對象

- (void)setItemName:(NSString *)itemName
{
    _itemName = [itemName copy];
}
@property (nonatomic, strong) BNRItem *containedItem;
@property (nonatomic, weak) BNRItem *container;
@property (nonatomic, copy) NSString *itemName;
@property (nonatomic, copy) NSString *serialNumber;
@property (nonatomic) int valueInDollars;
@property (nonatomic, readonly, strong) NSDate *dateCreated;

The memory management attribute’s values are strong, weak, copy, and unsafe_unretained. This
attribute describes the type of reference that the object with the instance variable has to the object that
the variable is pointing to.

上面的第二句話怎么理解? 好多that...

自定義set get方法

當你的set get方法中有邏輯時,需要在類實現文件中添加自定義的set get方法:

- (void)setContainedItem:(BNRItem *)containedItem
{
    _containedItem = containedItem;
    self.containedItem.container = self;
}

如果你同時實現了set get方法,則需要在頭文件中聲明實例變量,@property不會再幫你自動添加實例變量了。


本文是對《iOS Programming The Big Nerd Ranch Guide 4th Edition》第二,三章的總結。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容