《基础知识整理温习一》

                                              《基础知识整理温习一》

1、UIView 和CAlayer的区别和联系?

view是layer的代理,view主要处理与用户的交互,layer负责视图的绘制和动画的渲染,UIView继承自UIResponder,CALayer 继承NSObject,不可以响应事件,不可以处理事件。UIView主要是对显示内容的管理而 CALayer 主要侧重显示内容的绘制。

2、nil  Nil  null [NSNull null]的区别?

nil为一个对象的空指针。

Nil为一个类的空指针。

null为一个指向基本数据类型,C类型的空指针。

[NSNull null]是一个值为空的对象。

nil == Nil == Null 所以它们在object-c中是可以通用的,都表示空对象的意思。

[NSNull null]是值为空的对象。

“空对象”是已经释放了内存地址的对象,即不存在的对象。

“值为空的对象”是分配了地址,但是没有值得对象,是实际存在的对象。

3、block ?

3.1什么是block?

带有自动变量(局部变量)的匿名函数。

Block实质就是Objective-C对象。

int (^myBlock) (int num);

int 返回值类型

^语法标记,标记它是一个block类型

int num 函参类型和函参的变量名

//定义

int (^myBlock) (int num) = ^(int num ) {

       return num + 10;

};

//循环引用的例子:

Person *person = [[Person alloc] init];

person.age = 20;

__weak typeof (person) weakPerson = person;

person.block = ^{

    NSLog(@"age is %d",weakPerson.age);

};

person.block();

总结:循环引用是因为,对象强引用了blcok,block内部也强引用了捕获进去的对象,相互引用无法释放。

解决循环引用:

1、用__weak、__unsafe_unretained解决

2、用__block解决

__weak 、 __block、__unsafe_unretained的区别?

_block不管是ARC还是MRC模式下都可以使用,可以修饰对象,还可以修饰基本数据类型,__block对象可以在block中被重新赋值,__weak不可以,__block对象在ARC下可能会导致循环引用,非ARC下会避免循环引用。

__weak只能修饰对象,不能修饰基本数据类型,只在ARC下使用,可以避免循环引用,。 

__weak当对象销毁时,会自动指向nil.

__unsafe_unretained: 当对象销毁时,会依然指向之前的内存空间,会造成野指针。


//并不是所有的block都会循环引用 ,比如下边这种:

 [UIView animateWithDuration:(NSTimeInterval)  animations:^{

      self.name = @"";

   }];

3.2  block为什么用copy?

block分为全局block,栈区block,堆区block。

block内部没有调用外部变量时存放在全局区(ARC和MRC下均是)

block使用了外部变量,这种情况也正式我们平时所常用的方式,Block的内存地址显示在栈区,栈区的特点就是创建的对象随时可能被销毁,一旦被销毁后续再次调用空对象就可能会造成程序崩溃,在对block进行copy后,block存放在堆区,这样让其拥有保存调用的外部变量的内存的能力,copy的原因是为了让Block在初始化作用域外进行正常访问外部变量。

3.3项目中怎么检查内存泄漏?

1、通过写一个基类控制器,BaseViewController,所有的页面都继承于它,然后重写dealloc方法,在页面返回的时候看有没有走该方法。或者通过写UI ViewController的分类,里面通过runtime的方法交换dealloc方法实现页面打印,这样不用所有的页面都继承于基类。

2、使用xcode的Instruments -> Leaks进行检测

3、使用三方库,MLeaksFinder进行检测。

4、属性修饰符?

Strong:除NSString\block以外的OC对象

Weak:修饰已经被添加到父控件上的控件,比如xib拖出来的控件, 以及 delegate

Assign: 基本数据类型、枚举、结构体(非OC对象)

Copy : NSString,block

4.1 NSString为什么要用copy修饰,用strong可以吗?有什么区别?

//用strong修饰的时候

@property (nonatomic ,strong) NSString *name;

