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)中值为空的对象.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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"];

property 属性的关键字

  1. 表示原子性
  • atomic(默认): 线程安全,但是线程开销大,影响性能
    : atomic不一定是线程安全,因为其只保证 setter&getter 是线程安全,但是如果 threadA 进行写,这时其他现线程的读或者写会因为该操作而等待。但是当 threadA 写操作完成后,threadB 进行写操作,threadA 需要读操作的时候,可能会获得 threadB 线程的值,这就破坏了线程安全。而且如果 threadC 在 threadA 线程读操作之前 release 了, 会导致崩溃。
    所以 atomic 所说的线程安全只是保证了 getter和setter 存取方法的线程安全,并不能保证整个对象是线程安全的。
  • nonatomic: 非线程安全
  1. 表示引用计数
  • assign: assign 用于非指针变量,一般用于基础类型和C数据类型,这些类型不是对象,统一由系统栈进行内存管理。
  • weak: 对对象的弱引用,不增加对象的引用计数,也不持有对象,当对象消失后指针自动指向nil,所以这里也就防止了野指针的存在。
  • strong: 对对象的强引用,会增加对象的引用计数,如果指向了一个空对象,会造成野指针,平常我们用得最多的应该也是strong了。
  • copy: 建立一个引用计数为1的新对象,赋值时对传入值进行一份拷贝,所以使用 copy 关键字的时候,你将一个对象复制给该属性,该属性并不会持有那个对象,而是会创建一个新对象,并将那个对象的值拷贝给它。而使用 copy 关键字的对象必须要实现 NSCopying 协议。
  • unsafe_unretained: 跟 weak 类似,声明一个弱引用,但是当引用计数为 0 时,变量不会自动设置为 nil,现在基本都用 weak 了。
  1. 表示权限
  • 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. 对于不可变的容器类对象(如 NSArrayNSSetNSDictionary)进行 mutableCopy 操作,内存地址发生了变化,但是其中的元素内存地址并没有发生变化,属于单层深拷贝。
  2. 对于可变集合类对象(如 NSMutableArrayNSMutableSetNSMutableDictionary),不管是进行 copy 操作还是 mutableCopy操作,其内存地址都发生了变化,但是其中的元素内存地址都没有发生变化,属于单层深拷贝。

深拷贝

指拷贝一个对象的具体内容,拷贝结束之后,两个对象的值虽然是相同的,但是指向的内存地址是不同的。两个对象之间也互不影响,互不干扰。

  1. 非集合类型的 copymutableCopy

    • NSString
      • copy 内存地址不变,浅拷贝
      • mutableCopy 内存地址改变,深拷贝
    • NSMutableString
      • copy 内存地址改变,深拷贝
      • mutableCopy 内存地址改变,深拷贝
  2. 集合类型的 copymutableCopy

    • NSArray
      • copy 内存地址不变,内部元素地址不变,浅拷贝
      • mutableCopy 内存地址改变,但是内部元素地址不变,单层深拷贝
    • NSMutableArray
      • copy 内存地址改变,但是内部元素地址不变,单层深拷贝
      • mutableCopy 内存地址改变,但是内部元素地址不变,单层深拷贝
  3. 实现集合类型的深拷贝
  • 通过归解档生成两份完全独立的对象,但是前提是对象(子对象)必须全部支持 NSCoding 协议推荐使用 YYModel + NSCopying 这样就不用自己写逻辑代码了 😁

  • 自己实现一个深拷贝协议

    1
    2
    3
    @protocol YSDeepCopy <NSObject>
    - (id)ys_deepCopy;
    @end

@interface NSArray (YSDeepCopy)
@end

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

- (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;
}

[mArray addObject:deepCopySubObject ?: subObject];
}

if ([self isKindOfClass:[NSMutableArray class]]) {
return mArray;
} else {
return [NSArray arrayWithArray:mArray];
}
}

但是如果 数组里面 添加的是自己定义的Model, 则应该实现 ModelNSCopying 协议, 或者 YSDeepCopy 协议
推荐使用 YYModel + NSCopying 这样就不用自己写逻辑代码了 😁



-------------The End-------------

本文标题:iOS基础知识

文章作者:kysonyangs

发布时间:2016年02月05日 - 19:02

最后更新:2018年12月12日 - 16:12

原始链接:https://kysonyangs.github.io/default/iOS基础知识/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。