2021年iOS基础知识汇总-初步学习篇

  • Category类

    struct category_t {
        const char *name;
        classref_t cls;
        struct method_list_t *instanceMethods;
        struct method_list_t *classMethods;
        struct protocol_list_t *protocols;
        struct property_list_t *instanceProperties;
        // Fields below this point are not always present on disk.
        struct property_list_t *_classProperties;
    
        method_list_t *methodsForMeta(bool isMeta) {
            if (isMeta) return classMethods;
            else return instanceMethods;
        }
    
        property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
    };
    
  • OC的类信息存放在哪里?

    对象方法、属性、成员变量、协议信息,存放在class对象中

    类方法,存放在meta-class对象中

    成员变量的具体值,存放在instance对象

  • 对象的isa指针指向哪里?

    instance对象的isa指针指向class对象

    class对象的isa指针指向meta-class对象

    meta-class对象的isa指针指向基类的meta-class对象

  • 如何扩大button的点击面积

  • runtime 消息转发机制(最详细流程)

    2021年iOS基础知识汇总-初步学习篇_第1张图片

    // 动态解析
    + (BOOL)resolveInstanceMethod:(SEL)sel {
    //    NSLog(@"动态方法解析");
    //    NSString *methodName = NSStringFromSelector(sel);
    //
    //    if ([methodName isEqualToString:@"callMyName:"]) {
    //        return class_addMethod(self, sel, (IMP)callMyName, "v@:@");
    //    }
    //
        return NO;
    }
    
    - (id)forwardingTargetForSelector:(SEL)aSelector {
    //    NSString *methodName = NSStringFromSelector(aSelector);
    //    if ([methodName isEqualToString:@"sendMessage:"]) {
    //        return [Animal new];
    //    }
        
        return [super forwardingTargetForSelector:aSelector];
    }
    
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
        NSString *methodName = NSStringFromSelector(aSelector);
        if ([methodName isEqualToString:@"sendMessage:"]) {
            return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
        }
        return [super methodSignatureForSelector:aSelector];
    }
    
    - (void)forwardInvocation:(NSInvocation *)anInvocation{
        SEL sel = anInvocation.selector;
        Animal *child = [Animal new];
        if ([child respondsToSelector:sel]) {
            [anInvocation invokeWithTarget:child];
        }else {
            [super forwardInvocation:anInvocation];
        }
    
    }
    
    - (void)doesNotRecognizeSelector:(SEL)aSelector {
        NSLog(@"最终并未识别");
    }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ylVYpA0k-1616467169065)(/Users/ibrotherliyanbing/Library/Application Support/typora-user-images/image-20210323092843103.png)]

  • Swift的语言优势

    https://zhuanlan.zhihu.com/p/21755854

    Swift 是一个多范式(Multi-paradigm)的编程语言。它基本涵盖了函数式编程(Functional Programming)语言和面向对象编程语言的所有特性。可以进行函数式编程,或者 Protocol-Oriented Programming 等,而不局限于某种特定编程的范式。

    基本的现代化语言特性

    • 简洁的 class、extension 和 property 定义方式。 OC 定义一个 class 至少需要 4 行,而 swift 只需要两行。新建类的成本大大降低,可以避免因为书写成本高而没有使用类的情况,而此时新建类通常使代码更加整洁。
    • 命名空间。基于 module (Xcode target) 的命名空间,避免使用类前缀来防止命名冲突这种原始的方式。
    • 函数重载。避免了 numberWithInteger: numberWithDouble 这么啰嗦的命名了。所以 Swift 版 的 Masonry 的 equalTo 方法可以传对象和也可以传基本类型,而不需要使用宏。
    • override 修饰符。避免了无意间重写父类方法。
    • Tuple(元组)。低成本且使用方便的数据集合。它提供了函数多个返回值功能。
    • nested functions。在函数里面定义函数,直接在语法上限定只在局部使用的函数,使代码更清晰简洁。(虽然 OC 能使用 block 来代替实现,但写起来好难看)
    • 简洁的闭包写法。{ a in … } 比 C/OC 中丑陋的 block 写法好很多。
    • 运算符重载。例如我们定义了一个<|> 运算符,它的输入时两个函数使得 ( f <|> g )(x) 等同于 f(g(x)) 这样多重的嵌套 f(g(h(x))) ,就可以写成 (f<|>g<|>h)(z) 这样的线性表达式了。
    • 不用写分号!
  • kvo原理图

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VqgTkm3e-1616467169068)(/Users/ibrotherliyanbing/Library/Application Support/typora-user-images/image-20210321103816100.png)]

  • 多线程问题

    死锁发生条件:当前串行队列添加同步任务

    条件:当前、同步、串行 不满足不会死锁

    下文中的queue.sync就属于1线程中,然后在开同步,这么会导致sync死锁,修改方式是修改为并行队列

    func gcdTest() {print("=================")
    //    self.perform(#selector(self.pri), with: nil, afterDelay: 1.0)let queue = DispatchQueue(label: "serial")
    ​    queue.async {print(Thread.current)print(1)
    //      self.perform(#selector(self.pri), with: nil, afterDelay: 1.0)
    //      self.perform(#selector(self.pri))
    //      RunLoop.current.run()
    ​      queue.sync {print(2)}print(3)}print(4)
    
      }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8aGLv1ev-1616467169076)(/Users/ibrotherliyanbing/Library/Application Support/typora-user-images/image-20210321184908724.png)]

  • iOS 主队列同步造成死锁的原因

    如果在主线程中运用主队列同步,也就是把任务放到了主线程的队列中。
    而同步对于任务是立刻执行的,那么当把第一个任务放进主队列时,它就会立马执行。
    可是主线程现在正在处理 syncMain 方法,任务需要等 syncMain 执行完才能执行。
    syncMain 执行到第一个任务的时候,又要等第一个任务执行完才能往下执行第二个和第三个任务。
    这样 syncMain 方法和第一个任务就开始了互相等待,形成了死锁。
    
  • iOS存储

    1、Documents 目录:您应该将所有的应用程序数据文件写入到这个目录下。这个目录用于存储用户数据。该路径可通过配置实现iTunes共享文件。可被iTunes备份。

    2、AppName.app 目录:这是应用程序的程序包目录,包含应用程序的本身。由于应用程序必须经过签名,所以您在运行时不能对这个目录中的内容进行修改,否则可能会使应用程序无法启动。

    3、Library 目录:这个目录下有两个子目录:
    Preferences 目录:包含应用程序的偏好设置文件。您不应该直接创建偏好设置文件,而是应该使用NSUserDefaults类来取得和设置应用程序的偏好.
    Caches 目录:用于存放应用程序专用的支持文件,保存应用程序再次启动过程中需要的信息。
    可创建子文件夹。可以用来放置您希望被备份但不希望被用户看到的数据。该路径下的文件夹,除Caches以外,都会被iTunes备份。

    4、tmp 目录:这个目录用于存放临时文件,保存应用程序再次启动过程中不需要的信息。该路径下的文件不会被iTunes备份。

  • KVO的实现原理

    2021年iOS基础知识汇总-初步学习篇_第2张图片

    在KVO_MyObject类中,重写了一些方法,举例如

    • setter方法:在重写的setter方法中调用了willChangeValueForKey:方法和didChangeValueForKey:方法,在这两个方法中则会调用observeValueForKeyPath:ofObject:change:context方法,这个方法就是接受通知的回调方法。
    • class方法:重写的class方法返回的是MyObject类对象而不是KVO_MyObject类对象,其目的是欺骗使用者。
  • Runtime 消息转发

    meta-class是类对象的类,每个类都有自己单独的meta-class。所有的类对象并不会属于同一个meta-class。
    meta-class要保证类对象具有继承体系中基类的所有实例和类方法,以及继承体系中的所有中间类方法。对于所有NSObject继承体系下的类,NSObject的实例方法和协议方法对他们和他们meta-class的对象都要有效。
    所有的meta-class使用基类的meta-class作为自己的基类,对于顶层基类的meta-class也是一样,只是它指向自己而已。

    为了可以调用类方法,这个类的isa指针必须指向一个包含这些类方法的类结构体。
    这样就引出了meta-class的概念:meta-class是一个类对象的类。

    简单解释下:
    当你向一个对象发送消息时,runtime会在这个对象所属的那个类的方法列表中查找。
    当你向一个类发送消息时,runtime会在这个类的meta-class的方法列表中查找。
    meta-class之所以重要,是因为它存储着一个类的所有类方法。每个类都会有一个单独的meta-class,因为每个类的类方法基本不可能完全相同。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OEzHzWVd-1616467169084)(/Users/ibrotherliyanbing/Library/Application Support/typora-user-images/image-20210309140542885.png)]

    当一个方法找不到的时候,Runtime 提供了 消息动态解析消息接受者重定向消息重定向 等三步处理消息,具体流程如下图所示:

  • 消息机制的基本原理

    Objective-C 语言 中,对象方法调用都是类似 [receiver selector]; 的形式,其本质就是让对象在运行时发送消息的过程。

    我们来看看方法调用 [receiver selector]; 在『编译阶段』和『运行阶段』分别做了什么?

    1. 编译阶段:

      [receiver selector];
      

      方法被编译器转换为:

      1. objc_msgSend(receiver,selector) (不带参数)
      2. objc_msgSend(recevier,selector,org1,org2,…)(带参数)
    2. 运行时阶段:消息接受者 recevier寻找对应的 selector。

      1. 通过 recevierisa 指针 找到 recevierClass(类)
      2. Class(类)cache(方法缓存) 的散列表中寻找对应的 IMP(方法实现)
      3. 如果在 cache(方法缓存) 中没有找到对应的 IMP(方法实现) 的话,就继续在 Class(类)method list(方法列表) 中找对应的 selector,如果找到,填充到 cache(方法缓存) 中,并返回 selector
      4. 如果在 Class(类) 中没有找到这个 selector,就继续在它的 superClass(父类)中寻找;
      5. 一旦找到对应的 selector,直接执行 recevier 对应 selector 方法实现的 IMP(方法实现)
      6. 若找不到对应的 selector,消息被转发或者临时向 recevier 添加这个 selector 对应的实现方法,否则就会发生崩溃。
  • block都有哪些类型,并且都是在什么环境下的:

    2021年iOS基础知识汇总-初步学习篇_第3张图片

    2021年iOS基础知识汇总-初步学习篇_第4张图片

  • NSOperation介绍

    2021年iOS基础知识汇总-初步学习篇_第5张图片

  • 一个整数的二进制中1的个数

    常规解法。
    思路:将整数n与1进行与运算,当整数n最低位是1时,则结果非零,否则结果为0。
    然后将1左移一位,继续与n进行与运算,当次低位是1时,结果非零,否则结果为0。
    循环以上操作,记录非零的次数即可。
    代码如下:

    `public static int times1(int n ){

        int count = 0;
        int flag = 1;
        while(flag <= n){
            if((n&flag) != 0)
                count++;
            flag = flag<<1;
        }
        return count;
    }
    

    `

    优化的解法
    思路:
    1.对一个整数n,比如10,它的二进制是1010。
    2.将10减一变为9,9的二进制是1001.
    3.比较10和9的二进制数,对10减一操作就等于将10的二进制的最低位上的1以及后面的位取反,前面的数不变。
    总结:把一个整数减去1,再和原整数做与运算,会把该整数最右边1一个1变成0。那么一个整数的二进制表示中有多少个1,就可以进行多少次这样的操作。从而可以减少比较的次数。
    public static int times2(int n){ int count = 0; while(n!=0){ count++; n = n&(n-1); } return count; }

  • Weak-Strong搭配使用的误解

    在使用Block时,我们可以使用weak关键字来避免外部变量被Block强引用而导致的循环引用,同时为了Block中的代码能够正常执行,许多开发者提出了Weak-Strong搭配使用的方式,类似如下:

    { __weak typeof(self) weakSelf = self; self.block = ^{ __strong typeof(weakSelf) strongSelf = weakSelf; [strongSelf test1]; [strongSelf test2]; }; }

    以上代码相对于单独使用weak来说还是有好处的,在单独使用weak时,可以保证在执行 [weakSelf test1]或 [weakSelf test2]单条语句时,weakSelf所指的self不会被释放或self已经释放而直接向nil发送消息。

    若使用Weak-Strong搭配的方式的话,可以保证在执行 [strongSelf test1]和 [strongSelf test2]时,是向同一对象发送消息。

    为什么这么说呢?当开始执行Block语句时,若self还存在,那么strongSelf可以保证在整个Block代码块中不会被释放,即使Block中调用无数次strongSelf,strongSelf也不会因为多线程而在半途被释放;若开始执行Block时,self已经被释放,那么之后所有的消息都会被发送至nil。所以Weak-Strong搭配可以保证Block中语句被处理为一个事务。

    所以说,Weak-Strong并不能保证Block中语句一定会被执行,它只能保证Block中语句作为一个事务被发送到同一对象处。只要理解了weak实现原理,我们就能明白何时单独使用weak也能完成代码功能,而何时必须使用Weak-Strong来保证代码事务能力。

  • weak,__unsafe_unretained, unowned 与 assign区别

    __unsafe_unretained: 不会对对象进行retain,当对象销毁时,会依然指向之前的内存空间(野指针)

    weak: 不会对对象进行retain,当对象销毁时,会自动指向nil

    assign: 实质与__unsafe_unretained等同

    unsafe_unretained也可以修饰代表简单数据类型的property,weak也不能修饰用来代表简单数据类型的property。

    __unsafe_unretained 与 weak 比较,使用 weak 是有代价的,因为通过上面的原理可知,__weak需要检查对象是否已经消亡,而为了知道是否已经消亡,自然也需要一些信息去跟踪对象的使用情况。也正因此,__unsafe_unretained 比 __weak快,所以当明确知道对象的生命期时,选择__unsafe_unretained 会有一些性能提升,这种性能提升是很微小的。但当很清楚的情况下,__unsafe_unretained 也是安全的,自然能快一点是一点。而当情况不确定的时候,应该优先选用 __weak 。

    unowned使用在Swift中,也会分 weak 和 unowned。unowned 的含义跟 __unsafe_unretained 差不多。假如很明确的知道对象的生命期,也可以选择 unowned。

    作者:Colleny_Z
    链接:https://www.jianshu.com/p/3c5e335341e0

  • assign可以修饰OC对象么?

    可以,但不会增加该对象的引用计数,当没有强引用持有该对象时,该对象就会被释放,如果此时再向该对象发消息,会导致崩溃问题。

  • weak 实现原理的概括

    Runtime维护了一个weak表,用于存储指向某个对象的所有weak指针。weak表其实是一个hash(哈希)表,Key是所指对象的地址,Value是weak指针的地址(这个地址的值是所指对象指针的地址)数组。

    作者:逍遥晨旭
    链接:https://www.jianshu.com/p/13c4fb1cedea

  • weak 的实现原理可以概括一下三步

    1、初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。
    2、添加引用时:objc_initWeak函数会调用 objc_storeWeak() 函数, objc_storeWeak() 的作用是更新指针指向,创建对应的弱引用表。
    3、释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。

    Clang之后的结果如下:

    id __attribute__((objc_ownership(weak))) obj1 = obj;

    1、初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。

    2021年iOS基础知识汇总-初步学习篇_第6张图片

    2、添加引用时:objc_initWeak函数会调用 objc_storeWeak() 函数, objc_storeWeak() 的作用是更新指针指向,创建对应的弱引用表。

    2021年iOS基础知识汇总-初步学习篇_第7张图片

    3、释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。

    作者:Colleny_Z
    链接:https://www.jianshu.com/p/3c5e335341e0
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • weak基本用法

你可能感兴趣的:(swift,OC,ios,objective-c,开发语言)