-  (void)setName:(NSString *)name {

             [_name release];

              [name retain];

            _name = name;

}

//用copy修饰的时候

@property (nonatomic ,copy) NSString *name;

-  (void)setName:(NSString *)name {

             [_name release];

            _name = [name copy ];

}

copy修饰的时候是先释放旧值,然后把新值copy一份赋值给它,strong修饰的时候是先释放旧值,然后把新值赋值给它,为了防止可变字符串对其进行赋值时候内容被篡改,所以用copy修饰。

@property (nonatomic ,strong) NSString *name;

NSMutableString *newName = @"ZhangSan";

self.name = newName;

newName = @"LiSi";

self.name 会随着newName的改变而改变。

@property (nonatomic ,copy) NSString *name;

使用copy修饰的时候,是将新值copy一份然后赋值,当新值改变的时候,copy没变,所以不会影响属性的值。


4.2 delegate为什么用weak修饰?

避免循环引用


6828358-f7b5675f0f6e5d43.png

For Example: tableView

Controller - > tableView - >delegate ->Controller这形成了一个圈,如果delegate是strong的时候,就会造成循环引用。

4.3  Assign 和 Weak的区别?

Assign 修饰基础数据类型,非OC对象,如NSInter,CGFloat,BOOL等基础数据类型。不牵涉到内存管理。如果用assign修饰对象的时候,当修饰的对象销毁之后,指针不会置为nil,就会造成野指针的出现,当再向该对象发消息就会出现崩溃。

Weak 修饰OC对象,当修饰的对象销毁之后,指针会自动置为nil,之后再向该对象发消息也不会崩溃.

  4.3.1weak指针自动置为nil的底层实现?

  Runtime 维护了一个 Weak 表,用于存储所有 Weak 指针。Weak 表是一个哈希表,Key 是对象的地址,Value 是一个数组,数组里面放的是 Weak 指针的地址,然后遍历这个数组,把每个地址存储的数据设为 nil ,最后把这个 key-value entry 从 Weak 表中删除。

5、内存管理?

什么是内存管理?是指软件运行时对计算机内存资源的分配和使用的技术。其最主要的目的是如何高效,快速的分配,并且在适当的时候释放和回收内存资源。

想要知道内存怎么管理首先我们要知道内存是怎么分配的?

下面是iOS一个app的内存分布,分为代码区,常量区,全局/静态区,堆区,栈区。


6828358-fba5c4fb5ea15a12.png

app程序一般存放于ROM(储存行内存)中。启动app时,系统会把开启的app程序从ROM中转移到RAM(运行内存)中。不能再说了,再说就讲计算机组成原理了。

代码区 :存放程序的二进制代码。

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

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

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

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

MRC:Manual Reference Counting 手动引用计数器,需要我们手动管理对象引用计数器的值。

ARC:automatic reference counting自动引用计数,在程序编译时自动加入retain/release。在对象被创建时retain count+1,在对象被release时count-1,当count=0时,销毁对象。

Autoreleasepool:加入autoreleasepool对象会由系统自动加上autorelease方法,如果该对象引用计数为0,则销毁。

5.1 ARC 在编译时做了哪些工作?

自动调用保留(retain)与释放(release)的方法

相对于垃圾回收这类内存管理方案,ARC 不会带来运行时的额外开销,所以对于应用的运行效率不会有影响。ARC会把能够互相抵消retain、release、autorelease,操作简化,如果发现在同一个对象上执行了多次保留与释放操作,那么ARC有时可以成对的移除这两个操作。

5.2 ARC 在运行时做了哪些工作?

主要是指weak关键字。weak修饰的变量能够在引用计数为0时被自动设置成nil,显然是有运行时逻辑在工作的。关于原因会单独开一个问题

为了保证向后兼容性,ARC在运行时检测到类函数中的autorelease后紧跟其后retain,此时不直接调用对象的autorelease方法,而是改为调用objc_autoreleaseReturnValue。

