守望寒冬,多喝烫水。

栏目: IOS · 发布时间: 5年前

  • 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 顺序查找

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
    • initialize
      • 初始化父类
      • 在初始化子类(可能最终调用的是父类的initialize方法)

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变量的问题
  • 包装秤对象之后就可以通过指针修改 外部的变量了

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Linux内核完全剖析

Linux内核完全剖析

赵炯 / 机械工业出版社 / 2006-1 / 79.00元

本书对早期Linux操作系统内核全部代友文件进行了详细的剖析,旨在让读者在尽量短的时间内对Linux的工作机理获得全面而深刻的理解,为进一步学习和研究Linux系统打下坚实的基础。虽然选择的版本较低,但该内核已能够正常编译运行,并且其中已包括了Linux工作原理的精髓。书中首先以Linux源代码版本的变迁为主线,简要介绍了Lin-ux系统的发展历史,同时着重说明了各个内核版本之间的主要区别和改进方......一起来看看 《Linux内核完全剖析》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具