-
NSObject对象创建实例对象的时候系统分配了16个内存(通过malloc_size函数可获得)
-
但是 NSObject只使用了8个字节 使用(class_getinstanceSize可获得)
3. 对象的isa指针指向哪里?
- instance对象的isa指针指向class对象
- class对象的isa指针指向 meta-class对象
- meta-class对象的isa指针基类的meta-class对象
4. OC类的信息存储在哪里?
- meta-class存储:类方法
- class对象存储: 对象方法,属性,成员变量,协议信息
- instance存储: 成员变量具体的值
5. 说说你对函数调用的理解吧。
- 函数调用 实际实际上就是 给对象发送一条消息
- objc_messageSend(对象, @selectir(对象方法))
- 寻找顺序(对象方法) instance的isa指针找到类对象 在类对象中寻找方法,若没有向superClass中查找。
- 寻找顺序(类方法) instance的isa指针找到类对象 --> 类对象的isa找到meta-calss --> 在meta-class对象中寻找类方法,若没有向superClass中查找。
KVO
1. iOS用什么方式实现对一个对象的KVO?(KVO的本质是什么?)
- 利用Runtime API 动态生成了一个新的类, 并且instance对象的isa指针指向这个生成的新子类。
-
当修改instance对象的属性时,会调用Foundation的_NSSetXXXValueForKey函数
- willChangeValueForKey
- 父类原来的set方法
- didChangeValueForKey
- didChangeValueForKey 内部会触发observerValueForKeyPath方法 实现监听。
KVC
1. 使用KVC会不会调用KVO?
-
会调用KVO, 因为他的内部使用了:
- willChangeValueForKey
- 直接去_方式去更改
- didChangeValueForKey
- didChangeValueForKey 内部会触发
2. KVC的赋值和取值过程是怎么样的?原理是什么?
-
赋值
-
setKey、_setKey 顺序查找方法
- 有: 传递参数调用防范
- 没有: 查看accessInstanceVariablesDirectly 方法返回值 yes 继续查找
- _key、_iskey、key、iskey 顺序查找
-
setKey、_setKey 顺序查找方法
Category:
1. Category的使用场合是什么?
2. Category中的属性是否也存在类对象中?如果存在是怎么生成和存在的?如果不存在,它存在的位置在哪里?
- 一个类永远只有一个类对象
- 在运行起来之后 最重都会合并在 类对象中去。
3. Category的使用原理是什么?实现过程
-
Category在编译的时候会将 方法、属性、协议的数据合并到一个大数组中,
- 后参加编译一的数据会放在数组的前面
- 在运行时的时候,runtime会将Category的数据合并到类的信息中(类对象、元类对象中)
- 分类的调用顺序
4. Category和Extension的区别是什么?
- Category 在运行的时候才将数据合并到类信息中
- Extension 在编译的时候就将数据包含在类信息中了 @interface Xxxx() 也叫做匿名分类
5. Category中有load方法吗?load是什么时候调用的?load方法能继承码?
- 有load方法
- load方法在runtime加载类、分类的时候调用
- load方法可以继承,但是一般情况下不会主动去调用load方法,都是让系统自动调用
6. load、initialize方法的区别是什么?它们在Category中的调用顺序?以及出现继承时他们之间的调用过程?
-
调用方式:
- load 根据函数地址直接调用
- initialize 是通过 objc_msgSend调用
-
调用时刻:
- load是runtime加载类、分类的时候调用(只会调用1次)
- initialize 是类第一次接收到消息的时候调用、每一个类只会调用1次(但是父类的initialize方法可能会调用多次) 有些子类没有initialize方法所以调用父类的。
-
调用顺序:
-
load:
-
先调用类的load
- 先编译的类,优先调用load
- 调用子类的load之前会先调用父类的load
-
在调用分类的load
- 先编译的分类,优先调用load
-
先调用类的load
-
initialize
- 初始化父类
- 在初始化子类(可能最终调用的是父类的initialize方法)
-
load:
7. Category能否添加成员变量?如果可以,如何给Category添加成员变量?
-
不能直接给Category添加成员变量,但是可以间接添加。
- 使用一个全局的字典 (缺点: 每一个属相都需要一套相同的代码)
///> DLPerson+Test.h @interface DLPerson (Test) ///> 如果直接使用 @property 只会生成方法的声名 不会生成成员变量和set、get方法的实现。 @property (nonatomic, assign) int weigjt; @end ///> DLPerson+Test.m #import "DLPerson+Test.h" @implemention DLPerson (Test) NSMutableDictionary weights_; + (void)load{ weights_ = [NSMutableDictionary alloc]init]; } - (void)setWeight:(int)weight{ NSString *key = [NSString stringWithFormat:@"%p",self]; weights_[key] = @(weight); } - (int)weight{ NSString *key = [NSString stringWithFormat:@"%p",self]; return [weights_[key] intValue] } @end 复制代码
-
使用runtime机制给分类添加属性
#import<objc/runtime.h> const void *DLNameKey = &DLNameKey ///> 添加关联对象 void objc_setAssociatedObject( id object, ///> 给哪一个对象添加关联对象 const void * key, ///> 指针(赋值取值的key) &DLNameKey id value, ///> 关联的值 objc_AssociationPolicy policy ///> 关联策略 下方表格 ) eg : objc_setAssociatedObject(self,@selector(name),name,OBJC_ASSOCIATION_COPY_NONATOMIC); ///> 获得关联对象 id objc_getAssociatedObject( id object, ///> 哪一个对象的关联对象 const void * key ///> 指针(赋值取值的key) ) eg: objc_getAssociatedObject(self,@selector(name)); /// _cmd == @selector(name); objc_getAssociatedObject(self,_cmd); ///> 移除所有的关联对象 void objc_removeAssociatedObjects( id object ///> ) 复制代码
- objc_AssociationPolicy(关联策略)
objc_AssociationPolicy(关联策略) 对应的修饰符 OBJC_ASSOCIATION_ASSIGN assign OBJC_ASSOCIATION_RETAIN_NONATOMIC strong, nonatomic OBJC_ASSOCIATION_COPY_NONATOMIC copy, nonatomic OBJC_ASSOCIATION_RETAIN strong, atomic OBJC_ASSOCIATION_COPY copy, atomic
Block
1. block的原理是怎样的?本质是什么
- block的本质就是一个oc对象 内部也有isa指针, 封装了函数及调用环境的OC对象,
2. 看代码解释原因
int main(int argc, const char *argv[]){ @autoreleasepool{ int age = 10; void (^block)(void) = ^{ NSLog(@" age is %d ",age); }; age = 20; block(); } } /* 输出结果为? 为什么? 输出结果是: 10 如果没有修饰符 默认是auto 为了能访问外部的变量 block有一个变量捕获的机制 因为他是局部变量 并且没有用static修饰 所以它被捕获到block中是 一个值,外部再次改变时 block中的age不会改变。 */ 复制代码
变量类型 | 捕获到Block内部 | 访问方式 | |
---|---|---|---|
局部变量 | auto | :white_check_mark: | 值传递 |
局部变量 | static | :white_check_mark: | 指针传递 |
全局变量 | :x: | 直接访问 |
int main(int argc, const char *argv[]){ @autoreleasepool{ int age = 10; static int height = 10; void (^block)(void) = ^{ NSLog(@" age is %d, height is %d",age, height); }; age = 20; height = 20; block(); } } /* 输出结果为? 为什么? age is 10, height is 20 局部变量用static 修饰之后 捕获到block中的是 height的指针, 因此修改通过指针修改变量之后 外部的变量也被修改了 */ 复制代码
int age = 10; static int height = 10; int main(int argc, const char *argv[]){ @autoreleasepool{ void (^block)(void) = ^{ NSLog(@" age is %d, height is %d",age, height); }; age = 20; height = 20; block(); } } /* 输出结果为? 为什么? age is 20, height is 20 因为 age 和 height是全局变量不需要捕获直接就可以修改 全局变量 对应该就可以访问, 局部变量 需要跨函数访问,所以需要捕获 因此修改通过指针修改变量之后 外部的变量也被修改了 */ 复制代码
int main(int argc, const char *argv[]){ @autoreleasepool{ void (^block)(void) = ^{ NSLog(@" self %p",self); }; block(); } } /* self 会不会被捕获? 因为函数默认会有两个参数 void test(DLPerson *self, SEL _cmd) 所以self 也是一个局部变量 访问@property(nonatmic, copy) NSString *name; 因为他是成员变量, 访问的时候 用self.name 或者 self->_name 访问 所以 block在内部会捕获self。 */ 复制代码
3. 既然block是一个OC对象,那么block的对象类型是什么?
-
ios内存分为5发区域
- 堆: 动态分配内存,需要 程序员 申请,也需要程序员自己管理(alloc、malloc等...)
- 栈: 放一些局部变量,临时变量 系统自己管理
- 静态区(全局区): 存放全局的静态对象。(编译时分配,APP结束由系统释放)
- 常量区: 常量。(编译时分配,APP结束由系统释放)
- 代码区: 程序区
-
block有三种类型,最终都继承自NSBlock类型
- superClass NSGlobalBlock : __NSGlobalBlock : NSBlock : NSObject
block类型 环境 存放位置 NSGlobalBlock 没有访问auto变量 静态区 NSStackBlock 访问了auto变量 栈 NSMallocBlock __NSStackBlock__调用了copy 堆 -
__NSStackBlock__调用了copy 代码实例
void (^block)(void); void (^block1)(void); void test2(){ int age = 10; block = ^{ NSLog("age is %d",age); /* 因为 block访问了 auto变量 所以目前block的类型为NSStackBlock类型, 存放的位置在 栈 上 在 main访问是 这个block已经被释放了。 */ }; [block1 = ^{ NSLog("age is %d",age); /* 因为 block访问了 auto变量 并且 进行了copy操作 所以目前block的类型为 NSMallocBlock 类型 存放的位置在 堆 上 在 main访问是 这个block是可以被访问的。 */ } copy]; } int main(int argc, const char *argv[]){ @autoreleasepool{ test2(); block(); /// 输出的值为 很大不是想要的值 block1();/// 输出的值是10 } } 复制代码
-
每一种类型的block调用了copy之后结果如下所示
block的类型 副本源的配置存储域 复制后的区域 NSGlobalBlock 栈 从栈复制到堆 NSStackBlock 程序的数据区域 不做 NSMallocBlock 堆 引用计数器+1
4. 在什么情况下 ARC环境下,编译器会根据情况自动将栈上的block复制到堆上?
-
block作为函数的返回值的时候
-
将block赋值给__strong指针时
-
block作为 Cocoa API中方法名含有usingBlock的方法参数时
NSArray *arr = @[]; /// 遍历数组中包含 usingBlock方法的参数 [arr enumerateObjectUsingBlock:^(id _Nonnullobj, NSUInteger idx, Bool _Nonnull stop){ }] ; 复制代码
-
block作为GCD属性的建议写法
static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ }); disPatch_after(disPatch_time(IDSPATCH_TIME_NOW, (int64_t)(delayInSecounds *NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ }); 复制代码
-
MRC下block属性建议写法
- @property (copy, nonatomic) void (^block)(void);
-
ARM下block属性建议写法
- @property (strong, nonatomic) void (^block)(void);
- @property (copy, nonatomic) void (^block)(void);
5. __weak的作用是什么?有什么使用注意点?
- __weak 是一个修饰符
-
当block内部访问的对象类型的auto变量
- 如果block是在栈上,将不会对auto变量产生强引用
-
如果Block被拷贝到堆上
- 会调用block内部的copy函数
- copy函数会调用源码中的_Block_object_assign函数
- _Block_object_assign函数会根据修饰 auto 变量的修饰符(__strong、__weak 、__unsafe_unretained)来决定作出相应的操作,形成强引用或者弱引用
-
block从对上移除
- 会调用block内部的dispose函数
- dispose函数会调用源码中的 _Block_object_dispose函数
- _Block_object_dispose函数会自动释放auto变量(release)
6. __block的作用是什么?有什么使用注意点?
- __block修饰之后会将变量包装成一个对象 可以解决block内部无法修改auto变量的问题
- 包装秤对象之后就可以通过指针修改 外部的变量了
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。