《禅与Objective-C编程艺术》阅读总结

注:《Zen and the Art of the Objective-C Craftsmanship》 中文翻译版阅读总结

原文在这里 ,感谢作者

1、常量命名方法

常量的命名方法 以驼峰法命名,并以相关类名作为前缀
static const NSTimeInterval ZOCSignInViewControllerFadeOutAnimationDuration = 0.4;

static NSString * const ZOCCacheControllerDidClearCacheNotification = @"ZOCCacheControllerDidClearCacheNotification";

常量在头文件中以这样的形式暴露给外部
extern NSString *const ZOCCacheControllerDidClearCacheNotification;

2、类名的规范

(1)类名以是哪个大写字母作为前缀,双字母前缀为Apple的类预留。解决Objective-C没有命名空间所带来的问题。
(2)创建一个子类时,把说明性的部分放在前缀和父类名的中间。如一个 UIViewController 的子类会是 ZOCTimelineViewController

3、Initializer 和 dealloc

(1)推荐将dealloc方法放在实现文件的最前面(直接在 @synthesize 以及 dynamic 之后),
(2)为什么设置 self[super init] 的返回值?
申请分配内存和初始化被分离为两步,allocinit。这个特性叫两步创建

  • alloc负责创建对象,这个过程包括分配足够的内存来保存对象,写入isa指针,初始化引用计数,以及重置所有实例变量。
  • init负责初始化对象,这意味着使对象处于可用状态。通常意味着为对象的实例变量赋予合理有用的值。

alloc方法将返回一个有效的未初始化的对象实例。每一个对这个实例发送的消息会被转换成一次obj_msgSend()函数的调用,形参self的实参是alloc返回的指针,这样self在所有方法的作用域内都能够被访问。

init方法可以通过返回 nil来告诉调用者,初始化失败了。

4、Designated(指定) 和 Secondary (间接)初始化方法

(1)designated 初始化方法是提供所有的参数,每一个类总有且只有一个。Seconary 初始化方法可以是一个或多个,他们仅仅是提供一个或者更多的默认参数来调用designated初始化的初始化方法。

(2)在类继承中调用任何designated初始化方法都是合法的,应该保证所有的 designated initializer 在类继承中是从祖先(通常是NSObject)到你的类向下调用的。
定义一个新类的三种方式:

  • 不需要重载任何的初始化函数
  • 重载 designated initializer
  • 定义一个新的 designated initializer

第一个方案最简单,不需要天剑类的任何初始化逻辑,只需要依照父类的designated initializer

@implementation YOEViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // custom initialization(自定义的初始化过程)
    }
return self;
}

第三种 希望提供自己的初始化函数时,遵循三个步骤来保证获得正确的行为:

  • 定义自己designated initializer,确保调用了直接超类的designated initializer
  • 重载直接超类的 designated initializer。调用新的 designated initializer
  • 为新的designated initializer写文档

正确的实现例子:
- (id)initWithNews:(NSString *)news
{
//call the immediate superclass's designated initializer(调用直接超类的designated initializer)
self = [super initWithNibName:nil bundle:nil];
if (self) {
_news = news;
}
return self;
}

// Override the immediate superclass's designated initializer(重载直接父类的 designated initializer)
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    //Call the new designated initializer
    return  [self initWithNews:@"defaultNews"];
}

注:如果没有重载initWithNibName: bundle:方法,而调用者决定使用这个方法来初始化这个类(这是完全合法的)。initWithNews:永远也不会调用,这就导致了不正确的初始化流程,新类的特定初始化逻辑没有被执行。

可以使用编译器的指令 NS_DESIGNATED_INITIALIZER__attribute__((objc_designated_initializer))来明确的支出那个方法是designated initializer。这样如果新的designated initializer没有调用超类的designated initializer,编译器会发出警告。

写法:

- (id)initWithNews:(NSString *)news __attribute__((objc_designated_initializer));

- (id)initWithNews:(NSString *)news NS_DESIGNATED_INITIALIZER;

通过另一个编译器指令attribute((unavailable("Invoke the designated initializer")))来修饰一个方法,这样会使在试图调用这个方法的时候产生一个编译错误(实际上代码提示都不会有该方法了)。
写法:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil attribute((unavailable("Invoke the designated initializer")));

处理一个例外
如果一个对象遵循NSCoding协议,并且它通过initWithCoder:初始化。此时应该看超类是否符合NSCoding协议来区别对待。符合的时候,如只是调用了[super initWithCoder:],就需要在designated initializer里面写一些通用的初始化代码,处理这种方法的是把这些代码放在私有方法里面。当超类不符合NSCoding协议的时候,推荐把initWithCoder:作为Secondary initializer来对待,并且调用self的designated initializer。

5、单例的写法

+ (instancetype)sharedInstance
{
    static id sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[self alloc]init];
    });
    return sharedInstance;
}

6、属性定义

(1)属性定义参数按顺序排列:原子性,读写,内存管理。
@property(nonatomic,readwrite,copy) NSString *name;
(2)属性可以存储一个代码块,必须使用copyblock最早在栈里面创建,使用copyblock拷贝到堆里面去。
(3)声明一个公有的 getter 和一个私有的 setter,应该声明公开的属性为 readonly,并在类的扩展中重新定义属性为readwrite

//.h文件中
@interface YOEClass : NSObject

@property(nonatomic,readonly,strong) NSObject *obj;

@end

//.m文件中
@interface YOEClass ()

@property(nonatomic,readwrite,strong) NSObject *obj;

@end

@implementation YOEClass

- (id)init{
    self = [super init];
    if (self) {
        _obj = @"AA";
    }
    return self;
}

@end

(4)声明一个BOOL属性,setter不应该带is前缀,对应的getter应该带上前缀,So:

@property(assign,getter=isEditable) BOOL editable;

注:在实现文件中应该避免使用@synthesize,编译器已经实现了

7、NSNotification

定义一个NSNotification 的时候,应该把通知的名字作为一个字符串常量

//.h
extern NSString * const ZOCFooDidBecomeBarNotification;
//.m
NSString * const ZOCFooDidBecomeBarNotification = @"ZOCFooDidBecomeBarNotification";

8、利用代码块

代码块如果在闭合的圆括号里,会返回最后语句的值

NSURL *url = ({
    NSString *urlStr = @"http://www.baidu.com";
    [NSURL URLWithString:urlStr];
});

9、self的循环引用

当使用代码块和异步分发的时候,需要避免引用循环。应使用 weak 来引用对象,避免引用循环。

__weak __typeof(self) weakSelf = self;

例子:
单个语句时:

 __weak __typeof(self) weakSelf = self;
[self executeBlock:^(NSData *data, NSError *error) {
    [weakSelf doSomethingWithData:data];
}];

多个语句时:

[self executeBlock:^(NSData *data, NSError *error) {
    __strong __typeof(weakSelf) strongSelf = weakSelf;
    if (strongSelf) {
        [strongSelf doSomethingWithData:data];
        [strongSelf doSomethingWithData:data];
    }
}];

简单记录self在block俩面的三种情况

  • 直接在block里面使用关键字 self。只能在 block 不是作为一个property 的时候使用,否则会导致 retain cycle。
  • 在 block 外定义一个 __weak 的引用到self,并且在 block 里面使用这个弱引用。 当 block 被声明一个 property 的时候使用。
  • 在block 外定义一个 __weak 的引用到 self,并且在 block 内部通过这个弱引用定义一个 __strong 的引用。 和并发执行有关。当涉及异步的服务的时候,block 可以在之后被执行,并且不会发生关于self是否存在的问题。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335

推荐阅读更多精彩内容