可能比较杂乱,可以按照关键字来搜索快速查找定位。
可能会遇到的问题:
如果是复制进去的文件,Linker、编译的时候报错,说文件没有找到,可能是文件没有添加到 Compile Sources 列表里面,选择 项目根 -> Build Phases -> Compile Sources
.h 文件只是用来声明一个类有哪些成员变量和方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #import <Foundation/Foundation.h> @interface Student : NSObject { int _age; } - (int )getAge; - (void )setAge:(int )age; @end
@interface
代表声明一个类,最后接 @end
“:” 代表继承
.m 文件是用来实现类的一些方法的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #import "Student.h" @implementation Student - (int )getAge { return _age; } - (void )setAge:(int )age { _age = age; } @end
创建一个 Student 对象: 1、调用一个静态方法 alloc 来分配内存
1 Student *stu = [Student alloc];
2、调用一个动态方法 init 进行初始化
相当于:
1 2 3 4 5 Student *stu = [[Student alloc] init]; Person *person = [[Person alloc] init]; person.age = 10 ; int age = person.age;
在 OC 当中,使用 %@
来打印一个对象(内存地址),重写 description
方法
在 OC 当中格式化字符串:
1 NSString *string = [NSString stringWithFormat:@"age is %i and no is %i" , _age, _no];
如果直接把方法写在 .m 文件中,没有在 .h 文件中进行声明,那么这个方法就是私有的
.h 文件中,当编译器遇到 @property
时,会自动展开成 getter 和 setter 的声明,用在 @interface
里面
1 2 3 @property int age;- (int )age; - (void )setAge:(int )newAge;
.m 文件中,@synthesize age; 相当于:
1 2 3 4 5 6 7 - (void )setAge:(int )newAge { age = newAge; } - (int )age { return age; }
还可以写成:@synthesize age, no, height;
用在 @implementation
里面@synthesize
会自动生成 getter 和 setter 的实现方法。@synthesize
默认会去访问跟 age 同名的变量,如果找不到,会在类的内部自动生成一个同名的变量,所以在 .h 文件中可以不用再声明该变量。age = _age
代表 getter 和 setter 会去访问 _age 这个成员变量,自动生成的 age 就不会再存在了
在 Xcode 4.5 之后,如果在 .h 文件中使用 @property
声明了变量 age 或者 _age,在 .m 文件中没有使用 @synthesize
实现这个变量,编译器会自动帮我们创建一个变量 _age。
如果在 getter 或者 setter 方法里面需要做一些操作的话,就不能依赖于 @synthesize
了。 如果你实现了 @synthesize
和 getter/setter 方法,@synthesize
就作废,不会再帮你创建 getter/setter 方法了。
内存管理
范围:任何继承了 NSObject
的对象,对基本数据类型无效
原理:
每个对象内部都保存了一个与之相关联的整数,称为引用计数器
当使用 alloc、new 或者 copy 创建一个对象时,对象的引用计数器被设置为 1
给对象发送一条 retain 消息,可以使引用计数器 +1
给对象发送一条 release 消息,可以使引用计数器 -1
当一个对象的引用计数器值为 0 时,那么它将被销毁,其占用的内存被系统回收,OC 也会自动向对象发送一条 dealloc 消息。一般会重写 dealloc 方法,在这里释放相关资源。一定不要直接调用 dealloc 方法
可以给对象发送 retainCount 消息获得当前的引用计数器值
每个OC对象都有自己的引用计数器,是一个整数,表示“对象被引用的此数”,即有多少人正在使用这个OC对象 每个OC对象内部都专门有4个字节的存储空间来存储引用计数器
重写dealloc方法:
1 2 3 4 5 - (void )dealloc { [super dealloc]; }
内存管理法则
谁创建,谁释放(“谁污染,谁治理”)。如果你通过 alloc、new 或 (mutable)copy 来创建一个对象,那么你必须调用 release 或 autorelease。换句话说,不是你创建的,就不用你去释放
一般来说,除了 alloc、new 或 copy 之外的方法创建的对象都被声明了 autorelease
谁 retain,谁 release。只要你调用了 retain,无论这个对象时如何生成的,你都要调用 release
方法注释:
@class 关键字:
@class
用来声明一个类,在 .h 文件中,可以不需要 import 进来某个类的 .h 文件,用@class
类名 代替,在 .m 文件中,因为要实现方法,需要用到类的一些东西,所以需要 import
进来这个类的 .h 文件
1 @property (retain ) Book *book;
这里的 retain 代表:在 setter 方法中,release 旧值,retain 新值 在 @property
后面加上 (retain)
,在实现文件 .m 中,会自动生成管理内存的方法:
1 2 3 4 5 6 - (void )setBook:(Book *)book { if (_book != book) { [_book release]; _book = [book retain ]; } }
1 2 3 4 @property int age; @property (readonly ) int age; @property (assign ) int age;
@property
属性默认为 atomic,提供多线程安全 在多线程环境下,原子操作时必要的,否则有可能引起错误的结果 加了 atomic,setter/getter 是一个原子操作,如果有多个线程同时调用 setter 的话,不会出现某一个线程执行 setter 全部语句之前,另一个线程开始执行 setter 的情况,相当于函数头尾加了锁一样
如果是 nonatomic,代表方法不需要考虑线程安全问题 禁止多线程,变量保护,提高性能 atomic 是 OC 使用的一种线程保护技术,防止在写入未完成的时候被另外一个线程读取,造成数据错误。而这种机制是耗费系统资源的,所以在 iPhone 这种小型设备上,如果没有使用多线程间的通讯,那么 nonatomic 是一个非常好的选择 如果不需要多线程支持的话,用 nonatomic 就够了,另外由于不涉及锁操作,所以它执行相对快点
1 @property (nonatomic , getter = isRich) Bool rich;
getter 是用来指定 get 方法的方法名
1 2 3 4 5 6 @autoreleasepool { Student *stu = [[[Student alloc] init] autorelease]; }
当自动释放池被销毁了,池子里面的所有对象都会调用释放方法
使用静态方法快速创建一个 autorelease 的对象:
在 Student.m 文件中创建一个创建对象的静态方法:
1 2 3 4 5 + (id )student { Student *stu = [[[Student alloc] init] autorelease]; return stu; }
使用:
1 Student *stu = [Student student];
在静态方法中,不能访问成员变量
autoreleasepool 注意事项:
在 ARC 下,不能使用 [[NSAutoreleasePool alloc] init]
,而应当使用 @autoreleasepool
不要把大量循环操作放到同一个 NSAutoreleasePool 之间,这样会造成内存峰值的上升
尽量避免对大内存使用该方法,对于这种延迟释放机制,还是尽量少用
SDK 中一般利用静态方法创建并返回的对象都是已经 autorelease 的,不需要再进行 release 操作。如 [NSNumber numberWithInt:10];
返回的对象是不需要再 release 的。但是通过 [[NSNumber alloc] initWithInt:10]
创建的对象需要 release
Category:
OC 提供了一种与众不同的方式—— Category,可以动态地为已经存在的类添加新的行为(方法)
这样可以保证类的原始设计规模较小,功能增加时再逐步扩展
使用 Category 对类进行扩展时,不需要创建子类
Category 使用简单的方式,实现了类的相关方法的模块化,把不同的类方法分配到不同的分类文件中
创建一个 Category 也很简单, File -> New -> File… ,选择 Source 下的 Objective-C File,点击 Next,File 取名为你要分类的名称,比如写 Test,File Type 选择为 Category,Class 输入你要扩展的类,然后就一直点击 Next,创建好了 Category,文件名为 Student+Test.h 和 Student+Test.m,Student+Test.h 内容大致如下:
1 2 3 @interface Student (Test )@end
Student+Test.m 内容大致如下:
1 2 3 @implementation Student (Test )@end
()
代表这是一个分类,括号中的 Test 代表分类的名称,在 Student+Test.h 中添加一个方法:
这样就将 Student 类扩展了一个 test2 的方法了,test2 的实现写在 Student+Test.m 中
Category 的作用:在不改变原来类内容的基础上,可以为类增加一些方法 Category 只能扩展方法,不能增加成员变量 Category 方法实现中可以访问原来类中声明的成员变量 Category 也能给系统的类扩展方法 Category 可以重新实现原来类中的方法,但是会覆盖掉原来的方法,导致原来的方法不能再使用 方法调用的优先级: 分类(最后编译的优先) -> 原来类 -> 父类 如果多个 Category 重复定义了同名的方法,会执行最后编译的 Category 的方法
Protocol:
简单来说就是一系列方法的列表,其中声明的方法可以被任何类实现。这种模式一般称为代理 (delegation) 模式
在 iOS 和 OS X 开发中,Apple 采用了大量的代理模式来实现 MVC 中的 View(UI 控件) 和 Conroller(控制器) 的解耦
1 2 3 @protocol 协议名称 <NSObject > @end
如何遵守协议
类遵守协议 @interface 类名 : 父类名 <协议名称1, 协议名称2> @end
协议遵守协议 @protocol 协议名称 <其他协议名称1, 其他协议名称2> @end
协议中方法声明的关键字
@required(默认),要求时限,如果没有实现,会发出警告
@optional,不要求实现,不会有警告
定义一个变量的时候,限制这个变量保存的对象遵守某个协议 类名<协议名称> *变量名; id<协议名称> 变量名; NSObject *obj; id obj2; 如果没有遵守对应的协议,编译器会警告
@property 中声明的属性也可用做一个遵守协议的限制 @property (nonatomic, strong) 类名<协议名称> *属性名; @property (nonatomic, strong) id<协议名称> 属性名; @property (nonatomic, strong) Dog *dog; @property (nonatomic, strong) id dog2;
协议可以定义在单独的 .h 文件中,也可以定义在某个类中
如果这个协议只用在某个类中,应该把这个协议定义在该类中
如果这个协议用在很多类中,应该把这个协议定义在单独的文件中
分类可以定义在单独 .h 文件和 .m 文件中,也可以定义在原来的类中
一般情况下,都是定义在单独 .h 文件中
定义在原来的类中的分类,只要求能看懂语法
Button.h 主要代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 @protocol ButtonDelegate <NSObject >- (void )onClick; @end @interface Button : NSObject @portocol (nonatomic , retain ) id <ButtonDelegate> delagate; - (void )click; @end
Button.m:
1 2 3 4 5 6 7 8 9 10 11 @implementation Button - (void )dealloc { [_delegate release]; [super dealloc]; } - (void )click { [_delegate onclick]; } @end
ButtonListener.h:
1 2 3 4 5 #import "Button.h" @interface ButtonListener : NSObject <ButtonDelegate >@end
ButtonListener.m:
1 2 3 4 5 @implementation ButtonListener - (void )onClick{ NSLog (@"按钮被点击了" ); } @end
main.m:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #import "Button.h" #import "ButtonListener.h" int main(int argc, const char * argv[]) { @autoreleasepool { Button *button = [[[Button alloc] init] autorelease]; ButtonListener *listener = [[[ButtonListener alloc] init] autorelease]; button.delegate = listener; [button click]; } return 0 ; }
1 2 3 4 5 6 7 8 9 10 @protocol Study <NSObject >@required - (void )test; @optional - (void )test2; @end
判断某个对象是否有遵守某个 protocol:
1 2 3 if ([stu confirmsToProtocl:@protocol (protocol name )]) {}
判断是否实现了某个方法:
1 2 3 if ([_delegate respondsToSelector:@selector (onClick:)]) {}
Block:
Block 封装了一段代码,可以在任何时候执行
Block 可以作为函数参数或者函数的返回值,而其本身又可以带输入参数或返回值。它和传统的函数指针很类似,但是有区别:block 是 inline(内联函数) 的,并且默认情况下它对局部变量时只读的
苹果官方建议尽量多用 block。在多线程、异步任务、集合遍历、集合排序、动画转场用的很多
例如定义一个 a + b 的 Block:
1 2 3 int (^Sum) (int , int ) = ^(int a, int b) { return a + b; }
调用:
或者这样定义也行:
1 2 3 4 5 6 7 typedef int (^MySum) (int , int );void test() { MySum sum = ^(int a, int b) { return a + b; } }
调用也和之前一样:
Block 可以访问外部定义的局部变量,但是不能修改:
1 2 3 4 5 6 7 void test() { int c = 15 ; MySum sum = ^(int a, int b) { NSLog (@"c = %i" , c); return a + b; } }
要修改 c的值的话,将 c 声明为: __block int c = 15;
前面有两个下划线 _
直接访问成员变量,该变量的声明必须是 @public:
1 2 Student *stu = [[[Student alloc] init] autorelease]; stu->age = 10 ;
指向函数的指针
1 2 3 4 5 6 int sum(int a, int b) { return a + b; } int (*p)(int , int ) = sum;int d = p(10 , 20 );
方法和函数的区别: 方法:
对象方法都是以减号 - 开头,类方法都是以 + 开头
对象方法的声明必须写在 @interface 和 @end 之间,对象方法的实现必须写在 @implementation 和 @end 之间
对象方法只能由对象来调用,类方法只能由类来调用
对象方法归类/对象所有
对象方法中能访问当前对象的成员变量(实例变量);类方法中不能访问成员变量(实例变量)
函数:
函数能写在文件中的任意位置(@interface 和 @end 之间),函数归文件所有
函数调用不依赖于对象
函数内部不能直接通过成员变量名访问某个对象的成员变量
继承的使用场合:
当两个类拥有相同的属性和方法,就可以将相同的东西抽取到一个父类中
当 A 类完全拥有和 B 类中的部分属性和方法时,可以考虑让 B 类继承 A 类
多态:
没有继承就没有多态
代码的体现:父类类型的指针指向子类对象
好处:如果函数/方法参数中使用的是父类类型,可以传入父类、子类对象
局限性:
父类类型的变量不能调用子类特有的方法,必须强制类型转换为子类类型的变量之后才能调用 强制类型转换
1 2 Animal *a = [Dog new]; Dog *d = (Dog *)a;
成员变量的作用域:
@public:在任何地方都能直接访问对象的成员变量
@private:只能在当前类的对象中直接访问(@implementation 中默认就是 @private)
@protected:可以再当前类及子类的对象方法中直接访问(@interface 中默认就是 @protected)
@package:只要处在同一个框架中,就能直接访问对象的成员变量
@implementation 中不能定义和 @interface 中同名的成员变量
NSString 是不可变的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 NSString *str1 = @"A String!" ;NSString *str2 = [[NSString alloc] init];str2 = @"A String!" ; [str2 release]; NSString *str3 = [[NSString alloc] initWithString:@"A String!" ];[str3 release]; str3 = [NSString stringWithString:@"A String!" ]; NSString *str4 = [[NSString alloc] initWithUTF8String:@"A String!" ];[str4 release]; NSString *str5 = [[NSString alloc] initWithFormat:@"My nameis %s and age is %i" , "mlb" , 21 ];[str5 release]; *str5 = [[NSString alloc] stringWithFormat:@"My nameis %s and age is %i" , "mlb" , 21 ]; NSString *path = @"/Users/HelloWorld/Desktop/test.txt" ;NSError *error;NSString *str1 = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error];NSLog (@"%@" , str1);if (error == nil ) { NSLog (@"读取文件成功:%@" , str1); } else { NSLog (@"读取文件失败:@error" , error); }
1 2 3 4 5 6 7 8 9 10 11 12 NSString *str = @"123456" ;NSString *path = @"/Users/HelloWorld/Desktop/test.txt" ;NSError *error;[str writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:&error]; if (error) { NSLog (@"写入失败:%@" , error); } else { NSLog (@"写入成功" ); }
NSString 的常用方法:
1 2 3 4 5 6 NSString *str = @"HelloWorld" ;NSLog (@"大写:%@" , [str uppercaseString]);NSLog (@"小写:%@" , [str lowercaseString]);NSLog (@"helloWorld 首字母变大写:%@" , [@"helloWorld" capitalizedString]);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 BOOL result = [@"abc" isEqualToString:@"abc" ];NSLog (@"%i" , result);NSComparisonResult result2 = [@"abc" compare:@"Abc" ];if (result2 == NSOrderedSame ) { NSLog (@"两个字符串的内容" ); } else if (result2 == NSOrderedAscending ) { NSLog (@"右边 > 左边" ); } else if (result2 == NSOrderDescending ) { NSLog (@"右边 < 左边" ); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 NSString *str = @"123456456.txt" ;NSLog (@"是否以12开头:%i" , [str hasPrefix:@"12" ]);NSLog (@"是否以txt结尾:%i" , [str hasSuffix:@"txt" ]);NSRange range = [str rangeOfString:@"345" ];if (range.location == NSNotFound ) { NSLog (@"没有找到" ); } else { NSLog (@"找到,范围是:%@" , NSStringFromRange (range)); } range = [str rangeOfString:@"456" options:NSBackwardsSearch ]; NSLog (@"从尾部开始搜索,找到,范围是:%@" , NSStringFromRange (range));[str rangeOfString:@"456" options:NSBackwardsSearch range:(NSRange )];
1 2 3 4 5 6 7 8 NSString *str = @"123456" ;NSLog (@"%@" , [str substringFromIndex:3 ]);NSLog (@"%@" , [str substringToIndex:3 ]);NSRange range = NSMakeRange (2 , 3 );NSLog (@"%@" , [str substringWithRange:range]);
1 2 3 NSString *str2 = @"1,2,3,4,5,6" ;NSArray *array = [str2 componentsSeparatedByString:@"," ];NSLog (@"%@" , array);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 NSMutableArray *components = [NSMutableArray array];[components addObject:@"Users" ]; [components addObject:@"HelloWorld" ]; [components addObject:@"Desktop" ]; NSString *path = [NSString pathWithComponents:components];NSLog (@"%@" , path);NSArray *cmps = [path pathConponents];NSLog (@"%@" , cmps);path = @"/Users/HelloWorld/test/test.txt" ; NSLog (@"%i" , [path isAbsolutePath]);NSLog (@"最后一个目录:%@" , [path lastPathComponent]);NSLog (@"%@" , [path stringByDeletingLastPathComponent]);NSLog (@"%@" , [path stringByAppandingPathComponent:@"abc" ]);
1 2 3 4 5 6 7 NSString *path = @"/Users/HelloWorld/test/test.txt" ;NSLog (@"扩展名:%@" , [path pathExtension]);NSLog (@"%@" , [path stringByDeletingPathExtension]);NSLog (@"%@" , [@"abc" stringByAppendingPathExtension:@"mp3" ]);
1 2 3 4 5 6 7 8 9 10 11 12 NSString *str = @"12" ;int a = [str intValue];NSLog (@"%i" , a);NSLog (@"length = %zi" , [@"我是字符串" , length]);unichar c = [@"abcd" characterAtIndex:0 ];NSLog (@"%c" , c);const char *s = [@"abc" , UTF8String];NSLog (@"%s" , s);
NSMutableString
NSString 是不可变的,不能删除字符或者添加字符。NSString 有一个子类 NSMutableString,称为“可变字符串”
创建可变字符串的常用方法:
- (id)initWithCapacity:(NSUInteger)capacity
+ (id)stringWithCapacity:(NSUInteger)capacity
capacity 只是一个最优值,字符串的大小并不仅限于所提供的容量,设置了 capacity,可以预分配一块内存来存储它,操作速度会快很多
当然,也可以使用创建 NSString 的方法来创建 NSMutableString,因为 NSMutableString 是 NSString 的子类,NSString 能用的方法,NSMutableString 都能使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 NSMutableString *str = [[NSMutableString alloc] initWithCapacity:10 ];[str setString:@"1234" ]; [str appendString:@"567" ]; [str appendFormat:@"age is %i and name is %s" , 21 , "mlb" ]; NSRange range = [str rangeOfString:@"age" ];[str replaceCharactersinRange:range withString:@"years" ]; [str insertString:@"abc" atIndex:2 ]; range = [str rangeOfString:@"567" ]; [str deleteCharactersInRange:range]; NSLog (@"%@" , str);[str release];
NSArray
用来存储对象的有序列表,它是不可变的
不能存储C语言中的基本数据类型,如 int、float、enum、struct,也不能存储 nil
1 2 3 4 5 6 7 8 9 10 NSArray *array = [NSArray array];array = [NSArray arrayWithObject:@"123" ]; array = [NSArray arrayWithObjects:@"a" , @"b" , @"c" , nil ]; unsigned int count = [array count];NSLog (@"%zi" , count);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 NSArray *array = [NSArray arrayWithObjects:@"a" , @"b" , @"c" , nil ];if ([array containsObject:@"bc" ]) { NSLog (@"包含了字符串bc" ); } NSString *last = [array lastObject];NSLog (@"last = %@" , last);NSString *str = [array objectAtIndex:1 ];NSLog (@"str = %@" , str);int index = [array indexOfObject:@"c" ];NSLog (@"index = %i" , index);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 Student *stu1 = [[Student alloc] init]; Student *stu2 = [[Student alloc] init]; Student *stu3 = [[Student alloc] init]; NSLog (@"stu1:%zi" , [stu1 retainCount]);NSArray *array = [[NSArray alloc] initWithObjects:stu1, stu2, stu3, nil ];NSLog (@"stu1:%zi" , [stu1 retainCount]);NSLog (@"count = %zi" , array.count);[array release]; [stu1 release]; [stu2 release]; [stu3 release];
NSArray 的比较:
比较两个集合内容是否相同- (BOOL)isEqualToArray:(NSArray *)otherArray
返回两个集合中第一个相同的对象元素- (id)firstObjectCommonWithArray:(NSArray *)otherArray
1 2 3 4 [array makeObjectsPerformSelector:@selector (function name)]; [array makeObjectsPerformSelector:@selector (function name:) withObject:@"123" ];
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 NSArray *array = [NSArray arrayWithObjects:@"a" , @"b" , @"c" , nil ];unsigned int count = array.count;for (int i = 0 ; i < count; i++) { id obj = [array objectAtIndex:i]; NSlog (@"%i - %@" , i, obj); } int i = 0 ;for (id obj in array) { NSLog (@"%i - %@" , i, obj); i++; } [array enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) { NSLog (@"%zi - %@" , idx, obj); if (idx == 1 ) { *stop = YES ; } }]; NSEnumerator *enumerator = [array objectEnumerator];id obj = nil ;while (obj = [enumerator nextObject]) { NSLog (@"obj = %@" , obj); } NSEnumerator *reverseEnumerator = [array reverseObjectEnumerator];NSArray *arr = [enumerator allObjects];NSLog (@"arr: %@" , arr);
1 2 3 4 5 6 7 8 9 10 11 12 NSArray *array = [NSArray arrayWithObjects:@"a" , @"b" , @"c" , nil ];NSArray *array2 = [array arrayByAddingObject:@"d" ];NSArray *array3 = [array arrayByAddingObjectsFromArray:[NSArray arrayWithObjects:@"e" , @"f" , nil ]];NSLog (@"array = %@" , array);NSLog (@"array2 = %@" , array2);NSLog (@"array3 = %@" , array3);NSArray *array4 = [NSArray arrayWithObjects:@"1" , @"2" , @"3" , nil ];NSRange range = NSMakeRange (1 , 2 );NSArray *array5 = [array4 subarrayWithRange:range];NSLog (@"array5 = %@" , array5);
1 2 3 4 5 6 7 8 9 10 11 NSArray *array = [NSArray arrayWithObjects:@"a" , @"b" , @"c" , nil ];NSString *str = [array componentsJoinedByString:@"-" ];NSLog (@"str = %@" , str);NSString *path = @"/Users/HelloWorld/test/array.xml" ;[array writeToFile:path atomically:YES ]; NSArray *array = [NSArray arrayWithContentsOfFile:path];NSLog (@"array = %@" , array);
1 2 3 4 5 6 7 NSArray *array = [NSArray arrayWithObjects:@"2" , @"3" , @"1" , @"4" , nil ];NSLog (@"array = %@" , array);NSArray *sortArray = [array sortedArrayUsingSelector:@selector (compare:)];NSLog (@"sortArray = %@" , sortArray);
Book.h:
1 2 3 4 5 @interface Book : NSObject @property (nonatomic , retain ) NSString *name;+ (id )bookWithName:(NSString *)name; @end
Book.m:
1 2 3 4 5 6 7 8 9 10 11 12 @implementation Book + (id )bookWithName:(NSString *)name { Book *book = [[[Book alloc] init] autorelease]; book.name = name; return book; } - (void )dealloc { [_name release]; [super release]; } @end
Student.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @class Book ;@interface Student : NSObject @property (nonatomic , retain ) NSString *firstName;@property (nonatomic , retain ) NSString *lastName;@property (nonatomic , retain ) Book *book;+ (id )studentWithFirstName:(NSString *)firstname lastname:(NSString *)lastname; + (id )studentWithFirstName:(NSString *)firstname lastname:(NSString *)lastname bookName:(NSString *)bookName; - (NSComparisonResult )compareStudent:(Student *)stu; @end
Student.m:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 @implementation + (id )studentWithFirstName:(NSString *)firstname lastname:(NSString *)lastname { Student *stu = [[[Student alloc] init] autorelease]; stu.firstName = firstname; stu.lastName = lastname; return stu; } + (id )studentWithFirstName:(NSString *)firstname lastname:(NSString *)lastname bookName:(NSString *)bookName { Student stu = [Student studentWithFirstName:firstname lastName:lastname]; stu.book = [Book bookWithName:bookName]; return stu; } - (NSComparisonResult )compareStudent:(Student *)stu { NSComparisonResult result = [self .lastName compare:stu.lastName] if (result == NSOrderedSame ) { result = [self .firstName compare:stu.firstName]; } return result; } - (void )dealloc { [_firstName release]; [_lastName release]; [_book release]; [super release]; } - (NSString *) description { return [NSString stringWithFormat:@"[%@ %@ - %@]" , self .lastName, self .firstName, self .book.name]; } @end
main.m:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 Student *s1 = [Student studentWithFirstName:@"LeBin" lastName:@"Mei" ]; Student *s2 = [Student studentWithFirstName:@"JiBin" lastName:@"Chen" ]; Student *s3 = [Student studentWithFirstName:@"BinBin" lastName:@"Chi" ]; Student *s4 = [Student studentWithFirstName:@"Bi" lastName:@"Xue" ]; Student *s5 = [Student studentWithFirstName:@"Bin" lastName:@"Xu" ]; NSArray *array = [NSArray arrayWithObjects:s1, s2, s3, s4, s5, nil ];NSArray *array2 = [array sortedArrayUsingSelector:@selector (compareStudent:)];NSLog (@"array2 = %@" , array2);NSArray *array3 = [array sortedArrayUsingComparator: ^NSComparisonResult (Student *obj1, Student *obj2) { NSComparisonResult result = [self .lastName compare:stu.lastName] if (result == NSOrderedSame ) { result = [self .firstName compare:stu.firstName]; } return result; }]; NSLog (@"array3 = %@" , array3);Student *s1 = [Student studentWithFirstName:@"LeBin" lastName:@"Mei" bookName:@"book1" ]; Student *s2 = [Student studentWithFirstName:@"JiBin" lastName:@"Chen" bookName:@"book2" ]; Student *s3 = [Student studentWithFirstName:@"BinBin" lastName:@"Chi" bookName:@"book1" ]; Student *s4 = [Student studentWithFirstName:@"Bi" lastName:@"Xue" bookName:@"book2" ]; Student *s5 = [Student studentWithFirstName:@"Bin" lastName:@"Xu" bookName:@"book3" ]; NSArray *array = [NSArray arrayWithObjects:s1, s2, s3, s4, s5, nil ];NSSortDescriptor *bookNameDesc = [NSSortDescriptor sortDescriptorWithKey:@"book.name" ascending:YES ];NSSortDescriptor *lastNameDesc = [NSSortDescriptor sortDescriptorWithKey:@"lastName" ascending:YES ];NSSortDescriptor *firstNameDesc = [NSSortDescriptor sortDescriptorWithKey:@"firstName" ascending:YES ];NSArray *descs = [NSArray arrayWithObjects:bookNameDesc, lastNameDesc , firstNameDesc , nil ];NSArray *array2 = [array sortedArrayUsingDescriptors:descs];NSLog (@"array2 = %@" , array2);
NSMutableArray
可变的 NSArray,NSArray 的子类,可以随意的添加或者删除元素
创建 NSMutableArray 的方法:
+ (id)arrayWithCapacity:(NSUInteger)numItems
- (id)initWithCapacity:(NSUInteger)numItems
也可以使用创建 NSArray 的方法来创建 NSMutableArray
当一个元素被加到集合中时,会执行一次 retain 操作;当一个元素从集合中移除时,会执行一次 release 操作;当集合被销毁时(调用了 dealloc),集合里的所有元素都会执行一次 release 操作(这个原则还适用于其他集合:NSDictionary/NSSet 等)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 NSMutableArray *array = [NSMutableArray arrayWithObject:@"1" ];[array addObject:@"2" ]; [array addObject:@"3" ]; NSLog (@"%@" , array);[array removeObject:@"2" ]; [array removeLastObject]; [array removeAllObject]; NSMutableArray *array = [NSMutableArray arrayWithObject:@"1" , @"2" , @"3" ];[array relaceObjectAtIndex:1 withObject:@"4" ]; NSLog (@"array = %@" , array);NSMutableArray *array = [NSMutableArray arrayWithObject:@"1" , @"3" , @"2" ];[array sortUsingSelector:@selector (compare:)]; NSLog (@"array = %@" , array);
NSDictionary 通过唯一的 key 找到对应的 value,类似于 Java 的 Map
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 NSDiationary *dict = [NSDiationary dictionaryWithObject:@"v" forKey:@"k" ];dict = [NSDiationary dictionaryWithObjectsAndKeys: @"v1" , @"k1" , @"v2" , @"k2" , @"v3" , @"k3" , nil ]; NSArray *objects = [NSArray arrayWithObjects:@"v1" , @"v2" , @"v3" , nil ];NSArray *keys = [NSArray arrayWithObjects:@"k1" , @"k2" , @"k3" , nil ];dict = [NSDiationary dictionaryWithObjects:objects forKeys:keys]; NSLog (@"%@" , dict);NSLog (@"count = %zi" , dict.count);id obj = [dict objectForKey];NSLog (@"obj = %@" , obj);NSString *path = @"/Users/HelloWorld/Desktop/dict.xml" ;[dict writeToFile:path atomically:YES ]; dict = [NSDiationary dictionalryWithContentsOfFile:path]; NSLog (@"dict = %@" , dict);NSArray *keys = [dict allKeys];NSLog (@"keys = %@" , keys);NSArray *object = [dict allValues];NSLog (@"objects = %@" , objects);objects = [dict objectsForKeys:[NSArray arrayWithObjects:@"k1" , @"k2" , nil ] notFoundMarker:@"not_found" ]; NSLog (@"objects = %@" , objects);NSDiationary *dict = [NSDiationary dictionaryWithObjectsAndKeys: @"v1" , @"k1" , @"v2" , @"k2" , @"v3" , @"k3" , nil ]; for (id key in dict) { id value = [dict objectForKey:key]; NSLog (@"%@ = %@" , key, value); } NSEnumerator *enumer = [dict keyEnumerator];id key = nil ;while (key = [enumer nextObject]) { id value = [dict objectForKey:key]; NSLog (@"%@ = %@" , key, value); } [dict enumerateKeysAndObjectsUsingBlock: ^(id key, id obj, BOOL *stop) { NSLog (@"%@ = %@" , key, obj); }];
NSDiationary 的内存管理
Student.h:
1 2 3 4 @interface Student : NSObject @property (nonatomic , retain ) NSString *name;+ (id )studentWithName:(NSString *)name; @end
Student.m:
1 2 3 4 5 6 7 8 9 10 11 12 13 @implementation Student + (id )studentWithName:(NSString *)name { Student *stu = [[[Student alloc] init] autorelease]; stu.name = name; return stu; } - (void )dealloc { NSLog (@"%@被销毁了" , _name); [_name release]; [super dealloc]; } @end
main.m:
1 2 3 4 5 6 7 8 9 Student *s1 = [Student studentWithName:@"student1" ]; Student *s2 = [Student studentWithName:@"student2" ]; Student *s3 = [Student studentWithName:@"student3" ]; NSDiationary *dict = [NSDiationary dictionaryWithObjectsAndKeys: s1, @"k1" , s2, @"k2" , s3, @"k3" , nil ];
NSMutableDiationary
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 NSMutableDictionary *dict = [NSMutableDictionary dictionary];Student *s1 = [Student studentWithName:@"student1" ]; Student *s2 = [Student studentWithName:@"student2" ]; [dict setObject:s1 forKey:@"k1" ]; NSLog (@"s1: %zi" , [s1 retainCount]);NSDiationary *others = [NSDiationary dictionaryWithObjectsAndKeys: @"v1" , @"k1" , @"v2" , @"k2" , @"v3" , @"k3" , nil ]; [dict addEntriesFromDictionary:other]; [dict removeAllObjects]; [dict removeObjectForKey:@"k1" ]; [dict removeObjectsForKeys:[NSArray arrayWithObject:@"k1" ]];
NSNumber NSNumber 可以将基本数据类型包装成对象,这样就可以间接将基本数据类型存进 NSArray、NSDictionary 等集合中
1 2 3 4 5 6 7 8 9 10 11 NSNumber *num = [NSNumber numberWithInt:10 ];NSLog (@"num = %@" , num);NSMutableArray *array = [NSMutableArray array];[array addObject:number]; NSNumber *num1 = [array lastObject];int n = [num1 intValue];NSLog (@"n = %i" , n);
NSNumber 的常用方法
- (char)charValue
- (int)intValue
- (double)doubleValue
- (BOOL)boolValue
- (NSString *)stringValue
- (NSComparisonResult)compare:(NSNumber *)otherNumber
- (BOOL)isEqualToNumber:(NSNumber *)number
NSValue NSNumber 是 NSValue 的子类,但 NSNumber 只能包装数字类型,NSValue 可以包装任意值。也就可以用 NSValue 包装结构体后加入 NSArray、NSDictionary 等集合中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 CGPoint point = CGPointMake (10 , 10 );NSValue *value = [NSValue valueWithPoint:point];NSMutableArray *array = [NSMutableArray array];[array addObject:value]; NSValue *value1 = [array lastObject];CGPoint point1 = [value1 pointValue];BOOL result = CGPointEqualToPoint (point1, point);NSLog (@"result = %i" , result);typedef struct { int year; int month; int day; } Date; Date date = {2014 , 11 , 27 }; char *type = @encode (Date);[NSValue value:&date, withObjCType:type]; Date date1; [value getValue:&date1]; NSLog (@"year = %i, month = %i, day = %i" , date1.year, date1.month, date1.day);
NSNull
集合中是不能存放 nil 值的,因为 nil 在集合中有特殊含义,但有时确实需要存储一个表示“什么都没有”的值,那么就可以使用 NSNull,它也是 NSObject 的一个子类
创建和获取 NSNull 的方法: + (NSNull *)null
1 2 NSNUll *n = [NSNull null];
NSDate NSDate 的静态初始化 TimeInterval 是秒
返回当前时间:+ (id)date
返回以当前时间为基准,然后过了 secs 秒的时间+ (id)dateWithTimeIntervalSinceNow:(NSTimeInterval)secs
返回以 2001/01/01 GMT 为基准,然后过了 secs 秒的时间+ (id)dateWithTimeIntervalSinceReferenceDate:(NSTimeInterval)secs
返回以 1970/01/01 GMT 为基准,然后过了 secs 秒的时间+ (id)dateWithTimeIntervalSince1970:(NSTimeInterval)secs
返回很多年以后的未来的某一天+ (id)distantFuture
返回很多年以前的某一天+ (id)distantPast
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 NSDate *date = [NSDate date];date = [NSDate dateWithTimeIntervalSinceNow:10 ]; date = [NSDate dateWithTimeIntervalSince1970:10 ]; date = [NSDate distantFuture]; NSLog (@"future date = %@" , date);date = [NSDate distantPast]; NSLog (@"future date = %@" , date);NSTimeInterval interval = [date timeIntervalSince1970];NSDate *date2 = [NSDate date];[date earlierDate:date2]; [date laterDate:date2]; NSDateFormatter *formatter = [[NSDateFormatter alloc] init];formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss" ; NSString *str = [formatter stringFromDate:date];NSLog (@"str = %@" , str);NSDate *date2 = [formatter dateFromString:@"2014-11-27 16:02:50" ];NSLog (@"date2 = %@" , date2);[formatter release];
NSObject 常用方法
判断是否为 @class 或者 @class 的子类的实例- (BOOL)isKindOfClass:(Class)@class
判断是否为 @class 的实例(不包括 @class 的子类)- (BOOL)isMemberOfClass:(Class)@class
判断对象是否实现了 @protocol 协议- (BOOL)conformsToProtocol:(Protocol)@protocol
判断这个类的对象是否拥有参数提供的方法+ (BOOL)instancesRespondToSelector:(SEL)@selector
判断对象是否拥有参数提供的方法- (BOOL)respondsToSelector:(SEL)@selector
延迟调用参数提供的方法,方法所需参数用 withObject 传入- (void)performSelector:(SEL)@selector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay
Person.h:
1 2 3 @interface Person : NSObject - (void )test; @end
Person.m
1 2 3 4 5 @implementation Person - (void )test { NSLog (@"Person 调用了 test 方法" ); } @end
Student.h:
1 2 3 4 5 6 @interface Student : Person - (void )test; - (void )test2:(NSString *)str; @end
Student.m:
1 2 3 4 5 6 7 8 9 10 @implementation Student - (void )test { NSLog (@"Student 调用了 test 方法" ); } - (void )test2:(NSString *)str { NSLog (@"Student 调用了 test2 方法,str = %@" , str); } @end
main.m:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Student *stu = [[[Student alloc] init] autorelease]; if ([stu isKindOfClass:[Person class ]]) { NSLog (@"stu 属于 Person 或者属于继承自 Person 的类" ); } BOOL result = [stu isMemberOfClass:[Person class ]];NSLog (@"result = %i" , result);[stu test]; [stu performSelector:@selector (test)]; [stu performSelector:@selector (test2:) withObject:@"123" ]; [stu performSelector:@selector (test2:) withObject:@"123" afterDelay:2 ];
反射
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 NSString *str = @"Person" ;Class class = NSClassFromString (str); Person *person = [[class alloc] init]; NSLog (@"%@" , person);[person release]; NSString *name = NSStringFromClass ([Person class ]);NSString *method = @"test" ;SEL selector = NSSelectorFromString (method); [person performSelector:selector]; NSString *selectorName = NSStringFromSelector (selector);[person release];
SEL SEL 其实是对方法的一种包装,将方法包装成一个 SEL 类型的数据,去找对应的方法地址,找到方法地址就可以调用方法。其实消息(常说的发送消息)就是 SEL。
方法的存储位置
每个类的方法列表都存储在类对象中
每个方法都有一个与之对应的 SEL 类型的对象
根据一个 SEL 对象就可以找到方法的地址,进而调用方法
SEL 类型的定义 typedef struct objc_selector *SEL;
SEL 对象的创建
1 2 SEL s = @selector (test); SEL s2 = NSSelectorFromString (@"test" );
1 2 3 4 5 6 NSString *str = NSStringFromSelector (@selector (test));Person *p = [Person new]; [p performSelector:@selector (test)];
每个方法的内部都有一个隐藏的 SEL 对象:_cmd
,代表当前方法
1 2 3 4 5 6 - (void )test { NSString *str = NSStringFromSelector (_cmd); NSLog (@"_cmd = %@" , str); }
copy 和 mutableCopy
一个对象使用 copy 或 mutableCopy 方法可以创建对象的副本
copy - 需要先实现 NSCopying 协议,创建的是不可变得副本(如 NSString、NSArray、NSDictionary)
mutableCopy - 需要先实现 NSMutableCopying 协议,,创建的是可变副本(如 NSMutableString、NSMutableArray、NSMutableDictionary)
深复制:内容拷贝,源对象和副本指向不同的两个对象。源对象引用计时器不变,副本计数器设置为 1
浅复制:指针拷贝,源对象和副本指向的是同一个对象。对象的引用计数器+1,其实相当于做了一次 retain 操作
只有不可变对象创建不可变副本(copy)才是浅复制,其他都是深复制
copy 语法的目的:改变副本的时候,不会影响到源对象
为自定义类添加复制功能
如果想自定义 copy,那么就必须遵守 NSCopying,并且实现 copyWithZone 方法
如果想自定义 mutableCopy,那么就必须遵守 NSMutableCopying,并且实现 mutableCopyWithZone:方法
以 copy 为例,建议用 [self class]
代替直接类名
1 2 3 4 5 - (id )copyWithZone:(NSZone *)zone { id copy = [[[self class ] allocWithZone:zone] init]; return copy ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 NSString *string = [[NSString alloc] initWithFormat:@"age is %i" , 21 ];NSMutableString *str = [string mutableCopy];NSLog (@"str: &zi" , [str retainCount]); NSLog (@"string: %zi" , [string retainCount]);NSLog (@"%i" , str == string);[str appendString@"123456" ]; NSLog (@"str = %@" , str);NSLog (@"string = %@" , string);[str release]; [string release]; NSString *s1 = [[NSString alloc] initWithFormat:@"age is %i" , 21 ];NSLog (@"s1: %zi" , [s1 retainCount]);NSString *s2 = [s1 copy ];NSLog (@"s1: %zi" , [s1 retainCount]);NSLog (@"%i" , s2 == s1);
Student.h:
1 2 3 4 5 6 7 8 @interface Student : NSObject <NSCopying >@property (nonatomic , cop)NSString *name;+ (id )studentWithName:(NSString *)name; @end
Student.m:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @implementation Student + (id )studentWithName:(NSString *)name { Student *stu = [[[Student alloc] init] autorelease]; stu.name = name; return stu; } - (id )copyWithZone:(NSZone *)zone { Student *copy = [[Student allocWithZone:zone] init]; copy .name = self .name; return copy ; } - (void )description { return [NSString stringWithFormat:@"[name = %@]" , _name]; } - (void )dealloc { [_name release]; [super release]; } @end
main.m:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Student *stu = [[[[self class ] alloc] init] autorelease]; NSMutableString *string = [NSMutableString stringWithFormat@"age is %i" , 21 ];stu.name = string; [string addendString:@"abcd" ]; NSLog (@"name = %@" , stu.name);NSLog (@"string = %@" , string);Student *stu1 = [Student studentWithName:@"student1" ]; Student *stu2 = [stu1 copy ]; stu2.name = @"stu2" ; NSLog (@"stu1 = %@" , stu1);NSLog (@"stu2 = %@" , stu2);[stu2 release];
类的本质 类本身也是一个对象,是一个 Class 类型的对象,简称类对象 Class 类型的定义:typedef struct objc_class *Class;
类名就代表着类对象,每个类只有一个类对象 例如: 利用 Class 创建 Person 类对象 利用 Person 类对象创建 Person 类型的对象
获取内存中的类对象:
1 2 3 4 5 6 7 8 Person *p1 = [[Person alloc] init]; Person *p2 = [[Person alloc] init]; Class c1 = [p1 class ]; Class c2 = [p2 class ]; Class c3 = [Person class ]; NSLog (@"c1 = %p, c2 = %p, c3 = %p" , c1, c2, c3);
获取的类对象(Class)可以调用类方法,比如 Person.h 中有一个名为 test 的类方法:
Person.m 中有实现该方法:
1 2 3 + (void )test { NSLog (@"调用了类方法 test" ); }
测试:
1 2 Class c = [p1 class ]; [c test];
类的加载和初始化: +load
在程序启动的时候会加载所有的类和分类,并调用所有类和分类的 +load
方法
先加载父类,再加载子类:也就是先调用父类的 +load
方法,再调用子类的 +load
方法
先加载原始类,再加载分类
不管程序运行过程有没有用到这个类,都会调用 +load
加载
+initialize
在第一次使用某个类时(比如创建对象等),就会调用一次 +initialize
方法
一个类只会调用一次 +initialize
方法,先调用父类的,再调用子类的
+ (void)load
方法在程序启动的时候,就会加载一次项目中所有的类和分类。类加载完毕后就会调用每个类和分类的 load 方法。只会调用一次
load 方法会从父类开始调用,再是子类,包括 Category 也会调用 load 方法+ (void)initialize
方法在第一次使用某个类的时候调用
initialize 方法也是先初始化父类,再是子类
description 方法
-description
方法:使用 NSLog 和 %@ 输出某个对象时,会调用对象的 -description
方法,并拿到返回值进行输出
+description
方法:使用 NSLog 和 %@ 输出某个对象时,会调用类对象的 +description
方法,并拿到返回值进行输
死循环陷阱:如果在 -description
方法中使用 NSLog 打印 self
构造方法 作用:用来初始化对象的方法,是一个对象方法,-
开头 重写构造方法的目的:为了让对象创建出来,成员变量就已经有一些固定的值 重写构造方法的注意点:
先调用父类的构造方法([super init])
再进行子类的内部成员变量的初始化
例如: 重写 -init
方法:
1 2 3 4 5 6 7 8 9 10 11 - (id )init { self = [super init]; if (self != nil ) { _age = 10 ; } return self ; }
父类的属性交给父类方法去处理,子类方法处理子类自己的属性 自定义构造方法的规范:
一定是对象方法,一定以 -
开头
返回值一般是 id 类型
方法名一般以 init 开头
NSLog 输出的一些补充(都是两个下划线 _
):
1 2 3 4 5 6 7 NSLog (@"%s\n" , __func__);NSLog (@"%d" , __LINE__);printf("%s\n" , __FILE__);
ARC ARC 的判断准则:只要没有强指针指向对象,就会释放对象 ARC 的特点:
不允许调用 release、retain、retainCount
允许重写 dealloc,但是不允许调用 [super deallo]
@property 的参数:
strong:成员变量时强指针,相当于原来的 retain(适用于OC对象类型)
weak:成员变量时弱指针,相当于原来的 assign(适用于OC对象类型)
assign:适用于非 OC 对象类型
指针分两种:
强指针:默认情况下,所有的指针都是强指针 __strong
弱指针:__weak
Xcode 是默认使用 ARC 的,如果某个 .m 文件真的不想使用 ARC,可以通过以下步骤来不适用 ARC: 选择 Xcode 右侧项目树的根,然后是 TARGETS -> Build Phases -> Compile Sources ,下拉,选择目标 .m 文件,回车或者双击,弹出输入框,输入 -fno-objc-arc
回车,就可以了,如下图所示:
如果开发环境是非 ARC 的,想要使用 ARC 的,将上面的 -fno-objc-arc
改成 -f-objc-arc
就可以了。
ARC 循环引用 当两端循环引用的时候,解决方案如下:
ARC 1端用 strong,另一端用 weak
非ARC 1端用 retain,另一端用 assign
例如: 在使用 ARC 下,有两个类:Person、Dog,如下: Person.h:
1 2 3 4 5 6 @class Dog ;@interface Person : NSObject @property (nonatomic , strong ) Dog *dog;@end
Person.m:
1 2 3 4 5 @implementation Person - (void )dealloc { NSLog (@"Person ---- dealloc" ); } @end
Dog.h:
1 2 3 4 5 6 @class Dog ;@interface Dog : NSObject @property (nonatomic , strong ) Person *person;@end
Dog.m:
1 2 3 4 5 @implementation Dog - (void )dealloc { NSLog (@"Dog ---- dealloc" ); } @end
main.m:
1 2 3 4 5 6 Person *p = [[Person alloc] init]; Dog *d = [[Dog alloc] init]; p.dog = d; d.person = p;
如图所示:
如图所示:
如果将 Dog 中的 person 属性改成 weak:
1 @property (nonatomic , weak ) Person *person;
那么,上面第二种情况就变成了如下图所示:
这样的话,当程序运行结束,被回收的就是 Person 对象,既然 Person 对象被回收了,那么 Dog 对象就没有了强指针,也会被回收了。