其他零散的知识点
macOS上测试远程推送的工具
空指针 和 野指针
- 空指针:不指向任何内存地址的指针,给空指针发任何消息不会任何问题,相当于 [nil xxx];
- 野指针:指向一块被释放调的内存的指针,给野指针发消息会报异常,处理方法是将野指针置空:p = nil;
static 和 extern 的总结
static
- 修饰全局变量时,全局变量只能在定义该全局变量的文件中才能使用,跟其他源文件中的同名变量互不干扰
- 修饰函数时,该函数为内部函数,只能在定义该函数的文件中使用。不同的文件中可以有同名的内部函数,则互不干扰。
extern
(不管用来修饰 全局变量 还是 函数,extern 都可以省略)
- 修饰全局变量时,只表示声明,不代表定义。多个源文件中同名的全局变量都代表着同一个变量
- 修饰函数时,该函数为内部函数,可供其他文件调用。调用其他文件中的外部函数,需在当前文件中用extern声明该外部函数,然后就可以使用
- 定义函数时省略 extern,则隐含为外部函数
CoreGraphics 绘图
CGContextSaveGState 与 UIGraphicsPushContext 的区别
- CGContextSaveGState 和 CGContextRestoreGState 是保存和恢复上下文的绘制状态。
- UIGraphicsPushContext(ctx) 和 UIGraphicsPopContext() 是对上下文做入栈出栈,入栈后上下文 ctx 就成了当前上下文
Quartz 2D,是iOS和Mac OS X环境下的二维绘图引擎。 涉及内容包括:基于路径的绘图,透明度绘图,遮盖,阴影,透明层,颜色管理,防锯齿渲染,生成PDF,以及PDF元数据相关处理
ipa 包的瘦身
- 删除没用到的类
- 删除没用到的图片,对图片资源进行无损压缩
- 编译选项优化,检查Xcode的设置的编译指令集是否有多余的可以删除
- 第三方库只用生产环境的,不要使用生产和调试环境都可用的库
KVO & 通知
- KVO
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- (void)useKVO {
[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:@"无聊"];
self.person.name = @"李四";
}
// object:被观察的对象
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if ([keyPath isEqualToString:@"name"]) {
NSLog(@"旧值: %@ 新值:%@", change[NSKeyValueChangeOldKey], change[NSKeyValueChangeNewKey]);
}
}
// 记得移除
- (void)dealloc {
[self.person removeObserver:self forKeyPath:@"name" context:@"无聊"];
}
- 通知 NSNotification
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
- (void)viewDidLoad {
[super viewDidLoad];
// 通知
// 最后一个参数相当于二次校验,一般直接传 nil 。
// 当值为 nil 时,只要 name 相同就能接收通知;不为 nil 时,需要 name 和 object 两个都相同才会接收通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test3:) name:@"NotificationName" object:nil];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// 最后一个参数的作用是传递数据
[[NSNotificationCenter defaultCenter] postNotificationName:@"NotificationName" object:@"come_on" userInfo:@{@"say" : @"hello~"}];
}
- (void)test1:(id)object {
// 不会被调用
}
- (void)test2:(id)object {
// 不会被调用
}
- (void)test3:(id)object {
if ([object isKindOfClass:[NSNotification class]]) {
// 会被调用
}
}
// 记得移除
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
TCP协议和UDP协议的差别
比较 | TCP协议 | UDP协议 |
---|---|---|
是否连接 | 面向连接 | 面向非连接 |
传输可靠性 | 可靠 | 不可靠,易丢包 |
应用场合 | 传输大量数据 | 少量数据 |
速度 | 慢,传输前要经过3次握手 | 快 |
使用规范:
1
2
// 为了在 Swift 和 OC 混编时,让 Swift 编译器知道一个 OC 对象是可空还是不可空的,对基本类型无效
* nonnull/nullable 不可空/可空
对于 OC 属性、方法返回值、方法参数的修饰,使用:nonnull / nullable
;对于 C 函数的参数、Block 的参数、Block 返回值的修饰,使用:_Nonnull / _Nullable
,建议弃用 __nonnull / __nullable
。
1
2
3
4
// 声明属性的修饰:
@property (nonatomic, copy, nullable) NSString *aString;
@property (nonatomic, copy) NSString * __nullable aString;
@property (nonatomic, copy) NSString * _Nullable aString;
1
2
3
4
// 方法返回值修饰:
- (nullable NSString *)method;
- (NSString * __nullable)method;
- (NSString * _Nullable)method;
1
2
3
4
// 方法参数修饰:
- (void)methodWithString:(nullable NSString *)aString;
- (void)methodWithString:(NSString * _Nullable)aString;
- (void)methodWithString:(NSString * __nullable)aString;
1
NS_ASSUME_NONNULL_BEGIN 和 NS_ASSUME_NONNULL_END之间,定义的所有对象属性和方法默认都是 nonnull
null_resettable
:get
不能返回空,set
可以为空(注意:如果使用null_resettable
,必须 重写get
方法或者set
方法,处理传递的值为空的情况)
1
2
// 书写方式:
@property (nonatomic, copy, null_resettable) NSString *name;
_Null_unspecified
:不确定是否为空
__kindof
1
2
3
@property (nonatomic, strong) NSArray<UIView *> *viewCollection;
// 参数可以是 UIView 类型及其子类
@property (nonatomic, strong) NSArray<__kindof UIView *> *viewCollection;
深复制和浅复制?
- copy:返回不可变对象
- mutableCopy:返回可变对象
- 不可变对象的不可变复制是浅复制,不产生新对象,只是被复制对象的引用计数加一;其他复制都是深复制。
- 这里的深复制,对于数组来说实际上只是单层深复制,只对数组本身进行深复制,元素还是浅复制
- 若要实现真正的多层深复制,要把对象先归档再解档;但如果是数组对象,还必须让数组元素实现 NSCopying 和 NSMutableCopying 协议
- 把一个不可变对象赋值给一个可变对象时,这个对象调用添加元素或者拼接字符串等方法时会报错,因为它的本质还是一个不可变对象
[array mutableCopy];
只是对 array
进行了深复制,array
数组内部的元素只是指针的复制,属于浅复制
要想实现数组及其元素的完全深复制,需要数组元素对象实现 NSCoding
协议,并用归档、反归档
1
2
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array];
NSArray *array2 = [NSKeyedUnarchiver unarchiveObjectWithData:data];
初始化方法
initialize 和 load
区别在于:load
是只要类有被编译到,则这个类不管有没有被其他文件引用、使用,都会调用这个类的load
方法;而initialize
是在类或者其子类的第一个方法被调用前调用。所以即使类文件被引用进来,但是没有使用,那么initialize
也不会被调用。
相同点在于:方法都只会被调用一次,都是类方法。
init:
在此方法中初始化一些属性变量等,如果是 UIView 的 init:方法内部会调用 initWithFrame: 方法
initWithFrame:
继承 UIView 的自定义控件,可重写此方法来初始化子控件
initWithCoder:
当从 nib 文件中加载对象的时候,如果重写了此方法,系统会自动调用。
loadView:
- 如果控制器重写了此方法,且没有使用 nib 视图页面,则系统会自动调用此方法,
不能自己调用
。 - 当 view 需要被展示而它却是 nil 时,控制器会调用该方法。
- 在此方法中创建 UIView 并赋值给 self.view,但是不能使用 [self.view addSubview:xxx],因为此时 self.view 还是 nil,
1
2
3
4
5
6
7
8
9
10
11
12
13
- (void)loadView {
[super loadView];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 100, 100, 100)];
label.backgroundColor = [UIColor orangeColor];
// 如果前面没调用 [super loadView]; 则 self.view 还是 nil ,调用下面这行代码添加控
// 件到 self.view,又会调用 loadView 方法,会陷入死循环
// 但是如果有[super loadView]; 则此时 self.view 已经不为空,调用下面这行代码没问题
// [self.view addSubview:label];
self.view = label;
}
执行顺序:
当视图 A 通过 push 切换到视图 B 时真实的调用顺序是:
1、B 视图 viewDidLoad 2、A 视图 viewWillDisappear 3、B 视图 viewVillAppear 4、A 视图 viewDidDisappear 5、B 视图 viewDidAppear
UI 种类:
- 一进入界面的时的默认 UI
- 网络慢时正在加载的 UI
- 网络正常时的 UI(有时需要分 wifi 、 移动数据)
- 网络连接失败时的 UI
- 有网/无网,有GPS / 无GPS 等来回切换时,界面 UI 的显示情况
- 若正在执行动画时程序进入后台,再进入前台时要怎么显示
const 用法、值传递、地址传递、引用传递
- 值传递不改变实参的值,地址传递和引用传递都会改变实参的值
- 引用传递和值传递除了形参写法不一样外,调用时的写法是一样的,如:Exchg1(a, b); Exchg3(a, b);
1
2
3
4
5
6
7
8
// 值传递:调用时写法是 Exchg1(a, b);
Exchg1(int x, int y)
// 地址传递:调用时写法是 Exchg2(&a, &b);
Exchg2(int *px, int *py)
// 引用传递:调用时写法是 Exchg3(a, b); 注意定义时的形式参数的格式与值传递不同
Exchg3(int &x, int &y)
注意: 在 const 之后的东西(*p 或者 p)是常量,不可变
1
2
3
4
5
// 效果是完全一样的,x 不可变
const int x 和 int const x
// 效果不一样。前者 *p 不可变,p 可变;后者 p 不可变,*p 可变
const int *p 和 int *const p
去除前后的空格、换行
1
2
3
4
NSString *string = @" \n Hello this is a long string! \n ";
// 去除前后的空格、换行(\n),以下代码输出的内容是 @"Hello this is a long string!"
string = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
1
2
3
4
5
6
// C 字符串转 OC 字符串
// NSString *name = @("Jackey"); 不建议?
NSString *str = [NSString stringWithUTF8String:"123abc"];
// OC 字符串转 C 字符串
const char *s = [str cStringUsingEncoding:NSUTF8StringEncoding];