2021 - OC(一)

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

你可能感兴趣的:(2021 - OC(一))