美文网首页Objective-C
2021 - OC(一)

2021 - OC(一)

作者: 世玉茹花 | 来源:发表于2021-05-25 14:59 被阅读0次

    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<count; i++) {
                objc_property_t property = properties[i];
                NSString *propertyName = [[NSString alloc] initWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
                NSLog(@"属性名==%@",propertyName);
            }
            if (properties){
                //要释放
               free(properties);
            }
        //得到父类的信息
        cls = class_getSuperclass(cls);
        }
    }
    

    相关文章

      网友评论

        本文标题:2021 - OC(一)

        本文链接:https://www.haomeiwen.com/subject/ljeqsltx.html