Objective-C tips
nil
下面兩種OC語(yǔ)法是等價(jià)的:
if (venue == nil) {
[organizer remindToFindVenueForParty];
}
if (!venue) { //類似JS的語(yǔ)法,判斷對(duì)象為空或非空
[organizer remindToFindVenueForParty];
}
其他語(yǔ)言經(jīng)常要加非空判斷物遇,像JAVA JS都需要俩功,避免出現(xiàn)空指針異常,OC中不需要像下面這樣判斷扔傅,如果對(duì)象為nil耍共,OC會(huì)直接忽略被調(diào)用的方法。
// Is venue non-nil?
if (venue) { //例如JS猎塞,判斷對(duì)象不為空
[venue sendConfirmation];
}
@
創(chuàng)建字符串對(duì)象:
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”,當(dāng)對(duì)象被Log時(shí)荠耽,會(huì)發(fā)送給給對(duì)象description
消息钩骇,返回字符串。就像java中的toString()方法铝量。
實(shí)例變量 set&get方法
在頭文件中聲明實(shí)例變量倘屹,ANDROID中實(shí)例變量以m開(kāi)頭,OC中實(shí)例變量以 _
開(kāi)頭慢叨,約定而已纽匙,不是必須的。
get方法不需要以get
開(kāi)頭插爹。
#import <Foundation/Foundation.h>
@interface BKItem : NSObject
{
NSString *_itemName; //*號(hào)說(shuō)明變量是個(gè)指針
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; //只有g(shù)et方法
@end
類的實(shí)現(xiàn)中實(shí)現(xiàn)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的兩種訪問(wèn)語(yǔ)法,使用點(diǎn)語(yǔ)法最終會(huì)被編譯器轉(zhuǎn)成第一種方法赠尾,但是使用點(diǎn)語(yǔ)法更方便力穗。
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]);
// 推薦使用下面的點(diǎn)語(yǔ)法 dot syntax:等號(hào)左邊調(diào)用set方法,等號(hào)右邊調(diào)用get方法
item.itemName=@"Red Sofa";
item.serialNumber=@"A1B2C";
item.valueInDollars = 100;
NSLog(@"%@ %@ %@ %d", item.itemName, item.dateCreated,
item.serialNumber, item.valueInDollars);
重寫(xiě)方法
重寫(xiě)父類方法气嫁,只需在子類的實(shí)現(xiàn)文件中重寫(xiě)当窗,頭文件中不需要聲明,因?yàn)楸恢貙?xiě)的方法已經(jīng)被父類的頭文件聲明過(guò)了寸宵。
Initializers
構(gòu)造器崖面,類默認(rèn)只有init一個(gè)初始化方法元咙,可以添加自定義的初始化方法。
#import <Foundation/Foundation.h>
@interface BKItem : NSObject
{
NSString *_itemName; //*號(hào)說(shuō)明變量是個(gè)指針
NSString *_serialNumber;
int _valueInDollars;
NSDate *_dateCreated;
}
// 聲明自定義的初始化方法
- (instancetype)initWithItemName:(NSString *)name valueInDollars:(int) value serialNumber:(NSString *)sNumber;
- (instancetype)initWithItemName:(NSString *)name;
@end
實(shí)現(xiàn)在頭文件中聲明的初始化方法巫员,通常參數(shù)最多的初始化方法是指定初始化方法(designated initializer)
instancetype
關(guān)鍵字是初始化方法的返回類型庶香,誰(shuí)來(lái)調(diào)用初始化方法,instancetype
就是誰(shuí)(an instance of the receiving object)简识。
#import "BKItem.h"
@implementation BKItem
// Designated initializer (指定的構(gòu)造器赶掖,通常是參數(shù)最多的那個(gè)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) { // 判斷對(duì)象非空,類似JS語(yǔ)法
// 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:@""];
}
// 重寫(xiě)父類的init方法七扰,調(diào)用子類的designated initializer
- (instancetype)init
{
return [self initWithItemName:@"Item"];
}
// 重寫(xiě)父類的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來(lái)聲明變量時(shí)奢赂,由于他已經(jīng)是指針了,所以不需要添加 * 號(hào)颈走。
Class method 類方法(靜態(tài)方法)
類方法常被用來(lái)創(chuàng)建對(duì)象實(shí)例(類似JAVA中獲取單例類對(duì)象的方法)或是獲取全局屬性膳灶。類方法不能訪問(wèn)實(shí)例變量(instance variables)。
通過(guò) +
號(hào)來(lái)聲明類方法立由。
在頭文件中聲明類方法轧钓,注意頭文件內(nèi)容的順序:instance variable, class method, initializer, instance method.
#import <Foundation/Foundation.h>
@interface BKItem : NSObject
{
NSString *_itemName; //*號(hào)說(shuō)明變量是個(gè)指針
NSString *_serialNumber;
int _valueInDollars;
NSDate *_dateCreated;
}
+ (instancetype)randomItem; //類方法
- (instancetype)initWithItemName:(NSString *)name valueInDollars:(int) value serialNumber:(NSString *)sNumber;
- (instancetype)initWithItemName:(NSString *)name;
@end
在實(shí)現(xiàn)文件中實(shí)現(xiàn)類方法:
#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中,同一數(shù)組可以包含任意類型的對(duì)象拆吆,但是必須是Objective-C的對(duì)象聋迎,不能是基本類型和C struct。
不能將 nil
添加到數(shù)組中枣耀,但是 NSNull
可以霉晕。
NSMutableArray *items = [[NSMutableArray alloc] init];
[items addObject:nil]; //ERROR -[__NSArrayM insertObject:atIndex:]: object cannot be nil
[items addObject:[NSNull null]]; // this is OK
數(shù)組下標(biāo)訪問(wèn),以下兩個(gè)方法等價(jià):
NSString *str = [items objectAtIndex:0];
NSString *rts = items[0];
isa instance variable
每個(gè)對(duì)象都有一個(gè) isa
實(shí)例變量指針捞奕,指向?qū)ο笏鶎俚念悺?/p>
Exception
// id 可以代表任何類型牺堰,就像JAVA中的Object
id lastObj = [items lastObject];
[lastObj count];
如果lastObj沒(méi)有count方法,會(huì)報(bào)出以下錯(cuò)誤:
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的實(shí)例筏养,如果是 + 則receiver是BKItem class.
類名前綴
OC中沒(méi)有包或命名空間的概念(這點(diǎn)也真是醉了)...
所以O(shè)C的類名都有前綴來(lái)區(qū)分,可以是公司名的縮寫(xiě)或是項(xiàng)目名的縮寫(xiě)常拓,通常使用三個(gè)字母渐溶,因?yàn)閮蓚€(gè)字母被蘋(píng)果使用了,防止命名沖突弄抬。
NS -- NeXTSTEP茎辐,蘋(píng)果收購(gòu)的公司
Strong and Weak References
如果兩個(gè)對(duì)象都引用了對(duì)方,并且都是強(qiáng)引用,那么會(huì)造成內(nèi)存泄漏拖陆〕诨保可以通過(guò)弱引用來(lái)解決這個(gè)問(wèn)題,通常兩個(gè)對(duì)象互相引用對(duì)方時(shí)依啰,這兩個(gè)對(duì)象存在父子關(guān)系乎串,父對(duì)象會(huì)強(qiáng)引用子對(duì)象,子對(duì)象只需弱引用父對(duì)象速警。
//聲明一個(gè)弱引用變量
__weak BNRItem *_container;
@property
之前每聲明一個(gè)實(shí)例變量灌闺,都需要寫(xiě)對(duì)應(yīng)的set get方法,通過(guò)property來(lái)聲明可以不用寫(xiě)set get方法坏瞄,編譯器會(huì)幫你將實(shí)例變量和set get訪問(wèn)方法聲明好。
@property NSString *itemName;
下表為是否使用@property的區(qū)別:
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 (這兩個(gè)單詞有意思甩卓,都能翻譯成屬性...)
@property (nonatomic, readwrite, strong) NSString *itemName;
nonatomic, atomic
鸠匀,默認(rèn)值是atomic
,和多線程相關(guān)的attribute(原子的逾柿,即線程安全的缀棍;非原子,即非線程安全机错,性能更高)爬范,iOS中通常用nonatomic
readwrite, readonly
,默認(rèn)值是readwrite
弱匪,告訴編譯器是否生成set方法青瀑,只讀不生成set方法。
strong, weak, copy, unsafe_unretained
萧诫,針對(duì)OC的對(duì)象斥难,其默認(rèn)值是strong
。如果是非OC的對(duì)象(如基本類型)帘饶,其只有一個(gè)可選值unsafe-unretained
哑诊,也是非OC對(duì)象的默認(rèn)值,所以可以不用聲明及刻。
什么時(shí)候用copy
镀裤?當(dāng)要聲明的變量類型有可變的子類時(shí),如NSString/NSMutableString or NSArray/NSMutableArray缴饭,這時(shí)要用copy
暑劝,其生成的最終代碼如下,copy會(huì)復(fù)制對(duì)象茴扁,并將變量強(qiáng)引用到復(fù)制生成的對(duì)象
- (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方法
當(dāng)你的set get方法中有邏輯時(shí),需要在類實(shí)現(xiàn)文件中添加自定義的set get方法:
- (void)setContainedItem:(BNRItem *)containedItem
{
_containedItem = containedItem;
self.containedItem.container = self;
}
如果你同時(shí)實(shí)現(xiàn)了set get方法,則需要在頭文件中聲明實(shí)例變量毁习,@property
不會(huì)再幫你自動(dòng)添加實(shí)例變量了智嚷。
本文是對(duì)《iOS Programming The Big Nerd Ranch Guide 4th Edition》第二,三章的總結(jié)纺且。