面试

1. assign vs weak, __block vs __weak

  • strong 和 weak 是在arc后引入的关键字,strong类似于retain,引用时候会引用计算+1,weak相反,不会改变引用计数。
  • weak和assign都是引用计算不变,两个的差别在于,weak用于object type,就是指针类型,而assign用于简单的数据类型,如int BOOL 等。
    assign看起来跟weak一样,其实不能混用的,assign的变量在释放后并不设置为nil(和weak不同),当你再去引用时候就会发生错误,崩溃,EXC_BAD_ACCESS.
  • block 不能修改局部变量,如果需要修改需要加上__block.
    block 会对对象强引用,引起retain-cycle,需要使用__weak
    在block种使用weakSelf可避免这种强引用(两个指针,指向同一块地址(self))。

2. 如何用GCD同步若干个异步调用

  • 必须是并发队列才起作用
  • 需求分析
  • 首先,分别异步执行2个耗时的操作
  • 其次,等2个异步操作都执行完毕后,再回到主线程执行一些操作
    使用队列组实现上面的需求
  • 使用队列组实现上面的需求
// 创建队列组
dispatch_group_t group =  dispatch_group_create();
// 获取全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 往队列组中添加耗时操作
dispatch_group_async(group, queue, ^{    
    // 执行耗时的异步操作1
});
dispatch_group_async(group, queue, ^{    
    // 执行耗时的异步操作2 
});
// 当并发队列组中的任务执行完毕后才会执行这里的代码
dispatch_group_notify(group, queue, ^{
    // 如果这里还有基于上面两个任务的结果继续执行一些代码,建议还是放到子线程中,等代码执行完毕后在回到主线程
    // 回到主线程
    dispatch_async(group, dispatch_get_main_queue(), ^{
        // 执行相关代码...
    });
});

3. 分类中添加属性

利用runtime实现setter、getter方法

@interface ClassName (CategoryName)
@property (nonatomic, strong) NSString *str;
@end

//实现文件
#import "ClassName + CategoryName.h"
#import <objc/runtime.h>
  
static void *strKey = &strKey;
@implementation ClassName (CategoryName)

-(void)setStr:(NSString *)str
{
    objc_setAssociatedObject(self, & strKey, str, OBJC_ASSOCIATION_COPY);
}

-(NSString *)str
{
    return objc_getAssociatedObject(self, &strKey);
}

@end

6. isKindOfClass和isMemberOfClass的区别

isKindOfClass来确定一个对象是否是一个类的成员,或者是派生自该类的成员
isMemberOfClass只能确定一个对象是否是当前类的成员

7. isEqualisEqualToString==的区别

  • 首先 OC中的对象都是用指针表示,方法的调用是基于消息机制实现,== 比较的自然是指针指向的地址
  • 然后说下 isEqual 和 isEqualToString 的区别
    IsEqual 是 NSObject 的方法 ,而 isEqualToString 是 NSString 的方法,因此从继承关系角度来说isEqualToString 是 isEqual 的衍生方法。

首先贴个苹果官方重写isEqual 的demo

- (BOOL)isEqual:(id)other {  
   if (other == self)   
       return YES;  
   if (!other || ![other isKindOfClass:[self class]])  
       return NO;  
   return [self isEqualToWidget:other];  
}  
 
- (BOOL)isEqualToWidget:(MyWidget *)aWidget {  
   if (self == aWidget)  
      return YES;  
   if (![(id)[self name] isEqual:[aWidget name]])  
      return NO;  
   if (![[self data] isEqualToData:[aWidget data]])  
       return NO;  
   return YES;  
}

简述判断顺序:
首先都会判断 指针是否相等 ,相等直接返回YES,
不相等再判断是否是同类对象或非空,空或非同类对象直接返回NO,
而后依次判断对象对应的属性是否相等,若均相等,返回YES
这样就不难理解 isEqualToString 的实现内部的了

最后解释 HashCodeisEqual 的关系
hash和isEqual:方法都在NSObject协议中声明,且彼此关系紧密。实现hash方法必须返回一个整型数(NSInterger),作为哈希表结构中的表地址。两个对象相等(isEqual:方法的判断结果)意味着它们有相同的哈希值。如果哈希值相同,两个对象不一定相等。如果您的对象可能被包含在像NSSet这样的集合中,则需要定义hash方法,比如UIWebView,并确保该方法在两个对象相等的时候返回相同的哈希值。

8. block里面的如何防止retain cycle

使用弱引用打断block里面的retain cycle

  1. MRC中 block 是不会引起retain;
  2. 但在ARC中 _block 则会引起retain。ARC中应该使用 _weak __或__unsafe_unretained弱引用

9. __block 在MRC、ARC环境下的作用

MRC 1. 说明变量可改 2.说明指针指向的对象不做这个隐式的retain操作
ARC 1. 说明变量可改

9. load和initialize的区别

load initialize
执行时机 程序运行后立即执行 在类的方法第一次被调用时执行
自身未定义时 不沿用父类的 沿用父类的
类别中的定义 全部执行,但后于类中的方法 覆盖类中的方法,只执行一个

