美文网首页
2020-07-24

2020-07-24

作者: 王家小雷 | 来源:发表于2020-07-24 18:06 被阅读0次

    锁 OSSpinLock 自旋锁 实现机制:忙等 操作重点:原子操作
    1.自旋锁
    2.互斥锁
    3.读写锁
    4.信号量
    5.条件锁
    6.递归锁
    需导入头文件:

    import <libkern/OSAtomic.h>

    pragma mark --加锁

    -(void)addLock{
    __block OSSpinLock oslock = OS_SPINLOCK_INIT;
    //线程1
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSLog(@"准备上锁");
    OSSpinLockLock(&oslock);
    NSLog(@"已经上锁");
    OSSpinLockUnlock(&oslock);
    NSLog(@"解锁成功");
    });
    }
    锁 OSSpinLock 自旋锁 实现机制:忙等 操作重点:原子操作 https://www.jianshu.com/p/c8ed92cc43b6

    iOS哪些操作会造成内存泄漏
    1.Block循环引用(比如mj的下啦刷新需要用weakself)cmp.refreshingBlock = refreshingBlock;这一句,这里的refreshingBlock是属于MJRefreshHeader的强引用属性
    2.delegate循环引用问题
    3.NSTimer循环引用
    4.非OC对象内存处理
    比如常用的滤镜操作调节图片亮度 CGImageRelease(ref);//非OC对象需要手动内存释放
    5.地图类处理
    使用完毕时将地图、代理等滞空为nil,注意地图中标注(大头针)的复用,并且在使用完毕时清空标注数组等。
    6.大次数循环内存暴涨问题
    该循环内产生大量的临时对象,直至当前runloop休眠前才释放掉,可能导致内存泄漏,解决方法为在循环中创建自己的autoReleasePool,及时释放占用内存大的临时变量,减少内存占用峰值。

    通过调用autorelease方法把释放对象的任务交给Autoreleasepool对象,当Autoreleasepool对象执行到[pool drain]方法的时候会对自动释放池中所有的对象执行一次release操作,然后+ (instancetype)student方法中创建的对象引用计数就会被-1了,如果引用计数变为0了,对象自然就释放了
    调用方获取对象以后自己retain持有一下对象,防止使用期间对象被释放了,用完release释放一下
    for (int i = 0; i < 100000; i++) {

        @autoreleasepool {
            
            NSString *string = @"Abc";
            
            string = [string lowercaseString];
            
            string = [string stringByAppendingString:@"xyz"];
            
            NSLog(@"%@", string);
            
        }
        
    }
    

    wk的js交互
    self.methodArray = [[NSArray alloc]initWithObjects:XCJSGoToHousingDetails,XCJSVrviewer,nil];

     WKWebView *wkWebView = [self.curVC.view viewWithTag:WEBVIEW_TAG];
    
    for (int i = 0; i < self.methodArray.count; i++) {
        
        [wkWebView.configuration.userContentController addScriptMessageHandler:[[weakScriptMessageDelegate alloc] initWithDelegate:self.curVC] name:self.methodArray[i]];
    }
    

    //WKScriptMessageHandler协议方法

    • (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
      //code

      NSLog(@"%@",NSStringFromSelector(_cmd));
      [self.wkJSSDK setWKScriptMessage:message];
      }
      cookie都存在哪里 多个wkwebview的cookie怎么同步的
      怎么样保护一个线程安全
      内存管理机制(例如一个列表 一直滑动什么时候会崩溃 给app的最大内存分配是虚拟内存还是实际内存)
      设计模式 单例模式 代理模式 Cocoa模式等等
      其中包括常见的软件设计原则,责任链、适配器、桥接、命令、单例、策略模式等等,不要告诉我你只是看了几本书,面试官会让你结合实际业务场景,现场考察你对设计模式的运用和理解的。

    组件化

    1.runtime https://www.jianshu.com/p/c3a5ffcd856b
    runtime
    1.消息传递
    首先通过对象的isa指针找到对应的class(对象都是结构体里面包含isa指针,成员变量等)
    然后在class的methodlist中找对应的方法
    如果class中找不到,就继续往它的superclass中找
    一旦找到这个函数,就去执行它的实现IMP
    如果找不到先进入+(BOOL)resolveInstanceMethod:(SEL)sel
    方法 可以
    +(BOOL)resolveInstanceMethod:(SEL)sel
    {
    // 以"@@:"作为方法签名类型返回。这里第一字符@代表函数返回类型NSString,第二个字符@代表self的类型id,第三个字符:代表_cmd的类型SEL。

        if (sel == @selector(foo:)) {
            class_addMethod([self class], sel, (IMP)fooMethod, "v@:");
            return YES;
        }
        return [super resolveInstanceMethod:sel];
    

    }
    void fooMethod(id obj,SEL _cmd){
    NSLog(@"fooMethod");
    }
    如果这个方法没有实现替代返回yes继续
    备用接收者
    -(id)forwardingTargetForSelector:(SEL)aSelector
    -(id)forwardingTargetForSelector:(SEL)aSelector
    {
    if (aSelector==@selector(foo)) {
    return [Person new];
    }
    return [super forwardingTargetForSelector:aSelector];
    }
    这个方法返回nil的话
    进入-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
    -(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
    {
    if ([NSStringFromSelector(aSelector) isEqualToString:@"foo"]) {
    return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    }
    return [super methodSignatureForSelector:aSelector];
    }

    -(void)forwardInvocation:(NSInvocation *)anInvocation
    {
    SEL sel = anInvocation.selector;
    // Person *p = [Person new];
    Person *p = [[Person alloc] init];

    if ([p respondsToSelector:sel]) {
        [anInvocation invokeWithTarget:p];
    }
    else{
        [self doesNotRecognizeSelector:sel];
    }
    

    }

    如果再没有实现的话就报错了

    关联对象(Objective-C Associated Objects)给分类增加属性
    分类不能添加属性主要是因为不能实现set和get方法

    import <objc/runtime.h>

    static char kDefaultColorKey;
    //set方法
    -(void)setDefaultColor:(UIColor *)defaultColor
    {
    objc_setAssociatedObject(self, &kDefaultColorKey, defaultColor, OBJC_ASSOCIATION_COPY_NONATOMIC);
    }
    //get方法
    -(UIColor *)defaultColor
    {
    return objc_getAssociatedObject(self, &kDefaultColorKey);
    }

    方法魔法(Method Swizzling)方法添加和替换和KVO实现
    方法添加
    class_addMethod([self class], sel, (IMP)fooMethod, "v@:")
    方法替换
    +(void)load{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
    Class class = [self class];
    SEL oldSel = @selector(viewDidLoad);
    SEL newSel = @selector(viewDidLoadNew);
    Method oldM = class_getInstanceMethod(class, oldSel);
    Method newM = class_getInstanceMethod(class, newSel);
    // 判断newM方法是否存在
    BOOL didAddmethod = class_addMethod(class, oldSel, method_getImplementation(newM), method_getTypeEncoding(newM));
    if (didAddmethod) {
    class_replaceMethod(class, newSel, method_getImplementation(oldM), method_getTypeEncoding(oldM));
    }else{
    method_exchangeImplementations(oldM, newM);
    }
    });
    }
    注意点方法需要在load里面执行 单例只执行一次

    • load 作为 Objective-C 中的一个方法,与其它方法有很大的不同。它只是一个在整个文件被加载到运行时,在 main 函数调用之前被 ObjC 运行时调用的钩子方法。其中关键字有这么几个:

    文件刚加载
    main 函数之前
    钩子方法
    实现NSCoding的自动归档和自动解档
    原理描述:用runtime提供的函数遍历Model自身所有属性,并对属性进行encode和decode操作。
    核心方法:在Model的基类中重写方法:

    import <objc/runtime.h>

    • (id)initWithCoder:(NSCoder *)aDecoder
      {
      if (self = [super init])
      {
      unsigned int outCount;
      Ivar *ivars = class_copyIvarList([self class], &outCount);
      for (int i = 0; i < outCount; i ++)
      {
      Ivar ivar = ivars[i];
      NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
      [self setValue:[aDecoder decodeObjectForKey:key] forKey:key];
      }
      }
      return self;
      }

    • (void)encodeWithCoder:(NSCoder *)aCoder
      {
      unsigned int outCount;
      Ivar *ivars = class_copyIvarList([self class], &outCount);
      for (int i = 0; i < outCount; i ++)
      {
      Ivar ivar = ivars[i];
      NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
      [aCoder encodeObject:[self valueForKey:key] forKey:key];
      }
      }
      // 自动归档/解档
      NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:@"PersonInfo"];
      if (data) {
      // 解档使用
      Person *person = [NSKeyedUnarchiver unarchiveObjectWithData:data];
      NSLog(@"name: %@", person.name);
      } else {
      Person *person = [Person new];
      person.name = @"devZhang";
      person.age = @(35);
      person.company = @"ShengXue";
      person.job = @"iOSDev";
      person.address = @"龙岗坂田国际中心";
      // 归档存储
      data = [NSKeyedArchiver archivedDataWithRootObject:person];
      [[NSUserDefaults standardUserDefaults] setObject:data forKey:@"PersonInfo"];
      }
      实现字典和模型的自动转换(MJExtension)
      原理描述:用runtime提供的函数遍历Model自身所有属性,如果属性在json中有对应的值,则将其赋值。
      核心方法:在NSObject的分类中添加方法

    • (instancetype)initWithDict:(NSDictionary *)dict {

      if (self = [self init]) {
      //(1)获取类的属性及属性对应的类型
      NSMutableArray * keys = [NSMutableArray array];
      NSMutableArray * attributes = [NSMutableArray array];
      /*
      * 例子
      * name = value3 attribute = T@"NSString",C,N,V_value3
      * name = value4 attribute = T^i,N,V_value4
      */
      unsigned int outCount;
      objc_property_t * properties = class_copyPropertyList([self class], &outCount);
      for (int i = 0; i < outCount; i ++) {
      objc_property_t property = properties[i];
      //通过property_getName函数获得属性的名字
      NSString * propertyName = [NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
      [keys addObject:propertyName];
      //通过property_getAttributes函数可以获得属性的名字和@encode编码
      NSString * propertyAttribute = [NSString stringWithCString:property_getAttributes(property) encoding:NSUTF8StringEncoding];
      [attributes addObject:propertyAttribute];
      }
      //立即释放properties指向的内存
      free(properties);

        //(2)根据类型给属性赋值
        for (NSString * key in keys) {
            if ([dict valueForKey:key] == nil) continue;
            [self setValue:[dict valueForKey:key] forKey:key];
        }
      

      }
      return self;

    }

    2.kvc/kvo
    kvo机制
    kvo的作用主要是观察某个对象的属性变化
    kvo的实现原理 通过runtime 在观察某个对象的时候,kvo动态创建了一个当前类的子类,并且为这个新的子类重写了观察属性的setter方法
    重写的setter方法会负责在调用原setter方法前后,通知所有观察对象属性值的更改情况
    kvo
    1.iOS用什么方式实现对一个对象的KVO?(KVO的本质是什么)
    利用runtimeAPI动态生成一个子类,并且让instance对象的isa指向这个全新的子类
    当修改instance对象的属性时,会调用Foundation的NSSetXXXValveAndNotify函数
    willChangeValueForkey
    父类原来的setter
    didChangeValueForKey
    内部会触发监听器(obserer)的监听方法(observeValueForKeyPath:ofObject:change:context)
    2.如何手动触发KVO?
    手动调用willChangeValueForkey和didChangeValueForKey
    3.直接修改成员变量会触发KVO吗?
    不会触发KVO,因为只有触发set方法才会触发(
    变量=不会触发 .变量=才会触发)
    kvc
    1.基本概念
    可以允许开发者通过Key名直接访问对象的属性,或者给对象的属性赋值。而不需要调用明确的存取方法。这样就可以在运行时动态地访问和修改对象的属性。而不是在编译时确定,这也是iOS开发中的黑魔法之一。
    2.赋值方法

    • (nullable id)valueForKey:(NSString *)key;

    • (void)setValue:(nullable id)value forKey:(NSString *)key;

    • (nullable id)valueForKeyPath:(NSString *)keyPath;

    • (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;

    3.赋值原理

    找到对应的key,然后将一个已经准备好的值赋过去
    方法- (nullable id)valueForKey:(NSString *)key;
    内部流程
    1.程序优先调用set<Key>:属性值方法,代码通过setter方法完成设置。注意,这里的<key>是指成员变量名,首字母大小写要符合KVC的命名规则
    2.如果没有找到setName:方法,KVC机制会检查+ (BOOL)accessInstanceVariablesDirectly方法有没有返回YES,默认该方法会返回YES,如果你重写了该方法让其返回NO的话,那么在这一步KVC会执行setValue:forUndefinedKey:方法,不过一般开发者不会这么做。所以KVC机制会搜索该类里面有没有名为<key>的成员变量,无论该变量是在类接口处定义,还是在类实现处定义,也无论用了什么样的访问修饰符,只在存在以<key>命名的变量,KVC都可以对该成员变量赋值。
    3.如果该类即没有set<key>:方法,也没有_<key>成员变量,KVC机制会搜索is<Key>的成员变量。
    4.和上面一样,如果该类即没有set<Key>:方法,也没有
    <key>和_is<Key>成员变量,KVC机制再会继续搜索<key>和is<Key>的成员变量。再给它们赋值。
    5.如果上面列出的方法或者成员变量都不存在,系统将会执行该对象的setValue:forUndefinedKey:方法,默认是抛出异常。
    6.特别需要注意的是:如果开发者想让这个类禁用KVC里,那么重写+ (BOOL)accessInstanceVariablesDirectly方法让其返回NO即可,这样的话如果KVC没有找到set<Key>:属性名时,会直接用setValue:forUndefinedKey:方法。
    https://www.jianshu.com/p/5273c8e29799

    https://www.jianshu.com/p/c3a5ffcd856b/https://www.jianshu.com/p/c3a5ffcd856b
    3.线程/线程锁 原子安全
    4.gcd
    用过哪些api
    5.内存管理
    哪些方法和操作会导致内存泄露
    6.崩溃异常怎么修复
    7.@synthesize 和 @dynamic分别有什么作用
    @synthesize的语义是如果你没有手动实现setter方法和getter方法,那么编译器会自动为你加上这两个方法
    @dynamic告诉编译器:属性的setter与getter方法由用户自己实现,不自动生成(当然对于readonly的属性只需提供getter即可)
    为什么block要使用copy而不是strong或者其他属性修饰?
    block本身是像对象一样可以retain,和release。但是,block在创建的时候,它的内存是分配在栈上的,而不是在堆上。他本身的作于域是属于创建时候的作用域,一旦在创建时候的作用域外面调用block将导致程序崩溃。因为栈区的特点就是创建的对象随时可能被销毁,一旦被销毁后续再次调用空对象就可能会造成程序崩溃,在对block进行copy后,block存放在堆区.
    使用retain也可以,但是block的retain行为默认是用copy的行为实现的,
    因为block变量默认是声明为栈变量的,为了能够在block的声明域外使用,所以要把block拷贝(copy)到堆,所以说为了block属性声明和实际的操作一致,最好声明为copy。

    category的加载处理过程
    1.通过Runtime加载某个类的所有Category数据
    2.把所有Category的方法,属性,协议数据,合并到一个大数组中,后面参与编译的Category数据,会在数组的前面
    3.将合并后的分类数据(方法,属性,协议)插入到类原来数据的前面

    +load方法会在runtime加载类,分类时调用 在mian函数之前调用
    每个类,分类的+load,在程序运行过程中只调用一次

    调用顺序
    1.先调用类的+load
    按照编译先后顺序调用(先编译,先调用)
    调用子类的+load之前会先调用父类的+load

    2.再调用分类的+load
    按照编译先后顺序调用(先编译,先调用)

    相关文章

      网友评论

          本文标题:2020-07-24

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