objc_autoreleaseReturnValue会检视当前方法返回之后即将要执行的那段代码,若那段代码要在返回对象上执行retain操作,则设置全局数据结构中的一个标志位,而不执行autorelease操作,与之相似,如果方法返回了一个自动释放的对象,而调用方法的代码要保留此对象,那么此时不直接执行retain,而是改为执行objc_retainAoutoreleasedReturnValue函数。此函数要检测刚才提到的标志位,若已经置位,则不执行retain操作,设置并检测标志位,要比调用autorelease和retain更快。

6、KVC

解释:键 – 值编码(Key-ValueCoding),它可以直接通过key值对对象的属性进行存取操作,而不需通过调用明确的存取方法。这样就可以在运行时动态在访问和修改对象的属性,而不是在编译时确定

访问方法:

valueForKey:

valueForKeyPath:

设置方法:

setValue:forKey:

setValue: forKeyPath:

当根据KVC搜索规则,没有搜索到对应的key或者keyPath,则会调用对应的异常方法。异常方法的默认实现,在异常发生时会抛出一个NSUndefinedKeyException的异常,并且应用程序Crash

我们可以重写下面两个方法,根据业务需求合理的处理KVC导致的异常:

在取值时,未有对应的key:

- (nullable id)valueForUndefinedKey:(NSString*)key;

在赋值时,未有对应的key:

- (void)setValue:(nullable id)value forUndefinedKey:(NSString*)key;

KVC底层实现原理:runtime。

KVC 的Getter搜索模式:

先找相应的 getter 方法(get<Key>, <key>, is<Key>, 或者 _<key>),找到了则返回(对象类型直接返回,其它类型进行转换);

没有找到,则寻找 NSArray 相应的方法;

没有找到,则寻找 NSSet 相应的方法;

如果还没有,且 accessInstanceVariablesDirectly 类属性返回的是 YES,则去搜索实例变量(_<key>、_is<Key>、<key>、is<Key>)。如果发现了,则返回;

还没有,则转到 valueForUndefinedKey: 方法并抛出异常。

KVC的setter搜索模式:

先找 setter 方法(set<Key>:或_set<Key>);

没找到,如果则 accessInstanceVariablesDirectly 类属性返回的是 YES,则去查找实例变量(_<key>、_is<Key>、<key>、is<Key>),若找到,则赋值;

没有找到 setter 方法和实例变量,则转到 setValue:forUndefinedKey: 方法,并抛出异常。

应用:

1、字典转模型

2、对象归档,解档

3、动态地取值和设值

4、用KVC来访问和修改私有变量

5、修改一些控件的内部属性

比如UITabBarController的UITabbar不能满足我们的情况的时候,我们可以创建一个继承于UITabbar的类A,重写layoutSubviews对其进行样式调整,最后,[self setValue:myTabBar forKey:@"tabBar"];对其进行修改。

6. xib/storyboard设置圆角的地方

7、KVO

解释:键值观察Key-Value-Observe,提供了一个对象监听另外一个对象属性的变化的机制。

底层实现原理:

1.KVO是基于runtime机制实现的

2.当某个类的属性对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的setter 方法。派生类在被重写的setter方法内实现真正的通知机制

3.如果原类为Person,那么生成的派生类名为NSKVONotifying_Person

4.每个类对象中都有一个isa指针指向当前类,当一个类对象的第一次被观察,那么系统会偷偷将isa指针指向动态生成的派生类,从而在给被监控属性赋值时执行的是派生类的setter方法

5.键值观察通知依赖于NSObject 的两个方法: willChangeValueForKey: 和 didChangevlueForKey:;在一个被观察属性发生改变之前, willChangeValueForKey:一定会被调用,这就 会记录旧的值。而当改变发生后,didChangeValueForKey:会被调用,继而 observeValueForKey:ofObject:change:context: 也会被调用。

应用:

主要用于UI和数据的绑定方面。

猜你喜欢

转载自blog.csdn.net/weixin_34090643/article/details/87174012