10. 基本算法

二分查找 θ(logn)

递归方法
int binarySearch1(int a[] , int low , int high , int findNum)
{    
      int mid = ( low + high ) / 2;       
      if (low > high) {
            return -1;   
      }
      else {        
              if (a[mid] > findNum)          
                    return binarySearch1(a, low, mid - 1, findNum);        
              else if (a[mid] < findNum)            
                    return binarySearch1(a, mid + 1, high, findNum);                    
              else            
                    return mid;   
    }
}

非递归方法
int binarySearch2(int a[] , int low , int high , int findNum)
{    
      while (low <= high)
      {
            int mid = ( low + high) / 2;   //此处一定要放在while里面
            if (a[mid] < findNum)           
                low = mid + 1;        
            else if (a[mid] > findNum)            
                high = mid - 1;       
             else           
                return mid;    
    }       
    return  -1;
}
冒泡排序   θ(n^2)
void bubble_sort(int a[], int n)
{
    int i, j, temp;
    for (j = 0; j < n - 1; j++)
        for (i = 0; i < n - 1 - j; i++) //外层循环每循环一次就能确定出一个泡泡(最大或者最小),所以内层循环不用再计算已经排好的部分
        {
            if(a[i] > a[i + 1])
            {
                temp = a[i];
                a[i] = a[i + 1];
                a[i + 1] = temp;
            }
        }
}

快速排序  调用方法  quickSort(a,0,n);  θ(nlogn)
void quickSort (int a[] , int low , int high)
{
    if (high < low + 2)
        return;
    int start = low;
    int end = high;
    int temp;
    
    while (start < end)
    {
        while ( ++start < high && a[start] <= a[low]);//找到第一个比a[low]数值大的位子start

        while ( --end  > low  && a[end]  >= a[low]);//找到第一个比a[low]数值小的位子end

        //进行到此,a[end] < a[low] < a[start],但是物理位置上还是low < start < end,因此接下来交换a[start]和a[end],于是[low,start]这个区间里面全部比a[low]小的,[end,hight]这个区间里面全部都是比a[low]大的
        
        if (start < end)
        {
            temp = a[start];
            a[start]=a[end];
            a[end]=temp;
        }
        //在GCC编译器下,该写法无法达到交换的目的,a[start] ^= a[end] ^= a[start] ^= a[end];编译器的问题
    }
    //进行到此,[low,end]区间里面的数都比a[low]小的,[end,higt]区间里面都是比a[low]大的,把a[low]放到中间即可

    //在GCC编译器下,该写法无法达到交换的目的,a[low] ^= a[end] ^= a[low] ^= a[end];编译器的问题
    
    temp = a[low];
    a[low]=a[end];
    a[end]=temp;
    
    //现在就分成了3段了,由最初的a[low]枢纽分开的
    quickSort(a, low, end);
    quickSort(a, start, high);
}

11.为什么说Objective-C是一门动态的语言

名词释义:

  • 动态类型语言:动态类型语言是指在运行期间才去做数据类型检查的语言,也就是说,在用动态类型的语言编程时,永远也不用给任何变量指定数据类型,该语言会在你第一次赋值给变量时,在内部将数据类型记录下来。Python和Ruby就是一种典型的动态类型语言,其他的各种脚本语言如VBScript也多少属于动态类型语言。
  • 静态类型语言:静态类型语言与动态类型语言刚好相反,它的数据类型是在编译其间检查的,也就是说在写程序时要声明所有变量的数据类型,C/C++是静态类型语言的典型代表,其他的静态类型语言还有C#、JAVA等。Objective-C虽然在写程序时声明了变量的数据类型,但是在编译期间并没有检查。因为Objective-C类和对象都是在运行时候创建的,所以编译期间没办法检查。
    总结::两者的区别在于是否在编译期间做数据类型检查。

OC是动态语言的原因

  • Objective-C 是C 的超集,在C 语言的基础上添加了面向对象特性,并且利用Runtime 这个运行时机制,为Objective-C 增添了动态的特性。
  • Objective-C 可以通过Runtime 这个运行时机制,在运行时动态的添加变量、方法、类等,所以说Objective-C 是一门动态的语言。
  • Objective-C 使用的是 “消息结构” 并非 “函数调用”:使用消息结构的的语言,其运行时所应执行的代码由运行期决定;而使用函数调用的语言,则由编译器决定。
    总结:OC利用Runtime可以在运行的时候创建类,修改类,修改对象调用的方法。

