美文网首页
2019iOS总结学习

2019iOS总结学习

作者: 行走在北方 | 来源:发表于2019-06-27 23:19 被阅读0次

    1、递归反转字符串

    • (void)testReserveStr {
      NSLog(@"--%@", [self func:@"Antyum"]);
      }

    • (NSString *)func:(NSString *)str {
      return str.length ? [NSString stringWithFormat:@"%@%@",[self func:[str substringFromIndex:1]],[str substringToIndex:1]] : @"";

    2、快排及时间复杂度简单证明
    void quick_sort(int a[], int start, int end) {

    int val = a[start];
    
    int s = start;
    
    int e = end;
    
    while(s < e) {
    
        while(s < e && a[e] > val) e --;
    
        if(s < e) a[e --] = a[s];
    
        //一样
    
    }
    
    //赋初值
    
    //递归
    

    }

    冒泡算法
    void buddleSort(int num[],int count)
    {
        for (int i = 0; i < count - 1; i++) {
            for (int j = 0; j < count - i - 1; j++) {
                if (num[j] > num[j + 1]) EXCHANGE(num[j], num[j + 1])
            }
        }
    }
    

    作者:fvcasdgva
    来源:CSDN
    原文:https://blog.csdn.net/qq_36523667/article/details/79737648
    版权声明:本文为博主原创文章,转载请附上博文链接!

    3、什么时候在 block 里面用 self,不需要使用 weak self?

    __weak typeof(self) weakSelf = self;
    [self doSomeBlockJob:^{
        __strong typeof(weakSelf) strongSelf = weakSelf;
        if (strongSelf) {
            ...
        }
    }];
    

    当 block 本身不被 self 持有,而被别的对象持有,同时不产生循环引用的时候,就不需要使用 weak self 了。最常见的代码就是 UIView 的动画代码,我们在使用 UIView 的 animateWithDuration:animations 方法 做动画的时候,并不需要使用 weak self,因为引用持有关系是:

    [UIView animateWithDuration:0.2 animations:^{
        self.alpha = 1;
    }];
    

    UIView 的某个负责动画的对象持有了 block
    block 持有了 self
    因为 self 并不持有 block,所以就没有循环引用产生,因为就不需要使用 weak self 了。

    4、代理delegate与通知Notification、block的使用区别

    delegate与block一般用于两个对象1对1之间的通信交互、delegate需要定义协议方法,代理对象需要实现协议方法

    并且需要建立代理关系才可以实现通信。

    block更加简洁,不需要定义繁琐的协议方法,但是如果通信时间比较多的话,建议使用delgate。

    Notfication主要用于1对多的通信,而且通信对象之间不需要建立关系,但是使用通知,代码的可读性差。


    作者:-FIGHTING-
    来源:CSDN
    原文:https://blog.csdn.net/u012701023/article/details/49559549
    版权声明:本文为博主原创文章,转载请附上博文链接!

    5、 数组和链表的区别
    数组是将元素在内存中连续存储的;
    优点:因为数据是连续存储的,内存地址连续,所以在查找数据的时候效 率比较高;

            缺点:在存储之前,我们需要申请一块连续的内存空间,并且在编译的时候就必须确定好它的空间的大小。在运行的时候空间的大小是无法随着你的需要进行增加和减少而改变的,当数据两比较大的时候,有可能会出现越界的情况,数据比较小的时候,又有可能会浪费掉内存空间。在改变数据个数时,增加、插入、删除数据效率比较低。
    

    链表是动态申请内存空间,不需要像数组需要提前申请好内存的大小,

        链表只需在用的时候申请就可以,根据需要来动态申请或者删除内存空间,对于数据增加和删除以及插入比数组灵活。还有就是链表中数据在内存中可以在任意的位置,通过应用来关联数据(就是通过存在元素的指针来联系)。 
    

    数组应用场景:数据比较少;经常做的运算是按序号访问数据元素;数组更容易实现,任何高级语言都支持;构建的线性表较稳定。

    链表应用场景:对线性表的长度或者规模难以估计;频繁做插入删除操作;构建动态性比较强的线性表。


    作者:李@Boy96
    来源:CSDN
    原文:https://blog.csdn.net/m0_37135421/article/details/80964378
    版权声明:本文为博主原创文章,转载请附上博文链接!

    6、weak和assign的区别? assign可以修饰OC对象吗
    一、区别
    1.修饰变量类型的区别
    weak 只可以修饰对象。如果修饰基本数据类型,编译器会报错-“Property with ‘weak’ attribute must be of object type”。
    assign 可修饰对象,和基本数据类型。当需要修饰对象类型时,MRC时代使用unsafe_unretained。当然,unsafe_unretained也可能产生野指针,所以它名字是"unsafe_”。
    2.是否产生野指针的区别
    weak 不会产生野指针问题。因为weak修饰的对象释放后(引用计数器值为0),指针会自动被置nil,之后再向该对象发消息也不会崩溃。 weak是安全的。
    assign 如果修饰对象,会产生野指针问题;如果修饰基本数据类型则是安全的。修饰的对象释放后,指针不会自动被置空,此时向对象发消息会崩溃。
    二、相似
    都可以修饰对象类型,但是assign修饰对象会存在问题。
    三、总结
    assign 适用于基本数据类型如int,float,struct等值类型,不适用于引用类型。因为值类型会被放入栈中,遵循先进后出原则,由系统负责管理栈内存。而引用类型会被放入堆中,需要我们自己手动管理内存或通过ARC管理。
    weak 适用于delegate和block等引用类型,不会导致野指针问题,也不会循环引用,非常安全。

    作者:OnlyFunny
    链接:https://www.jianshu.com/p/e9a46253f587
    来源:简书
    简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

    7、weak置为nil是如何实现的

    Runtime维护了一个Weak表,用于存储指向某个对象的所有Weak指针。Weak表其实是一个哈希表,Key是所指对象的地址,Value是Weak指针的地址(这个地址的值是所指对象的地址)的数组。
    在对象被回收的时候,经过层层调用,会最终触发下面的方法将所有Weak指针的值设为nil。

    至于Weak指针如何注册到Weak表中、如何维护。Hash表的增删改查又是怎么做的?
    weak 的实现原理可以概括一下三步:
    1、初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。
    2、添加引用时:objc_initWeak函数会调用 objc_storeWeak() 函数, objc_storeWeak() 的作用是更新指针指向,创建对应的弱引用表。
    3、释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。
    再过具体的过程以后再完善。太过复杂。

    作者:费宇超
    链接:https://www.jianshu.com/p/7d95db4cd055
    来源:简书
    简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

    8、GCD中的Block用到的属性是否需要__weak修饰

    __weak __typeof(self) weakSelf  = self;
    self.block = ^{
        __strong __typeof(self) strongSelf = weakSelf; 
        [strongSelf doSomeThing];
        [strongSelf doOtherThing];
    };
    

    为什么使用weakSelf
    通过 clang -rewrite-objc 源代码文件名 将代码转为c++代码(实质是c代码),可以看到block是一个结构体,它会将全局变量保存为一个属性(是__strong的),而self强引用了block这会造成循环 引用。所以需要使用__weak修饰的weakSelf。
    为什么在block里面需要使用strongSelf
    是为了保证block执行完毕之前self不会被释放,执行完毕的时候再释放。这时候会发现为什么在block外边使用了__weak修饰self,里面使用__strong修饰weakSelf的时候不会发生循环引用?!
    PS:strongSelf只是为了保证在block内部执行的时候不会释放,但存在执行前self就已经被释放的情况,导致strongSelf=nil。注意判空处理。
    不会引起循环引用的原因
    因为block截获self之后self属于block结构体中的一个由__strong修饰的属性会强引用self, 所以需要使用__weak修饰的weakSelf防止循环引用。
    block使用的__strong修饰的weakSelf是为了在block(可以理解为函数)生命周期中self不会提前释放。strongSelf实质是一个局部变量(在block这个“函数”里面的局部变量),当block执行完毕就会释放自动变量strongSelf,不会对self进行一直进行强引用。
    总结
    外部使用了weakSelf,里面使用strongSelf却不会造成循环,究其原因就是因为weakSelf是block截获的属性,而strongSelf是一个局部变量会在“函数”执行完释放。

    作者:daybreak_
    链接:https://www.jianshu.com/p/404e0ea5f6d7
    来源:简书
    简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

    9、Block中对数组增删怎么操作
    使用enumerateObjectsUsingBlock删除数组里面的对象元素出现删除不全的问题

    [indexSceneList enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            NSLog(@"obj===>%@",obj);
            NSLog(@"idx===>%lu",(unsigned long)idx);
    
            SceneItem *customIndexSceneItem = obj;
            NSLog(@"customIndexSceneItem.uid=>%@",customIndexSceneItem.uid);
            for (SceneItem *item in defaultSceneList) {
                NSLog(@"item.uid==>%@==>%@",item.uid,customIndexSceneItem.uid);
                if ([item.uid isEqualToString:customIndexSceneItem.uid]) {
                    [indexSceneList removeObject:customIndexSceneItem];
                }
            }
        }];
    

    原因:当发现符合删除条件的时候将该元素从数组里删除,这是数组里的元素会向前移动,各个元素的下标会-1,但是遍历过程是按idx递增的,所以下一个获取的元素是跳过了一个下标的元素,也就是删除一个再遍历获取到的元素实际上是原始数组跳过一个下标的元素。简而言之,就是删除一个元素以后数组元素位置前移,但是我们遍历的元素的索引又向后加,就刚好完美错过。
    解决方案:倒叙删除

    for (NSInteger i = [indexSceneList count] - 1; i >= 0; i--) {
            SceneItem *customIndexSceneItem = [indexSceneList objectAtIndex:i];
            for (SceneItem *item in defaultSceneList) {
                NSLog(@"item.uid==>%@==>%@",item.uid,customIndexSceneItem.uid);
                if ([item.uid isEqualToString:customIndexSceneItem.uid]) {
                    [indexSceneList removeObjectAtIndex:i];
                }
            }
        }
    

    作者:我一不小心就
    链接:https://www.jianshu.com/p/eacd2e9f88da
    来源:简书
    简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

    10、+load和+initilaze在分类,父类,子类和main函数的调用顺序

    +load加载顺序:父类,子类,分类。如果多个分类会按照PBXSourcesBuildPhase中顺序逐个调用。
    main()
    +initialize加载顺序:首先有分类时,最后被load的分类会覆盖类的该方法。然后先父类,再子类,直到第一次被调用的类。

    11、IOS单例的两种实现方式
    根据线程安全的实现来区分,一种是使用@synchronized ,另一种是使用GCD的dispatch_once函数。

    1.@synchronized 实现

    static InstanceClass *instance;
    + (InstanceClass *)defaultInstance{
        @synchronized (self){
            if (instance == nil) {
                instance = [[InstanceClass alloc] init];
            }
        }
        
        return instance;
    }
    

    2.GCD的dispatch_once

    static InstanceClass *instance;
    + (InstanceClass *)defaultInstance{
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            instance = [[InstanceClass alloc] init];
        });
        
        return instance;}
    

    (https://www.cnblogs.com/caixuebin/p/4538657.html)

    12、abc三个任务,执行完刷新UI,怎么处理?除了你说的group还有其他方式吗

    在swift中分组管理异步任务的方式

    1 group enter 和 leave 进行同步管理

    func method1() {
        //  创建一个组 ,要是在一个控制器中去规划请求顺序,则这个组要是全局的组
        let group = DispatchGroup();
        let queue = DispatchQueue.global();
        
        //
        let imgsArr = ["http://qzonestyle.gtimg.cn/qzone/app/weishi/client/testimage/1024/21.jpg","http://qzonestyle.gtimg.cn/qzone/app/weishi/client/testimage/1024/22.jpg","http://qzonestyle.gtimg.cn/qzone/app/weishi/client/testimage/1024/23.jpg"]
        
        
        for i in 0 ..< imgsArr.count {
            // 每一次循环进入组
            group.enter();
         
            // 进行网络请求
            let session = URLSession.shared;
            
            let task = session.dataTask(with: URL(string: imgsArr[i])!) { (data, response, error) in
                if error == nil {
                    print("第\(i)张图片下载完成");
                    // 离开组
                    group.leave();
                }
            };
            // 执行
            task.resume();
            
        }
        
        // 执行完毕
        // 打印结果
        /* 第2张图片下载完成
        第1张图片下载完成
        第0张图片下载完成
        图片下载完毕
        
        */
        
        // 当组内所有任务都完成之后 出发notify
        group.notify(queue:queue) {
            print("图片下载完毕");
        };
    

    2 使用信号量进行实现

    信号量是基于计数器的一种线程同步机制

    func method2() {
     
        //
        let imgsArr = ["http://qzonestyle.gtimg.cn/qzone/app/weishi/client/testimage/1024/111.jpg","http://qzonestyle.gtimg.cn/qzone/app/weishi/client/testimage/1024/112.jpg","http://qzonestyle.gtimg.cn/qzone/app/weishi/client/testimage/1024/113.jpg"]
        
        // 创建一个信号量 默认值为0  每每发送一次 信号量+1
        
        let sem = DispatchSemaphore.init(value: 0);
    
        for i in 0 ..< imgsArr.count {
            
            
            // 进行网络请求
            let session = URLSession.shared;
            
            let task = session.dataTask(with: URL(string: imgsArr[i])!) { (data, response, error) in
                if error == nil {
                    print("第\(i)张图片下载完成");
                    
                    // 请求成功 信号进行+1操作
                    sem.signal()
                }
                
            };
            
            task.resume();
            // 一共创建了三个wait函数
            sem.wait(timeout: DispatchTime.distantFuture);
        }
        
        // 等待所有的wait函数都被释放的时候 任务都完成的时候 信号不为0 程序继续执行
       
        /*
         第0张图片下载完成
         第1张图片下载完成
         第2张图片下载完成
         图片下载完毕
         */
        print("图片下载完毕");
        
        
    }
    

    作者:iOS_Apple
    来源:CSDN
    原文:https://blog.csdn.net/github_36850997/article/details/93475834
    版权声明:本文为博主原创文章,转载请附上博文链接!

    13、单行多个Label,中间可压缩,怎么添加约束

    [self.label1 setContentHuggingPriority:1 forAxis:UILayoutConstraintAxisHorizontal];
    [self.label1 setContentCompressionResistancePriority:2 forAxis:UILayoutConstraintAxisHorizontal];
    

    14、iOS实现轮播图的两种思路
    https://www.jianshu.com/p/5847021bebc2

    15、当在dealloc方法中移除KVO监听时,如何防止崩溃

    - (void)dealloc {
        
        @try {
            
            [self.view removeObserver:self forKeyPath:@"frame"];
        }
        @catch(NSException *exception) {
            NSLog(@"--%@", exception);
        }
    }
    

    16、cell里面的button不能响应点击事件
    https://www.jianshu.com/p/86198458d0ae
    解决的办法也很简单,view 继承 uitableviewcell就ok了。

    17、可变类型声明属性用copy
    在iOS开发中,我们在定义一个NSString的时候都会用copy来修饰,@property (nonatomic, copy)NSString *str;那为什么不用strong呢,我写了一个测试,来简单的说明一下首先把修饰符写成strong 在viewDidLoad的方法中,定义一个可变的字符串

    @property (nonatomic, strong)NSString *str;
    
    (void)viewDidLoad {
    [super viewDidLoad];
    NSMutableString *string = [NSMutableString string];
    [string appendString:@"hello"];
    self.str = string;
    NSLog(@"%@",self.str);
    [string appendString:@"World"];
    NSLog(@"%@",self.str);
    }
    2019-06-27 23:17:47.548842+0800   [2133:178664] hello
    2019-06-27 23:17:47.548994+0800   [2133:178664] helloWorld
    
    如果用copy
    2019-06-27 23:18:44.321677+0800  [2156:180654] hello
    2019-06-27 23:18:44.321830+0800  [2156:180654] hello
    
    如果声明为copy类型,则在属性的setter方法中会对传入的对象进行拷贝并返回,并不会出现属性指向MutableString对象的地址,因为返回的是新的拷贝对象,新对象操作已经和原来的老对象无关了。
    

    作者:weixin_34375233
    来源:CSDN
    原文:https://blog.csdn.net/weixin_34375233/article/details/88192433
    版权声明:本文为博主原创文章,转载请附上博文链接!

    相关文章

      网友评论

          本文标题:2019iOS总结学习

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