守望寒冬,多喝烫水。

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

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

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

查看所有标签

猜你喜欢:

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

Getting Real

Getting Real

Jason Fried、Heinemeier David Hansson、Matthew Linderman / 37signals / 2009-11-18 / USD 24.99

Getting Real details the business, design, programming, and marketing principles of 37signals. The book is packed with keep-it-simple insights, contrarian points of view, and unconventional approaches......一起来看看 《Getting Real》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具