OC做为一门面向对象语言,自然具有面向对象的语言特性,如封装、继承、多态。他具有静态语言的特性(如C++),又有动态语言的效率(动态绑定、动态加载等)。
OC的动态特性表现为了三个方面:动态类型、动态绑定、动态加载。之所以叫做动态,是因为必须到运行时(run time)才会做一些事情。

  • 1)动态类型
    动态类型,说简单点就是id类型。动态类型是跟静态类型相对的。像内置的明确的基本类型都属于静态类型(int、NSString等)。静态类型在编译的时候就能被识别出来。所以,若程序发生了类型不对应,编译器就会发出警告。而动态类型就编译器编译的时候是不能被识别的,要等到运行时(run time),即程序运行的时候才会根据语境来识别。所以这里面就有两个概念要分清:编译时跟运行时。
  • 2)动态绑定
    动态绑定(dynamic binding)貌似比较难记忆,但事实上很简单,只需记住关键词@selector/SEL即可。先来看看“函数”,对于其他一些静态语言,比如c++,一般在编译的时候就已经将将要调用的函数的函数签名都告诉编译器了。静态的,不能改变。而在OC中,其实是没有函数的概念的,我们叫“消息机制”,所谓的函数调用就是给对象发送一条消息。这时,动态绑定的特性就来了。OC可以先跳过编译,到运行的时候才动态地添加函数调用,在运行时才决定要调用什么方法,需要传什么参数进去。这就是动态绑定,要实现他就必须用SEL变量绑定一个方法。最终形成的这个SEL变量就代表一个方法的引用。这里要注意一点:SEL并不是C里面的函数指针,虽然很像,但真心不是函数指针。SEL变量只是一个整数,他是该方法的ID。以前的函数调用,是根据函数名,也就是字符串去查找函数体。但现在,我们是根据一个ID整数来查找方法,整数的查找字自然要比字符串的查找快得多!所以,动态绑定的特定不仅方便,而且效率更高。
  • 3)动态加载
    就是根据需求动态地加载资源。

12. runtime 如何实现 weak 属性

  • 要实现 weak 属性,首先要搞清楚 weak 属性的特点:
    weak 此特质表明该属性定义了一种“非拥有关系” (nonowning relationship)。为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特质同 assign 类似, 然而在属性所指的对象遭到摧毁时,属性值也会清空(nil out)。

  • 那么 runtime 如何实现 weak 变量的自动置nil?
    runtime 对注册的类, 会进行布局,对于 weak 对象会放入一个 hash 表中。 用 weak 指向的对象内存地址作为 key,当此对象的引用计数为0的时候会 dealloc,假如 weak 指向的对象内存地址是a,那么就会以a为键, 在这个 weak 表中搜索,找到所有以a为键的 weak 对象,从而设置为 nil。

13. Objective-C中向nil发送消息会怎样

在Objective-C中向nil发送消息是完全有效的——只是在运行时不会有任何作用。Cocoa中的几种模式就利用到了这一点。发向nil的消息的返回值也可以是有效的:

  • 如果一个方法返回值是一个对象,那么发送给nil的消息将返回0(nil)。例如:Person * motherInlaw = [ aPerson spouse] mother]; 如果spouse对象为nil,那么发送给nil的消息mother也将返回nil。
  • 如果方法返回值为指针类型,其指针大小为小于或者等于sizeof(void*),float,double,long double 或者long long的整型标量,发送给nil的消息将返回0。
  • 如果方法返回值为结构体,正如在《Mac OS X ABI 函数调用指南》,发送给nil的消息将返回0。结构体中各个字段的值将都是0。其他的结构体数据类型将不是用0填充的。
  • 如果方法的返回值不是上述提到的几种情况,那么发送给nil的消息的返回值将是未定义的。
    下面的代码段就是一个有效地向nil发送消息的示例:
id anObjectMybeNil = nil;  
//这种写法是有效的  
if ( [ anObjectMaybeNil methordThatReturnADouble] == 0.0 )  
{  
    //其他的实现代码  
}  

注意:在Mac OS X v10.5版本中,向nil发送消息的结果与上面的描述会稍有不同。在Mac OS X v10.4以及更以前的版本中,向nil发送消息是完全有效的,只要消息的返回值是对象,任意类型的指针,void,或者是其他大小小于或者等于sizeof(void*)的整型标量。此时,发送给nil的消息将返回nil。如果发送nil的消息的返回值不是上述几种类型(比如说返回的类型是结构体,或者是浮点类型,或者是向量类型的),其返回值则是未定义的。因此,在Mac OS X v10.4以及更老的版本中,我们不应该依赖于发送给nil对象的消息的返回值,除非该消息的返回值是一个对象,任意类型的指针,或者是任意大小小于或者是等于sizeof(void *)的整型标量。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 1.ios高性能编程 (1).内层 最小的内层平均值和峰值(2).耗电量 高效的算法和数据结构(3).初始化时...
    欧辰_OSR阅读 29,522评论 8 265
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,135评论 1 32
  • 面试题参考1 : 面试题[http://www.cocoachina.com/ios/20150803/12872...
    江河_ios阅读 1,756评论 0 4
  • 生命在于运动,人生需要倒腾。 前几天有幸添加了一位非常优秀的个人品牌管理专家熊莉姐姐,印象深刻的是...
    瑞妈小思阅读 1,330评论 19 15
  • 文明狂想曲 一、冰河世纪: 苹果熟了,便落地。文明到顶点了,便衰亡。我们现在这个文明是唯一的吗?不是。我们从来不孤...
    阿峰_d548阅读 242评论 0 0