《Effective Objective-C 2.0》读书笔记(一)

本读书笔记仅为本人阅读此书的一些总结和联想,并非图书精讲。

第一章

第一条 了解Object-C 语言的起源

Object-C是C语言的超集,是一门面向对象的语言。该语言使用“消息结构(messaging structure)”而非“函数调用(function calling)”。关键区别在于:使用消息结构的语言,其运行时所应执行的代码由运行环境来决定;而使用函数调用的语言,则由编译器决定。
关于消息结构的动态语言特性,在学习runtime相关知识后会有较为深刻的理解,但是与函数调用的区别还是应掌握门对应的语言才能更加深刻的对其特性有所理解。
本章中多次提出 开发者应理解C语言的核心概念有助于写好Objective-C程序。尤其要掌握内存模型与指针。
Object-C有OC语言封装的API比如NSRunLoop也有c语言的API CFRunLoop。在一些功能性的框架中也有对c语言的使用比如#import 对h.264视频进行硬解码编码等。

    int a = 10;
    int * p = NULL;
    p = &a;
    NSLog(@"%d - %lu",*p,sizeof(*p));
    NSLog(@"%d",*p);
   输出10 - 4

p为int *型的指针 ,这个指针包含了两个要素:1、一个是指针指向的内存首地址 也就是在计算机内存中的起始位置。另一个是内存中存放数据的类型。本例子中a为int型在内存中占4个字节。这样知道位置知道长度就可以准确的找到数据了。

第2条 在类的头文件中尽量少引入其他的头文件

在OC中对文件的引用有如下几种
@import Photos;                                 下面单独讲解
@class EOCModel;                              向前声明 
#import    引用系统文件
#import"EOCModel.h"                        引用自定义文件

#include< >引用的是编译器的类库路径里面的头文件。
#include“ ”引用的是你程序目录的[相对路径]

首先讲解下@import
ios-@import和#import
@import是个什么,和#import啥区别
通过以上两篇文章的理解可以简单总结为:所有的#import和#include指令都被映射为自动使用@import。 @import不必单独使用,直接用#import和¥include就行和 在链接二中有说明。@import也是防止重复引用提高编译效率的一种方法。

《Effective Objective-C 2.0》读书笔记(一)_第1张图片
LLVM9中的modular默认状态.png

import 和include 的区别 使用include会引起文件的重复引用和文件引用的循环嵌套,import可以解决重复的问题,但是文件的循环引用问题无法的到解决。比如在第二条中EOCPerson和EOCEmployer的引用问题。如果使用@class即可解决。 代码和发生的问题如下

@class 在编译某个文件时不需要知道引入类的全部实现,只需要知道一个类型就好,可是使用@class 代替#import。

#import 
@class  EOCEmployer;
//#import "EOCEmployer.h"

@interface EOCPerson : NSObject

@property (nonatomic, copy) NSString * firstName;
@property (nonatomic, copy) NSString * lastName;
@property (nonatomic, strong) EOCEmployer * employer;

@end
#import 
#import "EOCEmployer.h"
#import "EOCPerson.h"

@interface EOCEmployer : NSObject

- (void)addEmployee:(EOCPerson *)person;
@end
《Effective Objective-C 2.0》读书笔记(一)_第2张图片
头文件嵌套问题.png

除非有必要,否则不要引入头文件。一般来说在某个类的头文件中使用向前声明来提及别的类,并在实现文件中引入那些类的头文件。这样做可以尽量降低类之间的耦合。
有时无法使用向前声明,比如要声明某个类遵循一项协议。这种情况下,尽量把“该类循序某协议”的声明移动至“class - continuation分类”中。如果不行的话,就把协议单独放在一个头文件中,然后将其引入。

第3条 多用字面量语法,少用与之等价的方法

  • 应该使用字面量语法来创建字符串、数值、数组、字典。与创建此对象的常规方法相比,这么做更加简明扼要。
  • 因该通过取下标操作来访问数组下标或字典中的键所对应的元素。
  • 用字面量语法创建数组或字典时,若值中有nil,则会抛除异常。因此,务必确保值里不含nil; (常见的处理方法使用runtime来防治这类的crash发生 git中也有很多开源项目)
    NSString * cat = @"cat";
    NSString * dog = nil;
    NSString * pig = @"pig";
    
    NSArray * arrayA = [NSArray arrayWithObjects:cat,dog,pig, nil];
    NSLog(@"arrayA count:%d",arrayA.count);
    NSArray * arrayB = @[cat,dog,pig];//字面量
    //使用字面量方法初始化arrayB时会抛出异常,arrayA的初始化方法会一次处理各个参数,直到发现nil为止。dog是nil,所以会提前结束。

第四条 多用类型常量,少用#define预处理指令

有些c基础的 应该知道#define 就是一个替换作用 比如定义了一个宽度 #define kMyBtnWidth 25.0 在程序中使用 self.myBtn.width = kMyBtnWidth; 如果 #define kMyBtnWidth 25.0; 这样添加宏定义的话self.myBtn.width = kMyBtnWidth 我们就不用在结尾添加;了。
这里插一个需要注意的地方 定义需要考虑使用场景。比如第一个一个加法 #define kLWAdd 3+5 在使用使用中我们是这样的

  #define  kLWAdd  3+5
  int result =    kLWAdd + 5; //打印 a ==13
   //换乘法
  int result2 = kLWAdd * 2;//打印结果 a==13  为啥不是16呢 8*2=16啊。这就使替换的问题 上面相当于 int result2 = 3+5 * 2;
  //这时我们应该这样定义   #define  kLWAdd  (3+5) 
  int result2 = kLWAdd * 2;//打印结果 a==16

因为不是常量因此可能会被更改 并且数据的类型不明确。比较好的做法如下
在.h文件中
extern NSString * const kLWRequestURL;
在.m文件中
static NSString * const kLWRequestURL = @"http//aaaaa";

static 关键字会让相关代码作用于本文件中 extern在.h中把声明暴漏给所有文件使用。NSString * 指向一个常量 是指针常量。

同时还有另外一个好处,就是不使用#define 时可以减少编译时间。XCode在运行程序的使用也是要进行编译 连接的。比如在修改的项目的运行借口 宏定义。从测试接口转换为线上接口,是不是感觉command + r的时间变长了呢。

第5条 用美剧表示状态、选项、状态码、

1.提到枚举首先想到的就是 NS_ENUM 系统默认第一个枚举为0 如果我们修改第一个枚举为2 那么接下来的枚举回依次增加。
2.还有一种枚举为NS_OPTIONS 是位移枚举,枚举的数据不在时NSUInteeger 我们可以在后面设置选项值的二进制大小。这种枚举值的定义为2的幂。好处就是可以通过按位或同时设置多个状态而不会出现数据混乱的问题。具体例子书中很明确不在赘述。使用中的例子,比如SDWebImage 的方法如下

 [headImageV sd_setImageWithURL:headUrl placeholderImage:[UIImage imageNamed:@
                                                             "headPlaceHolder.png"] options:SDWebImageRetryFailed | SDWebImageHighPriority];

你可能感兴趣的:(《Effective Objective-C 2.0》读书笔记(一))