文章

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 Huggingcontent Compression Resistance

不想变大/不想变小约束 灰常有用哦

layoutSubviews方法调用

  1. init方法不会调用
  2. addSubview方法等时候会调用
  3. bounds改变的时候调用
  4. scrollView滚动的时候会调用scrollView的layoutSubviews方法(所以不建议在scrollView的layoutSubviews方法中做复杂逻辑)
  5. 旋转设备的时候调用
  6. 子视图被移除的时候调用

内存的几大区域

栈区(Stack)

由编译器自动分配并释放,存放函数的参数值,局部变量等。栈是系统数据结构,对应线程/进程是唯一的。优点是快速高效,缺点时有限制,数据不灵活。[先进后出]

栈空间分 静态分配 和 动态分配两种。

  • 静态分配是编译器完成的,比如自动变量(auto)的分配
  • 动态变量是由 alloc 函数完成的
  • 栈的动态分配无需释放(是自动的),也就没有释放函数。
  • 为可移植的程序起见,栈的动态分配是不被鼓励的。

堆区(heap)

由程序员分配和释放,如果程序员不释放,程序结束时,可能会由操作系统回收 ,比如在 ios 中 alloc 都是存放在堆中。

优点是灵活方便,数据适应面广泛,但是效率有一定降低。

  • 堆是函数库内部数据结构,不一定唯一。
  • 不同堆的分配的内存无法相互操作。
  • 堆空间的分配总是动态的

虽然程序结束时所有的数据空间都会被释放回系统,但是精确的申请内存,释放内存匹配是良好程序的基本要素。

全局区(静态区) (static)

全局变量和静态变量的存储是放在一起的,初始化的全局变量和静态变量存放在一块区域,未初始化的全局变量和静态变量在相邻的另一块区域,程序结束后由系统释放。

文字常量区

存放常量字符串,程序结束后由系统释放;

代码区

存放函数的二进制代码

CGRectInset 与 CGRectOffset

CGRectInset 与 CGRectOffset 都是通过参数改变 CGRect 并返回一个 CGRect 类型的数据。总结出两者的区别在于:CGRectInset 会进行平移和缩放两个操作。CGRectOffset 做的只是平移。

  1. CGRect CGRectInset(CGRect rect, CGFloat dx, CGFloat dy) 通过第二个参数 dx 和第三个参数 dy 重置第一个参数 rect 作为结果返回。重置的方式为,首先将 rect 的坐标(origin)按照(dx,dy) 进行平移,然后将 rect 的大小(size) 宽度缩小2倍的 dx,高度缩小2倍的 dy。 注意: dx dy 正数 左右两边缩小,负数则是放大

  2. 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菜单中提供了几个菜单项来检测影响帧率的一些因素:

  1. Color Blended Layers: 高亮显示有混合操作的区域;
  2. Color Copied Images: 高亮显示被拷贝的图片。拷贝图片意味着Core Animation需要拷贝一份图片并发送给render server,这对内存和CPU的使用都是昂贵的;
  3. Color Misaligned Images: 高亮显示缩放或拉伸过的图片,或者没有正确对齐对到像素边界的图片;
  4. 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 生命周期
![](https://camo.githubusercontent.com/6ea2fb065c49dd902e98207429e65f7a28ea6997/687474703a2f2f6f6b68716d746438712e626b742e636c6f7564646e2e636f6d2f696d6167652f6a70672f566965772545372539342539462545352539312542442545352539312541382545362539432539462e706e67)

### 深拷贝与浅拷贝
#### 浅拷贝
指只是将对象内存地址多了一个引用,也就是说,拷贝结束之后,两个对象的值不仅相同,而且对象所指的内存地址都是一样的。

#### 单层深拷贝
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, 则应该实现 ModelNSCopying 协议, 或者 YSDeepCopy 协议 推荐使用 YYModel + NSCopying 这样就不用自己写逻辑代码了 😁

本文由作者按照 CC BY 4.0 进行授权