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》第二,三章的总结。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容