我们公司准备新招一个iOS开发,要求是中上的水平,然后让我出一套面试题。我认为既然是中上水平,那么基础和项目实战都要考,我自己也应该只能算中等水平。然后,我就出了一套题,没想到的是我出的第一题,竟然到现在还没有一个人答对(我们喊过来面试的都是毕业2年以上的)。我问了下同事,他们说,这个题有点刁钻。题目内容是这样的:
NSString *s1 = @"Hello world"; NSString *s2 = @"Hello world";请问 s1 == s2的返回值是YES还是NO?
写法①
很刁钻吗?这个不应该是基础题吗?
针对评论说,没有给答案
首先这道题的答案是:YES。其实,这道题真的不刁钻,刁钻的题可能是下面这样:
NSString *s1 = @"hello"; NSString *s2 = [[NSString alloc] initWithString:s1];请问s1==s2的返回值?
写法②
或者是下面这样:
NSString *s1 = [NSString stringWithFormat:@"hello"];NSString *s2 = [NSString stringWithFormat:@"hello"];请问s1==s2的返回值?
写法③
当然,上面的这几种写法,答案都是YES。可能学习Java的人,现在会有疑问了,可能认为写法②和写法③都应该是不相等的。
这道题考的两个字符串是否相等,这里的相等是绝对相等,不是isEqualToString
的方式,比较是两个字符串的内存地址是否一样。那这里就要从字符串在内存中的存储区间来看了,记得在Xcode6以前,字符串的定义还可以这么写:NSString *s2 = [[NSString alloc] initWithString:@"hello"]
,但是Xcode 6以后,这种写法就会有警告,说明编译器在字符串的存储这一块做了改变。
不管是直接定义一个字符串常量还是使用alloc
、init
的方式,字符串的存储都是在常量区,至于常量区是在堆区还是栈区,这个需要查证,因为不同的版本可能会有变动(java也是在Java7做了改变),而使用stringWithFormat方式创建的字符串,如果字符串相同也是只有一份,但是很明显,不是放在常量区的。也就是说使用同样的方式创建的相同的字符串(NSString)都是只有一份的。(NSMutableString是不一样的)
那是不是所有的字符串都是存在常量区呢?答案是否定的。你可以试着把NSString换成NSMutableString试试。很显然,这道题答对或者答错基本不会影响开发,但是,它是基础,也同样重要。
还有一道题是关于深拷贝和浅拷贝的区别,我发现也是基本没人说明白,我就在网上搜索了一下,发现网上的大多数答案也是出乎意料的大面积错误,也有正确的,但是很少。很多博客都是说的,浅拷贝是指针拷贝,深拷贝是重新创建对象。凡是这么答题的,我都会问他们指针拷贝是否就是两个不同引用指向同一个地址,他们大多也给了我肯定的答案。
我记得在学习Java和C++的时候,老师都讲过这个问题.。
Java里面的浅拷贝是实现Cloneable接口,实现里面的clone方法,类似下面这样:
class B implements Cloneable {
String name = null;
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return super.clone();
}
}
class A implements Cloneable
{
B b = null;
public A() {
b = new B();
}
@Override
protected Object clone() {
// TODO Auto-generated method stub
A a = null;
try {
a = (A) super.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return a;
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
A a1 = new A();
a1.b.name = "a1";
A a2 = (A) a1.clone();
a2.b.name = "a2";
System.out.println("第一个对象的name:"+a1.b.name + "第二个对象的name:"+a2.b.name);
}
} ```
深拷贝则是下面这样:
class B implements Cloneable {
String name = null;
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return super.clone();
}
}
class A implements Cloneable
{
B b = null;
public A() {
b = new B();
}
@Override
protected Object clone() {
// TODO Auto-generated method stub
A a = null;
try {
a = (A) super.clone();
a.b = (B) b.clone(); // 差别就在这里。多了对实例变量的拷贝。
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return a;
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
A a1 = new A();
a1.b.name = "a1";
A a2 = (A) a1.clone();
a2.b.name = "a2";
System.out.println("第一个对象的name:"+a1.b.name + "第二个对象的name:"+a2.b.name);
}
}```
差别只是一个是换壳,一个是把壳子包括里面的内容都换了。有的人用公司打比方,就是一个是换了个公司名,里面的员工不变;另外一个是换了公司名,里面的员工参照原公司重新招。差不多就是这个意思,只是人不能clone。
Objective-C 里面原理是一样的,只是遵守的是NSCopying
协议,实现里面的- (id)copyWithZone:(nullable NSZone *)zone;
方法。
针对评论说没有给出答案
上面的java代码其实已经给了答案了,OC可以类比过来。具体代码晚点发出来。