文章

其他零散的知识点

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];
本文由作者按照 CC BY 4.0 进行授权