应用沙盒
- 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
2int 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
3int *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
11NSArray *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 属性的关键字
- 表示原子性
atomic
(默认): 线程安全,但是线程开销大,影响性能
注: atomic不一定是线程安全,因为其只保证 setter&getter 是线程安全,但是如果 threadA 进行写,这时其他现线程的读或者写会因为该操作而等待。但是当 threadA 写操作完成后,threadB 进行写操作,threadA 需要读操作的时候,可能会获得 threadB 线程的值,这就破坏了线程安全。而且如果 threadC 在 threadA 线程读操作之前 release 了, 会导致崩溃。
所以 atomic 所说的线程安全只是保证了 getter和setter 存取方法的线程安全,并不能保证整个对象是线程安全的。nonatomic
: 非线程安全
- 表示引用计数
assign
: assign 用于非指针变量,一般用于基础类型和C数据类型,这些类型不是对象,统一由系统栈进行内存管理。weak
: 对对象的弱引用,不增加对象的引用计数,也不持有对象,当对象消失后指针自动指向nil,所以这里也就防止了野指针的存在。strong
: 对对象的强引用,会增加对象的引用计数,如果指向了一个空对象,会造成野指针,平常我们用得最多的应该也是strong了。copy
: 建立一个引用计数为1的新对象,赋值时对传入值进行一份拷贝,所以使用 copy 关键字的时候,你将一个对象复制给该属性,该属性并不会持有那个对象,而是会创建一个新对象,并将那个对象的值拷贝给它。而使用 copy 关键字的对象必须要实现 NSCopying 协议。unsafe_unretained
: 跟 weak 类似,声明一个弱引用,但是当引用计数为 0 时,变量不会自动设置为 nil,现在基本都用 weak 了。
- 表示权限
readwrite
: 可读可写readonly
: 只读,当你希望暴露出来的属性不能被外界修改时就需要申明为 readonly 。
id 和 instanceType
- instancetype 和 id 都是万能指针,指向对象。
- id 在编译的适合不能判断对象的真实类型,instancetype 在编译的时候可以判断对象的真实类型
- id 可以用来定义变量,可以作为返回值类型,可以作为形参类型;instancetype 只能作为返回值类型
- instancetype 只适用于初始化方法和便利构造器的返回值类型
@synthesize 和 @dynamic
- @property 有两个对应的词,一个是 @synthesize,一个是 @dynamic。如果 @synthesize 和 @dynamic 都没写,那么默认的就是 @syntheszie var = _var;
- @synthesize 的语义是如果你没有手动实现 setter方法和 getter方法,那么编译器会自动为你加上这两个方法。
- @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 生命周期
深拷贝与浅拷贝
浅拷贝
指只是将对象内存地址多了一个引用,也就是说,拷贝结束之后,两个对象的值不仅相同,而且对象所指的内存地址都是一样的。
单层深拷贝
- 对于不可变的容器类对象(如
NSArray
、NSSet
、NSDictionary
)进行mutableCopy
操作,内存地址发生了变化,但是其中的元素内存地址并没有发生变化,属于单层深拷贝。 - 对于可变集合类对象(如
NSMutableArray
、NSMutableSet
、NSMutableDictionary
),不管是进行copy
操作还是mutableCopy
操作,其内存地址都发生了变化,但是其中的元素内存地址都没有发生变化,属于单层深拷贝。
深拷贝
指拷贝一个对象的具体内容,拷贝结束之后,两个对象的值虽然是相同的,但是指向的内存地址是不同的。两个对象之间也互不影响,互不干扰。
非集合类型的
copy
与mutableCopy
NSString
copy
内存地址不变,浅拷贝mutableCopy
内存地址改变,深拷贝
NSMutableString
copy
内存地址改变,深拷贝mutableCopy
内存地址改变,深拷贝
集合类型的
copy
与mutableCopy
NSArray
copy
内存地址不变,内部元素地址不变,浅拷贝mutableCopy
内存地址改变,但是内部元素地址不变,单层深拷贝
NSMutableArray
copy
内存地址改变,但是内部元素地址不变,单层深拷贝mutableCopy
内存地址改变,但是内部元素地址不变,单层深拷贝
- 实现集合类型的深拷贝
通过归解档生成两份完全独立的对象,但是前提是
对象
(子对象
)必须全部支持NSCoding
协议。推荐使用YYModel + NSCopying
这样就不用自己写逻辑代码了 😁自己实现一个深拷贝协议
1
2
3@protocol YSDeepCopy <NSObject>
- (id)ys_deepCopy;
@end
@interface NSArray (YSDeepCopy)
@end1
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, 则应该实现 Model
的 NSCopying
协议, 或者 YSDeepCopy
协议
推荐使用 YYModel + NSCopying
这样就不用自己写逻辑代码了 😁