1、关于nil和Nil及NULL的区别:
nil: A null pointer to an Objective-C object. ( #define nil ((id)0) ) nil 是一个对象值。
Nil: A null pointer to an Objective-C class.
NULL: A null pointer to anything else. ( #define NULL ((void *)0) ) NULL是一个通用指针(泛型指针)。
NSNull: A class defines a singleton object used to represent null values in collection objects (which don’t allow nil values).
[NSNull null]: The singleton instance of NSNull.
[NSNull null]是一个对象,他用在不能使用nil的场合。
2、避免使用僵尸对象的方法
为了防止不小心调用了僵尸对象,可以将对象赋值nil(对象的空值)
?
3、对象的内存泄露
4、@property 参数
内存管理相关参数:
5、@class的使用
作用
可以简单地引用一个类
#### 简单使用
@class Dog; //类的引入
仅仅是告诉编译器: Dog是一个类; 并不会包含Dog这个类的所有内容
具体使用
在.h文件中使用@class引用一个类 在.m文件中使用#import包含这个类的.h文件
通常引用一个类有两种办法:
一种是通过#import方式引入;另一种是通过@class引入; 这两种的方式的区别在于:
1)#import方式会包含被引用类的所有信息,包括被引用类的变量和方法;@class方式只是告诉 编译器在A.h文件中 B *b 只是类的声明,具体这个类里有什么信息,这里不需要知道,等实现文 件中真正要用到时,才会真正去查看B类中信息;
2)使用@class方式由于只需要只要被引用类(B类)的名称就可以了,而在实现类由于要用到被 引用类中的实体变量和方法,所以需要使用#import来包含被引用类的头文件;
3)通过上面2点也很容易知道在编译效率上,如果有上百个头文件都#import了同一 个文件,或 者这些文件依次被#improt(A->B, B->C,C->D…),一旦最开始的头文件稍有改动,后面引用到这 个文件的所有类都需要重新编译一遍,这样的效率也是可想而知的,而相对来 讲,使用@class方 式就不会出现这种问题了;
所以:我们实际开发中尽量在.h头文件中使用@class
4)对于循环依赖关系来说,比方A类引用B类,同时B类也引用A类,B类的代码:
作用上的区别
import会包含引用类的所有信息(内容), 包括引用类的变量和方法 @class仅仅是告诉编译器有这么一个类, 具体这个类里有什么信息, 完全不知
效率上的区别
如果有上百个头文件都#import了同一个文件,或者这些文件依次被#import,那么一旦最开始的头 文件稍有改动,后面引用到这个文件的所有类都需要重新编译一遍 , 编译效率非常低 相对来讲,使用@class方式就不会出现这种问题了
6、循环引用
循环retain的场景
比如A对象retain了B对象,B对象retain了A对象 循环retain的弊端 这样会导致A对象和B对象永远无法释放
循环retain的解决方案
当两端互相引用时,应该一端用retain、一端用assign
7、NSString 类的内存管理问题
1)、NSString 等Foundation框架中类的内存管理
先看看以下这几种写法:
NSString *testStr1 = @”a”;
NSString *testStr2 = [NSString stringWithString:@”a”];
NSString *testStr3 = [NSString stringWithFormat:@”b”];
NSString *testStr4 = [[NSString alloc] initWithString:@”c”];
NSString *testStr5 = [[NSString alloc] initWithFormat:@”d”];
NSString *testStr6 = [[NSString alloc] init];
NSLog(@”testStr1 ->%p”,testStr1);
NSLog(@”testStr2 ->%p”,testStr2);
NSLog(@”testStr3 ->%p”,testStr3);
NSLog(@”testStr4 ->%p”,testStr4);
NSLog(@”testStr5 ->%p”,testStr5);
NSLog(@”testStr6 ->%p”,testStr6);
通过对比地址可以看到,从上可以看出,testStr1,testStr2,testStr4都是在一个内存区域,也 就是常量内存区,
1---> NSString *str = [[NSString alloc] initWithString:@"ABC"];
2---> str = @"123";
3---> [str release];
4---> NSLog(@"%@",str);
首先,咱们先对这段代码进行分析。
第一句 声明了一个NSString类型的实例 str, 并将其初始化init后赋值为@”ABC” 第二行,将str的指针指向了一个常量@”123”。 理论上讲在第一行初始化的@”ABC”没有任何任何 指针指向了。 所以造成了内存泄露
然后第三行, 将str的引用计数-1
第四行输出str的值 为123.
首先回答为什么不会崩溃, 因为第三行的release 实际上是release了一个常量@”123” 而作为 常量,其默认的引用计数值是很大的(100k+)
NSLog(@”retainCount = %tu”,[@”123” retainCount]);
最终的输出值会是一个很大很大的数。 所以单单一个release是不会将其释放掉的。
然后再回答这样会不会造成内存泄露。
其实…………理论上讲 会!
但是实际上,Objective-C对NSString类型有特殊照顾。所有的NSString的引用计数器默认初始值 都会非常非常大。
2)、危险的用法
while ([a retainCount] > 0) {
[a release];
}
如果运行结果正确,那么这是多么幸运的一个人啊!
8、自动释放池及autorelease介绍
自动释放池
(1)在iOS程序运行过程中,会创建无数个池子,这些池子都是以栈结构(先进后出)存在的。 (2)当一个对象调用autorelease时,会将这个对象放到位于栈顶的释放池中
自动释放池的创建方式
(1)iOS 5.0以前的创建方式
NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
`````````````````
[pool release];//[pool drain];用于mac
(2)iOS5.0以后
@autoreleasepool
{//开始代表创建自动释放池
·······
}//结束代表销毁自动释放池
autorelease
是一种支持引用计数的内存管理方式
它可以暂时的保存某个对象(object),然后在内存池自己的排干(drain)的时候对其中的每个 对象发送release消息 注意,这里只是发送release消息,如果当时的引用计数(reference-counted)依然不为0,则该 对象依然不会被释放。可以用该方法来保存某个对象,也要注意保存之后要释放该对象。
为什么会有autorelease?
(1)不需要再关心对象释放的时间
(2)不需要再关心什么时候调用release
autorelease何时释放?
对于autorelease pool本身,会在如下两个条件发生时候被释放
1)手动释放Autorelease pool
2)Runloop结束后自动释放
对于autorelease pool内部的对象
在引用计数的retain == 0的时候释放。release和autorelease pool 的 drain都会触发retain– 事件。
9、Block
一、静态变量和全局变量 在加和不加 __block都会直接引用变量地址。也就意味着可以修
改变量的值。在没有加__block 参数的情况下。
? 全局block和栈block区别为是否引用了外部变量,堆block则是对栈block copy得来。对全局block
copy 不会有任何作用,返回的依然是全局block。
二, 常量变量(NSString *a = @”hello”;a 为常量变量,@“hello”为常量。)—–不 加__block类型 block 会引用常量的地址(浅拷贝)。加__block类型 block会去引用常量变 量(如:a变量,a = @”abc”.可以任意修改a 指向的内容。)的地址。