Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commite60dda0

Browse files
committed
Init EOC
1 parentfadcce3 commite60dda0

16 files changed

+1075
-0
lines changed
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
###第一章: 熟悉Objective-C
2+
3+
####No.1: 了解Objective-C语言的起源
4+
5+
* C -> Smalltalk -> Objective-C
6+
7+
* 消息结构 vs 函数调用
8+
9+
* OC是使用消息结构的语言,运行时所执行的代码由runtime决定
10+
* C和C++是使用函数调用的语言,运行时所执行的代码由编译器决定,如果函数是多态的,则需要在运行时去virtual table查找
11+
12+
13+
14+
####No.2: 在类的头文件中尽量少引入其他文件
15+
16+
* 若ClassA持有属性ClassB,则使用@class在ClassA.h中@class ClassB,而不用#import "ClassB.h"
17+
18+
在ClassA.m中#import "ClassB.h",把引入ClassB.h的时机尽量延后,有效提高编译效率
19+
20+
* 若ClassA.h和ClassB.h需要互相#import,则需要使用@class解决循环引用
21+
22+
协议应该单独声明在一个头文件里,防止产生潜在的循环引用
23+
24+
*@class不能用做声明ClassA的超类和协议,因为此时编译器需要知道其中定义的方法
25+
26+
27+
28+
####No.3: 多用字面量语法,少用与之等价的方法
29+
30+
* 应该使用字面量语法,提高代码的可读性
31+
32+
* 使用NSArray时需要注意nil值
33+
34+
```objective-c
35+
id object1 =/* ...*/;
36+
id object2 =/* ...*/;
37+
id object3 =/* ...*/;
38+
// when object2 = nil
39+
NSArray *arrayA = [NSArrayarrayWithObjects: object1, object2, object3, nil]; //[object1]
40+
NSArray*arrayB = @[object1, object2, object3]; // 抛出异常
41+
```
42+
43+
* 使用NSDictionary时需要注意dictionaryWithObjectsAndKeys:方法是value-key结构,应该使用key-value结构的字面量语法
44+
45+
NSDictionary在遇到nil值时和NSArray一样,会抛出异常
46+
47+
```objective-c
48+
// value-key
49+
NSDictionary *personData = [NSDictionary dictionaryWithObjectsAndKeys:
50+
@"Matt", @"firstName",
51+
@"Galloway", @"lastName",
52+
[NSNumber numberWithInt:28], @"age",
53+
nil];
54+
55+
// key-value
56+
NSDictionary *personData = @{
57+
@"firstName" : @"Matt",
58+
@"lastName" : @"Galloway",
59+
@"age" : @28
60+
};
61+
```
62+
63+
* 使用字面量创建的NSString、NSArray和NSDictionary都是不可变的,需要手动mutableCopy
64+
65+
```objective-c
66+
NSMutableArray *mutable = [@[@1, @2, @3, @4, @5]mutableCopy];
67+
```
68+
69+
70+
71+
####No.4: 多用类型常量,少用#define预处理指令
72+
73+
* 应该使用类型常量替换#define,这样可以带上类型信息
74+
75+
```objective-c
76+
#defineANIMATION_DURATION 0.3
77+
78+
staticconstNSTimeIntervalkAnimationDuration =0.3;
79+
```
80+
81+
* 慎重在头文件里声明常量,OC没有命名空间,每个#import该头文件的文件中,都会出现我们声明的常量
82+
83+
如果只是为了在.m里使用,可以在.m的@implementation外声明
84+
85+
* 一定要用static和const修饰符
86+
87+
若有试图修改常量的操作,const修饰符会让编译器报错
88+
89+
若有多个.m声明同一个常量,static会限制每个.m里声明的常量仅在该.m里可见,不为常量创建external symbol
90+
91+
* 用extern创建外界可见的常量,此类常量存放在全局符号表中
92+
93+
```objective-c
94+
// In the header file
95+
externNSString *const EOCStringConstant;
96+
97+
// In the implementation file
98+
NSString *const EOCStringConstant =@"VALUE";
99+
```
100+
101+
102+
103+
####No.5: 用枚举表示状态、选项和状态码
104+
105+
* 默认的enum不支持指定底层数据类型,编译器会根据枚举值数量自动选择底层数据类型
106+
107+
我们应该使用宏定义好的NS_ENUM和NS_OPTIONS,分别对应单选和多选,支持我们自定义底层数据类型
108+
109+
```objective-c
110+
typedefNS_ENUM(NSUInteger, EOCConnectionState) {
111+
EOCConnectionStateDisconnected,
112+
EOCConnectionStateConnecting,
113+
EOCConnectionStateConnected,
114+
};
115+
116+
typedef NS_OPTIONS(NSUInteger, EOCPermittedDirection) {
117+
EOCPermittedDirectionUp = 1 << 0,
118+
EOCPermittedDirectionDown = 1 << 1,
119+
EOCPermittedDirectionLeft =1<<2,
120+
EOCPermittedDirectionRight = 1 << 3,
121+
};
122+
```
123+
124+
* 使用switch处理枚举时不要实现default分支,这样以后新加入枚举值时编译器会有提示
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
###第二章: 对象、消息、运行期
2+
3+
####No.6: 理解property
4+
5+
* OC把实例变量当作存储偏移量的特殊变量,由类对象管理,偏移量会在运行时查找
6+
7+
这样无论何时访问实例变量,总能找到正确的位置,并支持运行时向类增加实例变量
8+
9+
*@property根据我们指定的内存管理语义生成属性,并生成存取方法,需要注意的是weak和unsafe_unretained
10+
11+
* weak: 属性所指向的对象销毁时,属性值也会清空
12+
* unsafe_unretained: 属性所指向的对象销毁时,属性值不会清空
13+
14+
*@synthesize为我们定义的属性创建别名
15+
16+
*@dynamic告诉编译器不为制定属性创建存取方法,而由我们手动创建
17+
18+
* atomic和nonatomic,atomic使用同步锁保证每次读取的值都是有效的,但并不保证线程安全,且同步锁开销较大
19+
20+
因此我们应该使用nonatomic避免无谓开销
21+
22+
23+
24+
####No.7: 在对象内部尽量直接访问实例变量
25+
26+
* 通过属性访问 vs 直接访问
27+
28+
我们应该在读取实例变量时直接访问,设置实例变量时通过属性来做
29+
30+
* 直接访问需要注意的点
31+
32+
* 不经过runtime的消息转发而直接访问内存,速度较快
33+
* 绕过内存管理语义,例如ARC下直接访问声明为copy的属性,则不会拷贝该属性,只会保留新值并释放旧值
34+
* 不触发KVO
35+
* 不能在存取方法里添加断点debug
36+
37+
* 初始化方法里我们应该直接访问,因为子类可能会override设置方法
38+
39+
* 懒加载的属性必须通过属性访问,否则永远不会执行其懒加载方法
40+
41+
42+
43+
####No.8: 理解object equality
44+
45+
* NSObject协议中的isEqual:用于判定等同性,内部实现规则是当且仅当指针值完全相同,两个对象才相等
46+
47+
若isEqual:判定两个对象相等,则两个对象的hash值也想等
48+
49+
若两个对象的hash值相等,则两个对象不一定相等
50+
51+
```objective-c
52+
NSString *a1 =@"aaa";
53+
NSString *a2 = [NSStringstringWithString:@"aaa"];
54+
NSString*a3 = @"aaaaaa";
55+
NSLog(@"%p, %p, %lu", a1, &a1, (unsigned long)a1.hash);
56+
NSLog(@"%p, %p, %lu", a2, &a2, (unsigned long)a2.hash);
57+
NSLog(@"%p, %p, %lu", a3, &a3, (unsigned long)a3.hash);
58+
59+
// 2021-10-04 22:18:47.940473+0800 EOC[10148:828685] 0x100004018, 0x7ffeefbff458, 516200022
60+
// 2021-10-04 22:18:47.940875+0800 EOC[10148:828685] 0x100004018, 0x7ffeefbff450, 516200022
61+
// 2021-10-04 22:18:47.940908+0800 EOC[10148:828685] 0x100004038, 0x7ffeefbff448, 8835314326513740
62+
```
63+
64+
* 关于hash方法
65+
66+
* 只有NSSet加入新值和NSDictionary加入新key时调用,因为这两种都需要保证唯一性
67+
* hash方法可以被我们override,override hash方法时需要注意效率问题,要在collision频率和运算复杂度之间做tradeoff
68+
69+
* 检测对象等同性需要override isEqual:和hash方法,不要盲目地逐个检测每条属性
70+
71+
72+
73+
#### No.9: 用class cluster隐藏实现细节
74+
75+
* OC系统框架普遍使用类族模式,把实现细节隐藏在简单的公共借口背后,例如UIButton和大部分collection,我们可以使用工厂模式实现自己类族模式
76+
77+
判断类时我们应该用isKindOfClass:方法,而不是用==
78+
79+
```objective-c
80+
NSArray *array = @[@1, @2, @3];
81+
NSLog(@"%@", array.class);
82+
NSLog(@"%d", array.class == NSArray.class);
83+
NSLog(@"%d", [array isKindOfClass:NSArray.class]);
84+
85+
// 2021-10-04 22:51:05.613016+0800 EOC[10319:849813] __NSArrayI
86+
// 2021-10-04 22:51:05.613283+0800 EOC[10319:849813] 0
87+
// 2021-10-04 22:51:05.613314+0800 EOC[10319:849813] 1
88+
```
89+
90+
* 一般情况下不要继承类族的公共抽象基类,这往往很复杂,需要阅读其开发文档
91+
92+
93+
94+
####No.10: 在既有类中使用关联对象存放自定义数据
95+
96+
* 关联对象可以指定内存管理语义,把两个对象连接起来,但仅在其他做法不可用时才选用关联对象,因为关联对象可能会引入难以debug的bug
97+
98+
99+
100+
####No.11: 理解objc_msgSend作用
101+
102+
* OC对象的每个方法都会通过objc_msgSend转换成类似以下的形式
103+
104+
内部实现中,objc_msgSend做了尾调用优化,防止过早stack overflow,所以我们能在debug的backtrace中看到objc_msgSend
105+
106+
```objective-c
107+
<return_type>class_selector(id self, SEL_cmd, ...)
108+
```
109+
110+
111+
112+
#### No.12: 理解消息转发机制
113+
114+
* 对象无法响应selector时触发消息转发流程
115+
116+
* 通过runtime的动态方法解析,可以在用到某个方法时再将其加入类中,失败return NO
117+
118+
* 动态方法解析失败,可以把消息转发给其他对象,失败return nil
119+
120+
* 开启完整消息转发机制,封装该消息的selector、target和params以创建NSIvocation对象
121+
122+
按照hierachy挨个发送消息,直到NSObject,若还是不行,则触发doesNotRecognizeSelector:抛出异常
123+
124+
![1.jpg](./images/1.jpg)
125+
126+
* CALayer通过在resolveInstanceMethod为每个动态添加的属性实现getter和setter
127+
128+
CALayer这样的类称之为兼容于键值编码的(key-value coding compliant)
129+
130+
131+
132+
#### No.13: 用method swizzling调试黑盒方法
133+
134+
* 通过method swizzling可以在运行期,向类新增方法和替换已有方法
135+
136+
```objective-c
137+
#import <objc/runtime.h>
138+
139+
@implementation UIViewController (Tracking)
140+
141+
+ (void)load {
142+
static dispatch_once_t onceToken;
143+
dispatch_once(&onceToken, ^{
144+
Class class = [self class];
145+
146+
SEL originalSelector = @selector(viewWillAppear:);
147+
SEL swizzledSelector = @selector(xxx_viewWillAppear:);
148+
149+
Method originalMethod = class_getInstanceMethod(class, originalSelector);
150+
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
151+
152+
// When swizzling a class method, use the following:
153+
// Class class = object_getClass((id)self);
154+
// ...
155+
// Method originalMethod = class_getClassMethod(class, originalSelector);
156+
// Method swizzledMethod = class_getClassMethod(class, swizzledSelector);
157+
158+
BOOL didAddMethod =
159+
class_addMethod(class,
160+
originalSelector,
161+
method_getImplementation(swizzledMethod),
162+
method_getTypeEncoding(swizzledMethod));
163+
164+
if (didAddMethod) {
165+
class_replaceMethod(class,
166+
swizzledSelector,
167+
method_getImplementation(originalMethod),
168+
method_getTypeEncoding(originalMethod));
169+
} else {
170+
method_exchangeImplementations(originalMethod, swizzledMethod);
171+
}
172+
});
173+
}
174+
175+
#pragma mark - Method Swizzling
176+
177+
- (void)xxx_viewWillAppear:(BOOL)animated {
178+
[self xxx_viewWillAppear:animated];
179+
NSLog(@"viewWillAppear: %@", self);
180+
}
181+
182+
@end
183+
```
184+
185+
186+
187+
####No.14: 理解类对象
188+
189+
* objc_object和objc_class
190+
191+
```objective-c
192+
typedefstructobjc_object {
193+
Class isa;
194+
}*id;
195+
196+
typedef struct objc_class*Class;
197+
struct objc_class {
198+
Class isa;
199+
Class super_class;
200+
const char*name;
201+
long version;
202+
long info;
203+
long instance_size;
204+
struct objc_ivar_list*ivars;
205+
struct objc_method_list**methodLists; struct objc_cache*cache;
206+
struct objc_protocol_list*protocols;
207+
};
208+
```
209+
210+
* 继承关系
211+
212+
实例方法存储在类对象里,类方法存储在元类里
213+
214+
![2.jpg](./images/2.jpg)
215+
216+
* 判断类时不要直接比较类对象(object.class),而是直接使用isKindOfClass:和isMemberOfClass:,因为某些类对象可能实现了消息转发功能
217+
218+
isKindOfClass:判断对象是否为某类或其派生类的实例
219+
220+
isMemberOfClass:判断对象是否为某个特定类的实例
221+
222+
```objective-c
223+
NSMutableDictionary *dict = [NSMutableDictionary new];
224+
[dict isMemberOfClass:[NSDictionary class]]; ///< NO
225+
[dict isMemberOfClass:[NSMutableDictionary class]]; ///< YES
226+
[dict isKindOfClass:[NSDictionary class]]; ///< YES
227+
[dict isKindOfClass:[NSArray class]]; ///< NO
228+
```

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp