iOS基础知识
应用沙盒
- Documents: 保存应用运行时生成的需要持久化的数据,iTunes同步设备时会备份该目录。例如,游戏应用可将游戏存档保存在该目录.
- Library:
- Caches: 保存应用运行时生成的需要持久化的数据,一般存储体积大、不需要备份的非重要数据,缓存文件存储地. - Preferences: 保存应用的所有偏好设置,iOS的Settings(设置)应用会在该目录中查找应用的设置信息,iTunes同步设备时会备份该目录.
- tmp: 保存应用运行时所需的临时数据,使用完毕后再将相应的文件从该目录删除;应用没有运行时,系统也可能会清除该目录下的文件;iTunes同步设备时不会备份该目录.
路径获取:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 沙盒根目录
NSLog(@"%@", NSHomeDirectory());
// Documents
NSLog(@"%@", [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"]);
// OR
NSLog(@"%@", [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]);
// Library - Caches
NSLog(@"%@", [[NSHomeDirectory() stringByAppendingPathComponent:@"Library"] stringByAppendingPathComponent:@"Caches"]);
// OR
NSLog(@"%@", [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject]);
Preference
[NSUserDefaults standardUserDefaults] ...
// tmp
NSLog(@"t - %@", NSTemporaryDirectory());
content Hugging / content Compression Resistance
不想变大/不想变小约束 灰常有用哦
layoutSubviews方法调用
- init方法不会调用
- addSubview方法等时候会调用
- bounds改变的时候调用
- scrollView滚动的时候会调用scrollView的layoutSubviews方法(所以不建议在scrollView的layoutSubviews方法中做复杂逻辑)
- 旋转设备的时候调用
- 子视图被移除的时候调用
内存的几大区域
栈区(Stack)
由编译器自动分配并释放,存放函数的参数值,局部变量等。栈是系统数据结构,对应线程/进程是唯一的。优点是快速高效,缺点时有限制,数据不灵活。[先进后出]
栈空间分 静态分配 和 动态分配两种。
- 静态分配是编译器完成的,比如自动变量(auto)的分配
- 动态变量是由 alloc 函数完成的
- 栈的动态分配无需释放(是自动的),也就没有释放函数。
- 为可移植的程序起见,栈的动态分配是不被鼓励的。
堆区(heap)
由程序员分配和释放,如果程序员不释放,程序结束时,可能会由操作系统回收 ,比如在 ios 中 alloc 都是存放在堆中。
优点是灵活方便,数据适应面广泛,但是效率有一定降低。
- 堆是函数库内部数据结构,不一定唯一。
- 不同堆的分配的内存无法相互操作。
- 堆空间的分配总是动态的
虽然程序结束时所有的数据空间都会被释放回系统,但是精确的申请内存,释放内存匹配是良好程序的基本要素。
全局区(静态区) (static)
全局变量和静态变量的存储是放在一起的,初始化的全局变量和静态变量存放在一块区域,未初始化的全局变量和静态变量在相邻的另一块区域,程序结束后由系统释放。
文字常量区
存放常量字符串,程序结束后由系统释放;
代码区
存放函数的二进制代码
CGRectInset 与 CGRectOffset
CGRectInset 与 CGRectOffset 都是通过参数改变 CGRect 并返回一个 CGRect 类型的数据。总结出两者的区别在于:CGRectInset 会进行平移和缩放两个操作。CGRectOffset 做的只是平移。
CGRect CGRectInset(CGRect rect, CGFloat dx, CGFloat dy)通过第二个参数 dx 和第三个参数 dy 重置第一个参数 rect 作为结果返回。重置的方式为,首先将 rect 的坐标(origin)按照(dx,dy) 进行平移,然后将 rect 的大小(size) 宽度缩小2倍的 dx,高度缩小2倍的 dy。 注意: dx dy 正数 左右两边缩小,负数则是放大CGRect CGRectOffset(CGRect rect, CGFloat dx, CGFloat dy)rect 按照(dx,dy)进行平移。
断言 NSAssert() 和 NSParameterAssert 区别和用处
NSAssert 和 assert 是断言,主要的差别是 assert 在断言失败的时候只是简单的终止程序, 而 NSAssert 会报告出错误信息并且打印出来. 所以尽管的使用 NSAssert,可以不去使用 assert.
NSAssert/NSCAssert 两者的差别, 前者是适合于Objective-C的方法, 后者是适用于 C 的函数. NSParameterAssert/NSCparameterAssert 两者的区别也是前者适用于 Objective-C 的方法, 后者适用于 C 的函数. NSAssert/NSCAssert 和 NSParameterAssert/NSCparameterAssert 的区别是前者是所有断言, 后者只是针对参数是否存在的断言, 所以可以先进行参数的断言,确认参数是正确的,再进行所有的断言,确认其他原因. NSAssert的用法
1
2
int a = 4;
NSAssert(a == 5, @"a must equal to 5"); //第一个参数是条件,如果第一个参数不满足条件,就会记录和打印第二个参数
NSParameterAssert的用法
1
NSParameterAssert(str); //只需要一个参数,如果参数存在程序继续运行,如果参数为空,则程序停止打印日志
注: Xcode 已经默认将release环境下的断言取消了, 免除了忘记关闭断言造成的程序不稳定.
iOS模拟器的Debug菜单
iOS模拟器的Debug菜单中提供了几个菜单项来检测影响帧率的一些因素:
Color Blended Layers: 高亮显示有混合操作的区域;Color Copied Images: 高亮显示被拷贝的图片。拷贝图片意味着Core Animation需要拷贝一份图片并发送给render server,这对内存和CPU的使用都是昂贵的;Color Misaligned Images: 高亮显示缩放或拉伸过的图片,或者没有正确对齐对到像素边界的图片;Color Offscreen-Rendered: 高亮显示离屏渲染的层对象。
nil、NIL、NULL、 NSNull 的区别
- nil: 指向一个对象的空指针, 对 Objective-C id 对象赋空值.
1
NSString *str = nil;
- Nil: 指向一个类的空指针, 表示对类进行赋空值.
1
Class Class1 = Nil;
- NULL: 指向其他类型(如:基本类型、C类型)的空指针, 用于对非对象指针赋空值.
1 2 3
int *intA = NULL; char *charC = NULL; struct structStr = NULL;
- NSNull: 在集合对象中,表示空值的对象. NSNull在Objective-C中是一个类. NSNull有
+ (NSNull *)null;单例方法. 多用于集合(NSArray,NSDictionary)中值为空的对象. ``` NSArray *array = [NSArray arrayWithObjects: [[NSObject alloc] init], [NSNull null], nil];
NSMutableDictionary *mutableDictionary = [[NSMutableDictionary alloc] init]; [mutableDictionary setObject:nil forKey:@”Key-nil”]; // 会引起Crash [mutableDictionary setObject:[NSNull null] forKey:@”Key-nil”]; // 不会引起Crash //所以在使用时,如下方法是比较安全的 [mutableDictionary setObject:(nil == value ? [NSNull null] : value) forKey:@”Key”];
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
### property 属性的关键字
1. 表示原子性
* `atomic`(默认): 线程安全,但是线程开销大,影响性能
*注*: atomic不一定是线程安全,因为其只保证 setter&getter 是线程安全,但是如果 threadA 进行写,这时其他现线程的读或者写会因为该操作而等待。但是当 threadA 写操作完成后,threadB 进行写操作,threadA 需要读操作的时候,可能会获得 threadB 线程的值,这就破坏了线程安全。而且如果 threadC 在 threadA 线程读操作之前 release 了, 会导致崩溃。
所以 atomic 所说的线程安全只是保证了 getter和setter 存取方法的线程安全,并不能保证整个对象是线程安全的。
* `nonatomic`: 非线程安全
2. 表示引用计数
* `assign`: assign 用于非指针变量,一般用于基础类型和C数据类型,这些类型不是对象,统一由系统栈进行内存管理。
* `weak`: 对对象的弱引用,不增加对象的引用计数,也不持有对象,当对象消失后指针自动指向nil,所以这里也就防止了野指针的存在。
* `strong`: 对对象的强引用,会增加对象的引用计数,如果指向了一个空对象,会造成野指针,平常我们用得最多的应该也是strong了。
* `copy`: 建立一个引用计数为1的新对象,赋值时对传入值进行一份拷贝,所以使用 copy 关键字的时候,你将一个对象复制给该属性,该属性并不会持有那个对象,而是会创建一个新对象,并将那个对象的值拷贝给它。而使用 copy 关键字的对象必须要实现 NSCopying 协议。
* `unsafe_unretained`: 跟 weak 类似,声明一个弱引用,但是当引用计数为 0 时,变量不会自动设置为 nil,现在基本都用 weak 了。
3. 表示权限
* `readwrite`: 可读可写
* `readonly`: 只读,当你希望暴露出来的属性不能被外界修改时就需要申明为 readonly 。
### id 和 instanceType
* instancetype 和 id 都是万能指针,指向对象。
* id 在编译的适合不能判断对象的真实类型,instancetype 在编译的时候可以判断对象的真实类型
* id 可以用来定义变量,可以作为返回值类型,可以作为形参类型;instancetype 只能作为返回值类型
* instancetype 只适用于初始化方法和便利构造器的返回值类型
### @synthesize 和 @dynamic
1. @property 有两个对应的词,一个是 @synthesize,一个是 @dynamic。如果 @synthesize 和 @dynamic 都没写,那么**默认的就是 @syntheszie var = _var;**
2. @synthesize 的语义是如果你没有手动实现 setter方法和 getter方法,那么编译器会自动为你加上这两个方法。
3. @dynamic 告诉编译器,属性的 setter与getter 方法由用户自己实现,不自动生成。(当然对于readonly的属性只需提供getter即可)。假如一个属性被声明为 @dynamic var,然后你没有提供 @setter方法和@getter 方法,编译的时候没问题,但是当程序运行到instance.var = someVar,由于缺setter方法会导致程序崩溃;或者当运行到 someVar = var时,由于缺getter方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。
### UIView 和 CALayer
* UIView 和 CALayer 算是相互补充的关系。
* UIView = CALayer.delegate
* UIView : 负责用户的交互事件。
* CALayer: 负责图像和动画的渲染。
### Bounds 和 Frame
* Bounds: 一般是相对于自身来说的,是控件的内部尺寸。如果你修改了 Bounds,那么子控件的相对位置也会发生改变。
* Frame: 是相对于父控件来说的,是控件的外部尺寸。
### UIViewController 生命周期

### 深拷贝与浅拷贝
#### 浅拷贝
指只是将对象内存地址多了一个引用,也就是说,拷贝结束之后,两个对象的值不仅相同,而且对象所指的内存地址都是一样的。
#### 单层深拷贝
1. 对于不可变的容器类对象(如 `NSArray`、`NSSet`、`NSDictionary`)进行 `mutableCopy` 操作,内存地址发生了变化,但是其中的元素内存地址并没有发生变化,属于单层深拷贝。
2. 对于可变集合类对象(如 `NSMutableArray`、`NSMutableSet`、`NSMutableDictionary`),不管是进行 `copy` 操作还是 `mutableCopy`操作,其内存地址都发生了变化,但是其中的元素内存地址都没有发生变化,属于单层深拷贝。
#### 深拷贝
指拷贝一个对象的具体内容,拷贝结束之后,两个对象的值虽然是相同的,但是指向的内存地址是不同的。两个对象之间也互不影响,互不干扰。
1. 非集合类型的 `copy` 与 `mutableCopy`
* `NSString`
* `copy` 内存地址不变,浅拷贝
* `mutableCopy` 内存地址改变,深拷贝
* `NSMutableString`
* `copy` 内存地址改变,深拷贝
* `mutableCopy` 内存地址改变,深拷贝
2. 集合类型的 `copy` 与 `mutableCopy`
* `NSArray`
* `copy` 内存地址不变,内部元素地址不变,浅拷贝
* `mutableCopy` 内存地址改变,但是内部元素地址不变,单层深拷贝
* `NSMutableArray`
* `copy` 内存地址改变,但是内部元素地址不变,单层深拷贝
* `mutableCopy` 内存地址改变,但是内部元素地址不变,单层深拷贝
3. 实现集合类型的深拷贝
* 通过归解档生成两份完全独立的对象,但是前提是`对象`(`子对象`)**必须全部支持 `NSCoding` 协议**。**推荐使用 `YYModel + NSCopying` 这样就不用自己写逻辑代码了 😁**
* 自己实现一个深拷贝协议
@protocol YSDeepCopy
- (id)ys_deepCopy; @end
1 2
@interface NSArray (YSDeepCopy) <YSDeepCopy> @end
(instancetype)ys_deepCopy { NSMutableArray *mArray = [NSMutableArray arrayWithCapacity:self.count];
for (id subObject in self) { id deepCopySubObject = nil; if ([subObject respondsToSelector:@selector(ys_deepCopy)]) { deepCopySubObject = [subObject ys_deepCopy]; } else if ([subObject conformsToProtocol:@protocol(NSCopying)]) { deepCopySubObject = [subObject copy]; } else { NSAssert1(NO, @”Class "%s" not support YSDeepCopy”, object_getClassName(subObject)); deepCopySubObject = subObject; }
1
[mArray addObject:deepCopySubObject ?: subObject]; }
if ([self isKindOfClass:[NSMutableArray class]]) { return mArray; } else { return [NSArray arrayWithArray:mArray]; } } ``` 但是如果 数组里面 添加的是自己定义的Model, 则应该实现
Model的NSCopying协议, 或者YSDeepCopy协议 推荐使用YYModel + NSCopying这样就不用自己写逻辑代码了 😁