1、ios一个对象占用多少字节?
ios系统分配了16byte(16字节)内存空间,通过malloc_size可以获取到。
通过函数class_getInstanceSize获取实例对象占用内存大小为8byte。因为系统分配内存空间规律是16的倍。
2、如何计算图片加载内存中占用大小?
图片内存大小的计算公式 宽度 * 高度 * bytesPerPixel/8。
bytesPerPixel : 每个像素所占的字节数。
内存计算:像素高*像素宽*4(4是每个像素占用字节数)
但是通常情况下颜色还有alpha通道也是8位 也就是传说中的RGBA,总共32位。
取内存:用NSHashtable,弱引用持有对象。将图片的对象放到这个弱引用的hash表中,可以实时查看当前仍存活的所有图片对象,并据此计算图片占用的内存
3、VC生命周期
initialize:类初始化方法
init:实例初始化方法
inintWithCoder:从归档初始化,如果使用storyboard或xib
loadview:加载view
viewdidload:view加载完毕
viewwillLaoutSubviews:控制器的view将要布局子控件
viewdidlayoutSubviews:控制器view布局子控件完成
viewwillappear:控制器的view将要显示
viewdidappear:控制器view完全显示
viewwilldisappear:控制器的view即将消失
viewdiddisappear:控制器的view完全消失
viewdidunload
dealloc
4、多个网络请求完毕执行操作?
gcd三种方式
1.dispatch_group_t控制
2.信号量dispatch_semaphore_t控制
3.栅栏块dispatch_barrier_async控制
注意:所有任务必须在同一个队列;
队列不能使用全局队列,需要自己创建队列。
5、为什么NSArray要用copy?NSMutableArray要用strong修饰?
1.如果NSArray用strong修饰,由于是强引用,副本对象数组和源对象数组只是指向同一块内存区域,这样会造成副本对象会随着源对象数组改变而改变。
2.如果NSMutableArray用copy修饰,可变数组会变成不可变数组。用copy关键字,调用setter方法后, 进行了深拷贝,并且拷贝的对象是copy的,所以copy修饰NSMutableArray被视为NSArray了,再调用可变数组方法就会崩溃。
6、方法延迟执行多种方法?
1.performSelector【非阻塞】
[self performSelector:@selector(delayMethod) withObject:nil/*可传任意类型参数*/ afterDelay:2.0];
2.NSTimer【非阻塞】
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(delayMethod) userInfo:nil repeats:NO];
3.NSThread线程的sleep【阻塞】
[NSThread sleepForTimeInterval:2.0];
4.GCD【非阻塞】
__block ViewController *weakSelf = self;
dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC));
dispatch_after(delayTime, dispatch_get_main_queue(), ^{
[weakSelf delayMethod];
});
5、面向过程与面向对象区别
面向过程:是一种以过程为中心的编程思想,分析出解决问题的步骤,然后用函数把这些步骤一一实现。面向过程数据和对数据操作是分离的。
(1)优点:性能比面向对象高,因为类调用需要实例化,开销大,比较耗资源。
(2)缺点:没有面向对象易维护,易复用,易扩展。
面向对象:将事务对象化,通过对象通信来解决问题。面向对象编程中数据和对数据操作在一起的。
(1)优点:由于面向对象封装、继承、多态特性,可以设计低耦合,系统更灵活,更易于维护。
(2)缺点:性能比面向过程低。
6、封装、继承、多态
封装:即隐藏对象的属性和实现方法,仅对外提供公共的访问方法。
继承:子类继承父类非私有的属性和方法,子类可以添加自己的属性和方法,子类可以重新定义父类的方法。
多态:
1.没有继承就没有多态
2.代码体现:父类类型指针指向子类对象
3.好处:如果函数方法参数使用的是父类类型,则可以传入父类和子类对象,而不用去调用多个来对类进行匹配了。
4.局限性:父类类型变量不能直接调用子类持有的方法,如果必须调用,须强制转换为子类后再调用。
【定义为父类类型,实际调用为子类类型,通过调用子类类型调用子类的方法】
【打印机- 彩色打印机 -黑白打印机例子】
7、面向对象六个基本原则
1.单一职责原则:一个类功能要单一,一个类只负责一个职责。
2.开放封闭原则:对外扩展开放,对修改关闭。
3.里氏替换原则:任何使用基类的地方都能使用子类进行替换,替换子类后,系统能正常工作。
4.接口隔离原则:接口颗粒度小化,建立单一接口,接口要小而专,不能大而全。
5.依赖倒置原则:我们的类要依赖于抽象,而依赖于具体,通过抽象使各个类或模块的实现彼此独立,不想糊影响,实现模块间松耦合。
6.迪米特原则:一个对象尽可能少的去了解其他对象,消除耦合。
8、dispatch_once怎么实现线程安全?
此函数通采用“原子访问”来查询标记,以判断其所对应的代码原来是否已经执行过。
+ (instancetype)sharedInstance
{
/*定义相应类实例的静态变量;
意义:函数内定义静态变量,无论该函数被调用多少次,
在内存中只初始化一次,并且能保存最后一次赋的值
*/
static ClassName *instance = nil;
/*定义一个dispatch_once_t(其实也就是整型)静态变量,
意义:作为标识下面dispatch_once的block是否已执行过。
static修饰会默认将其初始化为0,当值为0时才会执行block。
当block执行完成,底层会将onceToken设置为1,这也就是为什
么要传onceToken的地址(static修饰的变量可以通过地址修改
onceToken的值),同时底层会加锁来保证这个方法是线程安全的
*/
static dispatch_once_t onceToken;
/*只要当onceToken == 0时才会执行block,否则直接返回静态变量instance*/
dispatch_once(&onceToken, ^{
instance = [[ClassName alloc] init];
//...
});
return instance;
}
9、从输入url到浏览器显示发生了什么?
1. 在浏览器地址栏输入URL
2. 浏览器查看缓存,如果请求资源在缓存中并且新鲜,跳转到转码步骤
1. 如果资源未缓存,发起新请求
2. 如果已缓存,检验是否足够新鲜,足够新鲜直接提供给客户端,否则与服务器进行验证。
3. 检验新鲜通常有两个HTTP头进行控制`Expires`和`Cache-Control`:
* HTTP1.0提供Expires,值为一个绝对时间表示缓存新鲜日期
* HTTP1.1增加了Cache-Control: max-age=,值为以秒为单位的最大新鲜时间
3. 浏览器解析URL获取协议,主机,端口,path
4. 浏览器组装一个HTTP(GET)请求报文
5. 浏览器获取主机ip地址,过程如下:
1. 浏览器缓存
2. 本机缓存
3. hosts文件
4. 路由器缓存
5. ISP DNS缓存
6. DNS递归查询(可能存在负载均衡导致每次IP不一样)
6. 打开一个socket与目标IP地址,端口建立TCP链接,三次握手如下:
1. 客户端发送一个TCP的SYN=1,Seq=X的包到服务器端口
2. 服务器发回SYN=1, ACK=X+1, Seq=Y的响应包
3. 客户端发送ACK=Y+1, Seq=Z
7. TCP链接建立后发送HTTP请求
8. 服务器接受请求并解析,将请求转发到服务程序,如虚拟主机使用HTTP Host头部判断请求的服务程序
9. 服务器检查HTTP请求头是否包含缓存验证信息如果验证缓存新鲜,返回304等对应状态码
10. 处理程序读取完整请求并准备HTTP响应,可能需要查询数据库等操作
11. 服务器将响应报文通过TCP连接发送回浏览器
12. 浏览器接收HTTP响应,然后根据情况选择关闭TCP连接或者保留重用,关闭TCP连接的四次握手如下:
1. 主动方发送Fin=1, Ack=Z, Seq= X报文
2. 被动方发送ACK=X+1, Seq=Z报文
3. 被动方发送Fin=1, ACK=X, Seq=Y报文
4. 主动方发送ACK=Y, Seq=X报文
13. 浏览器检查响应状态吗:是否为1XX,3XX, 4XX, 5XX,这些情况处理与2XX不同
14. 如果资源可缓存,进行缓存
15. 对响应进行解码(例如gzip压缩)
16. 根据资源类型决定如何处理(假设资源为HTML文档)
17. 解析HTML文档,构件DOM树,下载资源,构造CSSOM树,执行js脚本,这些操作没有严格的先后顺序,以下分别解释
18. 构建DOM树:
1. Tokenizing:根据HTML规范将字符流解析为标记
2. Lexing:词法分析将标记转换为对象并定义属性和规则
3. DOM construction:根据HTML标记关系将对象组成DOM树
19. 解析过程中遇到图片、样式表、js文件,启动下载
20. 构建CSSOM树:
1. Tokenizing:字符流转换为标记流
2. Node:根据标记创建节点
3. CSSOM:节点创建CSSOM树
21. [根据DOM树和CSSOM树构建渲染树]
22. js解析如下:
23. 显示页面(HTML解析过程中会逐步显示页面)
10、KVO监听可变数组count变化?
继承NSObject创建一个model类,model类添加一个NSMutaleArray数组的属性modelArr,进行初始化。
VC里定义一个model属性,添加model的KVO监听,监听modelArr属性值变化,并重写监听方法,通过keypath判断modelArr的变化。
11、load和initialize
(https://www.jianshu.com/p/c52d0b6ee5e9)
12、Block几种类型?
1.全局block:globalBlock,不使用外部变量的block,存储在已初始化数据区
- (void)block0 {
// 声明: typedef NSString *(^MyBlock)(NSString *str);
MyBlock block = ^(NSString *str){
NSLog(@"处理一些无关于外部属性的事务");
return str;
};
block(@"");
NSLog(@"block类型%@",block);
}
2.栈block:StackBlock,使用外部变量并且未进行copy操作的block。保存在栈中的block,没有用copy去修饰并且访问了外部变量,你的block类型就是这种类型,会在函数调用结束被销毁 (需要在MRC)
MRC环境下:访问外界变量的block默认存储在栈区。
ARC环境下:访问外界变量的block默认存放在堆中,实际上是先放在栈区,在ARC情况下自动又拷贝到堆区,自动释放。
通过官方文档可以看出,复制到堆区的主要目的就是保存block的状态,延长其生命周期。因为block如果在栈上的话,其所属的变量作用域结束,该block就被释放掉,block中的__block变量也同时被释放掉。为了解决栈块在其变量作用域结束之后被释放掉的问题,我们就需要把block复制到堆中。
- (void)block1 {
int a = 1;
NSLog(@"block111类型%@",^{ NSLog(@"block 0:%i", a); });
}
3、堆block,NSMallocBlock, 保存在堆中的block 此类型blcok是用copy修饰出来的block 它会随着对象的销毁而销毁,只要对象不销毁,我们就可以调用的到在堆中的block。
int a = 5;
self.block1 = ^(NSString *str, UIColor *color){
NSLog(@"%d",a);
};
NSLog(@"block1=%@",self.block1);
13-1、block为什么用copy修饰?
首先,block是一个将函数及其上下文封装起来的一个对象。理论是可以retain,release的,但是创建block默认是在栈上的,而不是在堆上,所以他的作用域仅限创建时候的当前上下文,出了作用域调用该block,程序会崩溃。
1.一般情况不需要自行copy或者retain 一个block,只有当你需要在block定义域以外地方调用时才需要copy。
2.其实copy和strong都一样,因为block的retain就是用copy来实现的。
13-2、block截获变量
局部变量:截获其值
局部静态变量:截获其指针
全局变量:不截获
全局静态变量:不截获
外部变量:截获其指针及其修饰符
常量存储在常量区,全局变量/静态变量存储在全局区/静态区。
变量:1.1 基本数据类型/非oc对象一般存储在栈区,
1.2 oc对象一般是存储在堆区
oc对象有指针概念,指针变量存储在栈区,而指针存放的是对象在堆区的地址。所以我们是通过栈区的指针变量来索引堆中地址,从而访问对象的。
Block不允许修改外部变量的值,这里所说的外部变量的值,指的是栈中指针的地址。要想在Block内修改“外部变量”的值,必须被__block修饰。
auto类型的局部变量,可以被block捕获,但是不能修改值
__block可以解决block内部无法修改外部auto变量的问题,
__block修饰的变量,编译器会把它包装成一个对象,然后我们的这个成员变量放到了这个对象的内部
14、深浅拷贝?
copy和mutableCopy是NSObject的方法,很多系统容器已经实现了这个方法,如果自定义对象想使用这两个方法,必须实现NSCopying协议和NSMutableCopying协议。
指针拷贝
内容拷贝
例子:
1. [NSArray copy] ->不可变数组,浅拷贝
2. [NSArray mutableCopy] -> 可变数组,深拷贝
3. [NSMutableArray copy] -> 不可变数组,深拷贝
4. [NSMutableArray mutableCopy] -> 可变数组,深拷贝
15、当点击屏幕上的一个按钮,这个过程具体发生了什么?
UIResponder:{
UIApplication
UIView -> UIControl -> UIButton,UISlider
UIViewController
}
https://www.cnblogs.com/machao/p/5471094.html
1、用户触摸屏幕,系统硬件进程会获取到这个事件,将事件简单封装后存入系统中,硬件检测进程会将事件放到APP检测的那个端口。
2、APP启动主线程runloop会注册一个端口事件,来检测事件的发生。当事件到达会唤起当前APP主线程的runloop。
3、最后系统会判断该次触摸是否导致了一个新的事件,如果是第一个手指开始触碰,系统会从响应网中寻找响应链。
响应者链条:很多响应者对象(继承自UIResponder的对象)一起组合起来的链条称之为响应者链条。
16、内存溢出 && 内存泄露
内存溢出:指程序申请内存时,没有足够的空间供其使用;
内存泄露:指程序在申请内存后,无法释放已申请的内存空间,
1.排查方法:
1.静态分析方法(Analyze)
2.动态分析(Instrument 工具库leaks)
2.原因分析:ARC根本原因还是存在循环引用,导致一些内存无法释放。
1.VC存在timer,合适时机invalidate
2.VC中的delegate,代理尽量使用weak修饰
3.VC中使用block,在block外部对弱化self,再在block内部强化已经弱化的weakSelf
17、野指针:指针指向的对象已经被回收掉了,这个指针叫做野指针。
18、线程安全?加锁
自旋锁会忙等: 所谓忙等,即在访问被锁资源时,调用者线程不会休眠,而是不停循环在那里,直到被锁资源释放锁。
互斥锁会休眠: 所谓休眠,即在访问被锁资源时,调用者线程会休眠,此时cpu可以调度其他线程工作。直到被锁资源释放锁。此时会唤醒休眠线程
递归锁:递归锁可以被同一线程多次请求,而不会引起死锁。这主要是用在循环或递归操作中。
19、HTTP总结
20、MVC,MVP,MVVM
https://www.jianshu.com/p/32078fcd704d
21、[[NSMutableArray alloc]init]和[NSMutableArray array]的区别
[[NSMutableArray alloc]init] alloc分配内存,init初始化,需要手动释放
[NSMutableArray array] 不需要手动release,遵循autoreleasepool机制
在ARC(自动引用计数)中两种方式并没什么区别
22、说一下dispatch_group_t和dispatch_barrier_sync的区别吗
dispatch_group_t:相关函数:dispatch_group_t创建组,dispatch_group_async异步处理,dispatch_group_notify监视这些处理结果。
一旦检测到所有处理执行结束,将结束的处理追加到dispatch queue中,这就是使用dispatch_group的原因。
===============================================
dispatch_barrier_sync && dispatch_barrier_async
将队列一分为二,前面任务执行完毕才能执行 dispatch_barrier_sync的任务(主线程),然后才执行队列后面任务。 dispatch_barrier_async在子线程中执行,不会阻塞当前线程,将自己任务插入队列之后,不会等待自己任务结束,会继续吧后面任务插入队列,然后等待自己任务结束,才执行后面任务。
23、UI绘制原理&系统/异步绘制流程
##UIView绘制原理:
当我们调用uiview的setNeedDisplay方法以后,实际并没有立刻发生当前视图的绘制工作。
1.当调用UIView的setNeedDisplay后
2.系统会立刻调用view的layer的同名方法,之后相当于在layer上打了一个脏标记
3.当runloop将要结束的时候,才会调用CALayer的display函数方法,然后进入当前视图的真正绘制流程中,
4.CALayer的display方法,在内部会判断layer的delegate是否响应displayLayer这个方法
5.若不响应,则系统开始绘制流程
6.若响应,则开始异步绘制
##系统绘制流程:
1.首先CALayer内部会创建一个CGContentextRef,在drawRect方法中,可以通过上下文堆栈中的取出这个context,拿到的就是当前视图的上下文。
2.然后layr判断是否有代理,若没有,则调用CALayer的drawInContext
3.若有代理调用代理方法,然后做当前视图的绘制工作,在在合适的时机基于drawRect回调方法
4.drawRect 默认操作是什么都不做,我们可以做一些自定义绘制工作
5.最后再由CALayer上传对应的backing store给GPU,这里的backing store理解为位图。
##异步绘制:
【基于layer的delegate,如果实现了displaylayer方法,就可以进入异步绘制流程中】
1.通过子线程的切换,在子线程中做位图绘制,此时主线程可以做些其他工作
2.再回到主队列中,提交这个位图,设置给CALayer的contents属性。
子线程绘制:
1.通过CGBitmapCOntextCreat方法,创建位图上下文
2.通过coreGraphic相关API,做一些UI控件的一些绘制工作
3.之后通过CGBitmapContextImage方法,根据所绘制的上下文,生成一张CGImag图片。
24、(iOS中的SEL和IMP)[https://www.jianshu.com/p/4a09d5ebdc2c]
SEL:类成员方法的指针, SEL只是方法编号
IMP:一个函数指针,保存了方法的地址
关系:
每一个继承于NSObject的类都能自动获得runtime的支持,在这样一个类中,又一个isa指针,指向该类定义的数据结构体,这个结构体是由编译器编译时为类创建的。这个结构体包含了指向父类的指针和dispatch table,这是一张SEL和IMP的对应表。
方法编号SEL通过dispatch table表寻找对应的IMP,IMP时一个函数指针,然后执行方法。
25、
dispatch_queue_t quete = dispatch_queue_create("com.taikang.com", DISPATCH_QUEUE_SERIAL);
dispatch_async(quete, ^{
NSLog(@"1------%@", [NSThread currentThread]);
});
dispatch_async(quete, ^{
NSLog(@"2------%@", [NSThread currentThread]);
});
dispatch_sync(quete, ^{
NSLog(@"3------%@", [NSThread currentThread]);
});
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"4------%@", [NSThread currentThread]);
});
NSLog(@"5------%@", [NSThread currentThread]);
输出:1,,2,,3,,5,,,4
26、什么是method swizzling【黑魔法】
简单说就是方法交换
在Objective-C中调用一个方法,其实是向一个对象发送消息,查找消息的唯一依据是selector的名字。利用Objective-C的动态特性,可以实现在运行时偷换selector对应的方法实现,达到给方法挂钩的目的。
##应用场景:
1.防止数组取值越界crash
2.改变app中所有按钮的大小[替换setframe方法]
3.处理按钮重复点击[替换sendAction:to:forEvent:]
4.每个vc增加标记,[替换viewdidload]
27、SDWebImageView 原理[https://www.jianshu.com/p/ff9095de1753?utm_source=desktop&utm_medium=timeline]
[http://www.jishudog.com/5171/html]
1.入口 setImageWithURL:placeholderImage:options:会先把 placeholderImage显示,然后 SDWebImageManager根据 URL 开始处理图片。
2.进入SDWebImageManager 类中downloadWithURL:delegate:options:userInfo:,交给
SDImageCache从缓存查找图片是否已经下载
queryDiskCacheForKey:delegate:userInfo:.
3.先从内存图片缓存查找是否有图片,如果内存中已经有图片缓存,SDImageCacheDelegate回调 imageCache:didFindImage:forKey:userInfo:到
SDWebImageManager。
4.SDWebImageManagerDelegate 回调
webImageManager:didFinishWithImage: 到 UIImageView+WebCache,等前端展示图片。
5.如果内存缓存中没有,生成 `NSOperation `
添加到队列,开始从硬盘查找图片是否已经缓存。
6.根据 URL的MD5值Key在硬盘缓存目录下尝试读取图片文件。这一步是在 NSOperation 进行的操作,所以回主线程进行结果回调 notifyDelegate:。
7.如果上一操作从硬盘读取到了图片,将图片添加到内存缓存中(如果空闲内存过小, 会先清空内存缓存)。SDImageCacheDelegate'回调 imageCache:didFindImage:forKey:userInfo:`。进而回调展示图片。
8.如果从硬盘缓存目录读取不到图片,说明所有缓存都不存在该图片,需要下载图片, 回调 imageCache:didNotFindImageForKey:userInfo:。
9.共享或重新生成一个下载器 SDWebImageDownloader开始下载图片。
10.图片下载由 NSURLConnection来做,实现相关 delegate
来判断图片下载中、下载完成和下载失败。
11.connection:didReceiveData: 中利用 ImageIO做了按图片下载进度加载效果。
12.connectionDidFinishLoading: 数据下载完成后交给 SDWebImageDecoder做图片解码处理。
13.图片解码处理在一个 NSOperationQueue完成,不会拖慢主线程 UI.如果有需要 对下载的图片进行二次处理,最好也在这里完成,效率会好很多。
14.在主线程 notifyDelegateOnMainThreadWithInfo:
宣告解码完成 imageDecoder:didFinishDecodingImage:userInfo: 回调给 SDWebImageDownloader`。
15.imageDownloader:didFinishWithImage:回调给 SDWebImageManager告知图片 下载完成。
-16. 通知所有的 downloadDelegates下载完成,回调给需要的地方展示图片。
17.将图片保存到 SDImageCache中,内存缓存和硬盘缓存同时保存。写文件到硬盘 也在以单独 NSOperation 完成,避免拖慢主线程。
18.SDImageCache 在初始化的时候会注册一些消息通知,
在内存警告或退到后台的时 候清理内存图片缓存,应用结束的时候清理过期图片。
28、同一个url,图片更新时如何操作?
[SDWebImageRefreshCached :将硬盘缓存交给系统自带的NSURLCache去处理,当同一个URL对应的图片经常更改时可以用这种策略]
[https://www.jianshu.com/p/d559cb3ca1b3?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation]
29、通过view获取view所在的viewcontroller
响应链:
事件有触摸事件,滑动事件,远程控制事件。当屏幕上发生了触摸事件,最先响应的是最外层view,然后依次传递给他的父view,然后再到viewcontroller,再到application。通过这个思路,查找所在vc
-(UIViewController *)findViewController:(UIView *)currentView{
for (UIView *next = currentView.superview;next;next = next.superview) {
UIResponder *responder = [next nextResponder];
if([responder isKindOfClass:[UIViewController class]]){
return (UIViewController *)responder;
}
}
return nil;
}
30、如何解决哈希冲突?哈希表在ios中的应用?
哈希表(Hash table,也叫散列表),是根据关键码值而直接进行访问的数据结构,是一块连续的存储空间。
哈希函数常用设计:(1)直接定址法,哈希函数为线性函数;(2)平方取中法,平方后取中间几位;(3)折叠法,拆分进行加法计算得到数值;(4)随机数法。
哈希冲突:对于不同的关键字,经过哈希函数计算以后的哈希值相同。
解决办法:
1.开放定址法:一旦发生冲突就寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到。
2.再哈希法:有多个不同的哈希函数,当发生冲突时,使用第二个,第三个。。。等哈希函数计算地址,直到无冲突,虽然不易发生聚集,但是增加了计算时间。
3.链地址法:每个哈希表节点都有一个next指针,多哈希表节点可以用next指针构成一个单向链表,被分配到同一个索引上的多个节点可以用单向链表连起来。
4.建立公共溢出区:将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表。
哈希表在iOS应用:[https://www.jianshu.com/p/48709f466db9]
31、成员变量和属性区别?@dynamic与@synthesize的区别?
成员变量:
1.成员变量默认修饰@protected
2.成员变量不会自动生成set和get方法,需要自己手动实现
3.成员变量不能用.语法调用,因为没有set,get方法,只能用->调用
属性:
1.属性默认@protected
2.属性会自动生成get,set方法
3.属性用.语法调用,点语法实际上调用的是set和get方法。
@synthesize:
为属性添加一个实例变量名,或者别名,同时为该属性生成setter/getter方法。
@dynamic:
告诉编译器,属性的setter和getter方法由用户自己实现,不自动生成。
set,get方法重写:
-(NSString *)caption{
return _caption;
}
-(void)setCaption:(NSString *)caption{
_caption = caption;
}
直接这样写会报错,需要在@implementation实现中添加:
@synthesize caption=_caption; 即可。
32、KVO运行时创建的分类与自定义创建分类重名?
例如:创建了一个NSNotification_A类。
编译通过,因为KVO是运行时创建的,并不在编译时刻,但是此时KVO起不了作用。
可以手动添加set方法,添加willChangeValueForKey,didChangeValueForKey。
33、property的作用是什么,有哪些关键词,分别是什么含义?
@property 是声明属性语法的,可以快速为实例变量创建存取器,并允许我们通过点语法使用存取器。
@property是一个属性访问声明,主要属性分三类,原子性,存取器控制,内存管理;
原子性:automic,,nonautomic
读取属性:readwrite,,readonly
内存管理:assign,,retain,,copy,,
34、父类的property是如何查找的
根据子类获取父类所有属性:
//获取父类所有属性
+(void)showStudentClassProperties
{
Class cls = [model4 class];
unsigned int count;
while (cls!=[NSObject class]) {
objc_property_t *properties = class_copyPropertyList(cls, &count);
for (int i = 0; i