time

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

2015-10-09  本文已影响141人  懒得起名的伊凡

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

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

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

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

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

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

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

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

正确的实现例子:
- (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俩面的三种情况

上一篇下一篇

猜你喜欢

热点阅读