1、有多继承吗?没有的话用什么代替?
答:没有!这个是肯定的。(委托只能代替继承!)类别。在类的一个类别里面添加方法,而所添加的方法会被原来的类所拥有,这相当于多继承。但是类别有一个重要的限制,导致它很鸡肋,不能添加实便变量。当然这个是有解决办法的,比如存取方法,可以在其中来存取静态全局变量,或者使用运行时api动态添加实例变量。第二个方法是使用消息转发。
关于多继承,我还有一些要说。多继承的作用是什么?多继承可以为一个类添加额外的方法,抽象来说的话可以为一个类添加额外的行为而可能影响,又或者不影响原来的行为。那么多继承的应用情况就这两种了,一是一个类已经定义好了,它已经有了它自己的父类,这时你需要为经添加一些新的方法,新的行为,这就需要多继承的。在这种情况下,多继承的作用说白了,就是添加方法,那么类别是完全可以满足需要的,如果不需要添加实例变量的话,当然还有一种方法,那就是动态添加方法。 但是还有一种情况,多个类已经定义好了,而他们又有各自的父类,而这时你需要为他们添加共同,或者类似的方法,比如你不想一份代码写多次,减少冗余,这时你可以定义一个新的基类,实现这个需要共有的方法而由这几个类共同继承,这时,多继承的意义便是共用。这时,类别就完全不够用了。这时,应该使用委托,注意,这里的委拖不同于delegate模式,而是一种广义上的委托,比如这几个类要做一件共同的事,那么就新定义一个类,用来实现这个任务,而原来的这几个类则将任务委托给它来做。
2、ios没有真正意义上的私有变量。
无论你的变量放在哪里,只要我想,总能拿的到,kvc就是用来干这个的!但是放在.m文件,类扩展里的方法和变量,实际上对于别的类来说是不可见的,可以理解为私有的属性。
3、static的作用!
static的主要作用是隐藏。对于变量来说,不加static的全局变量是全局可见的,在其他的文件中也可以被访问到。而加了static的全局变量却只能在本文件被访问。而局部变量如果加了static修饰,则会被放到静态存储区,在局部函数被再次访问的时候他的值还是会保持前一次的状态,另外,这个变量只会被初始化一次,而且应该只能用直接量来进行初始化,在程序第一次遇到它的时候进行初始化,换言之,它不能被动态的值初始化。举例来说,static a = 1;但是static a = b就不行。函数也是起到全局的隐藏效果。对于c++来说,static可以用来定义静态方法。
所以静态全局变量相对于普通全局变量来说的好处就是不会被其他文件所使用,和避免命名冲突。
局部函数中所声明变量叫自动变量,它们会被放在栈中,随着函数的退出,这些空间会被释放。而由程序动态分配的变量则是放在堆中。全局变量放在静态数据区中。
下面是c++中的static所起的作用。
在成员前加static修饰可以成为类静态员,类所有,为所有该类的对象共享。静态方法具有相同的特性。
4、@import的作用是只引用一次,防止多次引用。
即使多次@import一个文件,该文件也只会被引用一次 #include则不同。@class告诉类声明,这个类在实现的时候才去查找他的定义,这个标识可以用来防止交叉编译。
5、进程与线程的差别:
进程可以理解为程序的一次执行,而线程则是进程中的一个片段。
1)进程间是独立的,一般而言进程是无法突破程序的边界限制而去访问另一个进程的存储空间,而一个进程的线程则共享这个进程的存储空间,并可以进行互相通信。
2)一般而言,进程至少需要一个线程,但可以多于一个,但线程却只属于一个进程,并且只能属于一个。
3)其实线程就是一个简化版的进程,它的划分尺度小进程,从而使系统的并发程度提高。
4)进程是程序的一次运行,是系统进行资源分配和调度的一个独立单位。线程是cpu调度和分派的基本单位。
5)一个线程可以创建和撤消另一个线程;同一个进程之中的多个线程可以并发执行。
6、关于内存管理:ios的内存管理使用的是引用计数模式。所谓引用计数模式,是相对于垃圾回收机制来说的。对于有垃圾回收机制的语言,程序中所创建的内存会被系统所管理,在一定的时候进行回收,程序员只管创建而不需要去释放。相对的,使用引用计数模式的语言中,程序员需要对程序中所申请的内存负责。每一个对象都维护着一个引用计数值,当一个新的引用指向它时,引用计数器就递增,去掉一个引用时,计数器就递减,当计数器的值为0时,系统便认为它是无人需要的,这时便可去它所占有的资源进行回收。
具体到ios里面来说,当一个对象被创建时,它的引用计数为1,具体到ios语句,为alloc。一个被创建的对象可以被继续引用,这时使用的是retain,它告诉这个对象,我要使用你了,在我结束使用你以前,请不要死。而这时该对象则对它作出回应,并且自身的引用计数加1。当对象结束使用它时,对它发送release消息,告诉它我不使用你了,相应的该对象则将自己的引用计数减一。直到没有引用直向这个对象的时候,也就是说这个对象的引用计数为0了,系统认为这个对象已经没有用了,将会对它进行回收。
注意,以上所内容所说的引用为强引用。还有一种引用叫弱引用。简单来说,对一个对象的弱引用,表明我知道你在那里,我会用你,但是你是死是活与我无关。即,弱引用持有一个对象的指针,但没有属于它的引用计数。
以上所陈述的是ios内存管理的一些基本的原理。实际操作上没有这么复杂,只要明白几条基本的规则就能保证内存的合理应用。一是谁创建谁释计,谁(强)引用谁释放,这样就保证了强引用与引用释放的一一对应,避免了只引用不释放和过度释放问题的发生。二是对于强引用的对象可以发送自动释放,也就是autorelease来进行释放;收到autorelease消息的对象会在将来的某一刻自己将引用计数减一。三是作为返回值的强引用对象要发送autorelease消息,比如你在一个方法里面局部创建了一个对象,并且要将这个对象作为该方法的返回值返回,那么在这之前你需要对它发送autorelease消息。四是对于retain的属性,要在deleac函数里面对它进行释放并置空。也可以使用self.property = nil这样的语句,这个语句与arc方法进行了统一。五是使用弱引用避免引用回环。引用回环多出现在代理模式上,实际项目中我们常看到a.delegate = self的语句,实际上这里a的property属性多是弱引用以避免引用回环。
虽然上文频频提到强引用与弱引用,并且指出,所谓强引用是指持有指针加引用计数加1,但是这两项因素并没有特定的联系。在这种情况下,实际内存是不安全的,因为一个弱引用也可以对该对象发送release消息,而被引用对象并不知道对它发送release消息的对象是否持有一个有效的引用计数;同时一个强引用也可以在不对它发送release消息的情况下将这个引用抛掉,而该对象仍会去等待这个引用所持有的引用计数减1。所以,在非arc情况下的强弱引用只是一个概念上的东西。举个例子来说,如果a对b进行了retain,并且a且持有了b的指针,这时我们可以说a对b持有强引用。但实际上a可以将这个指针抛掉而并不对b发送release消息,这时没有一个有用的指针指向b,而b并不知道a已经抛弃了它,仍会一直等待,这时便出现了我们常说的内存泄漏。另一种情况是,出现了另一外对象c,它对b持有指针但并没有属于它的引用,而他仍可以向b发送release消息,b并不知道是a还是别的谁发送了release消息,但它仍会对这个release消息做出响应并将引用计数减一。这时b实际的引用计数值已经为0,并且被释放,而当a最终找到b并对b发送release消息的时候,它所持有的实际是一个已经被系统回收了的对象,这时便发生了我们常说的过度释放。
在非arc情况下,强引用与弱引用的维护全在程序员的身上。
另外说一下arc。对于arc首先要强调一点,arc并不是垃圾回收,虽然它简化了内存管理,但它实际上还是引用计数,只不过从手动变成了自动,更确切一点说,它其实是将在非arc模式下必须遵守的一些隐式的规则显示的表达出来并放到了编译环境中,从编译时便避免了内存问题的发生。arc模式下显示的定义了强引用与弱引用,并用将相关的规则集成到其中。强引用被定义为strong指针,而弱引用则被定义为weak指针。strong指针的含议是,对一个对象持有引用并且有一个属于它的引用计数。strong指针不能被release,但当一个strong指针被置为nil的时候,相应的retainCount也会被减去。相应的weak指针也不能再对引用计数做任何的操作,因为它没有属于自己的引用计数。同时,当一weak指针所指向的对向被消毁的时候,这个weak指针也会被置空--这是一项重要的特性,它保证了弱引用指针不能去引用一个不可用的对象。
实际上,arc模式并不是在运行的时候被执行的--它是一个编译过程。编译器在编译的过程中,自动的在合适的地方插入retain,release等代码。
关于autorelease:
面试中常见的一个问题是:autorelease对象什么时候该用什么时候不该用,以及什么时候被释放。首先解释一下什么是autorelease。手动对一个对象发送release消息会导致该对象的引用计数立刻被减一。而autorelease则会在将来的某一刻对该对象发送一个release消息。实际上被加了autorelease消息的对象会被放到autorelease pool中,而该pool在释放时,会对放到其中的对象发送一次release消息。这个说法是官方的说法,实际上我认为放到autorelease pool中这个说法是不准确的--对于两次放到同一个pool的同一个对象,pool会怎么做?实际上我们可以认为,autorelease pool是一个链表,或者一个数组,每一个被发送autorelease pool消息的对象的指针都会被加入到autorelease pool结构中,而当autorelealse pool释放的时候,这些指针也都会收到一次release消息。而多次另入其中的对象实际上也会被释放多次。
那么下一个问题就是,autorelease对象何时被释放?哈哈!这个问题是不专业的。实际上我们应该问的是autorelease pool何时对其中的对象发送release消息?苹果文档告诉我们,在每一次run loop结束时,autorelease pool都会被drain一次,一次点击,触摸,都可以是一次run loop。这样说似乎很不好理解,让我们换一种说法。我们想象整个程序都是被一个while循环驱动的,在每一次while循环中,程序不断接收事件,然后交给相应的对象来处理。每一次循环,都是一次run loop。而autorelease pool,则可以理解为这个循环中的对象管理器,每一次循环中所产生的autorelease对象都被它记录,循环完的时候,对这些对象进行一次release。
(void)viewDidLoad
{
[super viewDidLoad];
[self test];
NSLog(@"%@", testString);
}
(void)test
{
testString = [[[NSString alloc] initWithFormat:@"aaaaaa"] autorelease];
[self test1];
}
(void)test1
{
NSLog(@"%@", testString);
}
这两个连续调用并没有走出一个run loop的范围,所以没有引发内存错误。
还有一个问题:什么时候该显示创建autorelease pool?为什么只有一个autorelease pool?这个问题太简单,不解释。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。