------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
1. ARC
1) 指针分类:
① 指针:默认的情况下,所有的指针都是强指针,关键字strong
② 弱指针:__weak关键字修饰的指针(两个下划线而且中间每空格)
_ _weak Person *p;
2) ARC了解:是编译器的特性,当ARC开启后,编译器会自动在代码合适的地方插入retain,release和autorelease。ARC与其他语言的“垃圾回收”机制不同,ARC是编译器特性,“垃圾回收”是运行时特性
3) ARC工作原理及判断准则
只要没有强指针指向对象,对象就会被释放。
当使用ARC的时候,不要使用“引用计数器”,因为判断标准变了。
4) ARC机制:
① 判断是否是ARC机制
查看项目信息 不能使用retain,release,autorelease,retaincount,在dealloc方法中不能使用[super dealloc]
② 使用
正常创建对象,不用手动释放对象。
2. ARC下单对象内存管理
1) 只要没有强指针指向对象,对象就会立即释放内存空间
2) 如果只有弱指针指向一个对象,则会把弱指针赋值为nil
3. ARC下多对象内存管理
如果A对象有强指针指向B对象,则A对象释放后B对象才能释放。
4. ARC的循环引用
A对象有强指针指向B对象,B对象有强指针指向A对象,则两个对象的内存空间都不会被释放。
解决方法:只需要把其中一个强指针改为弱指针,两个对象就都能释放了。
5. ARC的@property参数
assign,strong(强指针,用于OC其他的对象),weak(弱指针,用于UI控件),copy
ARC:
strong,用于OC对象,相当于MRC中的retain
weak,用于OC对象,相当于MRC中的assign
assign:用于基本数据类型,跟MRC的assign一样
copy:一般用于NSString,跟MRC的copy一样
6. ARC的使用总结
1) 特点:
① 允许调用release,retain,retainCount
② 允许重写dealloc,不允许调用[super dealloc];
③ @property参数多了strong和weak,分别表示成员变量为强指针和弱指针
2) 使用注意
① ARC中,只要弱指针指向的对象不存在了,就把弱指针赋值为nil
② 在@property中要生成强指针或弱指针,使用strong或weak就可以了,不用加两个下划线
7. MRC转换为ARC
Edit--->convert--->oc to ARC
8. ARC与非ARC的兼容
项目名称---->当前target---->build phases---->compilesources--->在对应的类右侧双击----->输入-fno-objc-arc----->点击任何其他地方设置成功。
非ARC兼容ARC的设置为-f-objc-arc
9. Category概念及使用流程(理论)
1) 分类的概念
译为分类/类别/类目(一般为分类),Category是OC中特有的语法。
2) 分类的作用:
① 在不修改原有类的基础上增加新的方法
② 一个庞大的类可以分模块开发
③ 一个庞大的类可以由多个人来编写,更有利于团队合作
3) 使用分类的目的
① 对现有类进行扩展
比如,你可以扩展Cocoatouch框架中的类,你在类别中增加的方法会被子类所继承,而且在运行时跟其他方法没有区别
② 作为子类的替代手段
不需要定义和使用一个子类,可以通过类别直接项已有的类里增加方法
③ 对类中的方法归类
利用category把一个庞大的类划分为小块来分别进行开发,从而更好地对类中的方法进行更新和维护
4) 使用分类的步骤
① 先声明分类--->实现分类--->使用分类
② 命名规则:类名 +扩展的方法名。比如:”NSString+countNum”
③ 分类的接口声明与类的定义十分相似,但分类不继承父类,只需要带有一个括号,标明给分类的主要用途。
10. Category的声明和实现
创建分类文件步骤
右键点击类名,选择new file
选择oc文件,点next
点next确定finish,创建完成,在项目列表中会看到创建的分类
1) 声明格式:
@interface 待扩展的类名 (分类的名称)
@end
比如:给Student类扩展一个study分类
#import "Student.h"
@interface Student (study)
-(void)studyC;
@end
2) 实现格式:
@implementation 待扩展的类 (分类的名称)
@end
比如:上面study分类的实现,注意头文件格式
#import "Student+study.h"
@implementation Student (study)
-(void)studyC{
NSLog(@"studyC");
}
@end
3) 分类中方法的使用
和使用类中方法一样,[对象名 方法名];
要注意引入分类的头文件和原有类的头文件
#import <Foundation/Foundation.h>
#import "Student.h"
#import "Student+study.h"//引入分类的头文件
int main(int argc, const char * argv[]) {
@autoreleasepool {
Student *stu = [Student new];
//分类中方法的调用
[stu studyC]
}
return 0;
}
11. 分类的使用注意事项
1) 分类只能增加方法,不能增加成员变量
① 能定义实例变量,也不能使用@property
②
2) 分类可以访问原来类中的成员变量
3) 如果分类和原来类出现同名的方法,优先调用分类中的方法,原来类中的方法会被忽略。
4) 当在多个类别中有同名方法时,执行的是最后编译的类别中的方法,编译顺序如下,其顺序可以通过拖动进行调整。
同名方法优先级:
分类>原类
最后编译的分类 > 其他分类
12. 分类的非正式协议(面试考点)
非正式协议就是类别,凡是NSObject或其子类Foundation框架中的类增加的类别,都是非正式协议。
应用举例:
统计一个字符串中阿拉伯数字的个数。
#import <Foundation/Foundation.h>
@interface NSString (countNum)
-(void)countNumForString;
@end
#import "NSString+countNum.h"
@implementation NSString (countNum)
-(void)countNumForString{
int count;//进行计数
//循环控制
for (int i=0; i<self.length; i++) {
//去除字符串的每一个字符
//characterAtIndex 取得字符串某个位置的字符
unichar ch = [self characterAtIndex:i];
//判断是否是阿拉伯数字
if(ch >='0' && ch <='9'){
//是 +1
count++;
}
}
NSLog(@"字符串%@中有%d个阿拉伯数字",self,count);
}
@end
#import <Foundation/Foundation.h>
#import "NSString+countNum.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSString *str = @"ansdfk3298u2387rbhj";
[str countNumForString];
NSString *str2 = [NSStringstringWithFormat:@"dsffdg4434131geger434"];
[str2 countNumForString];
}
return 0;
}
运行结果:
13. 分类延展(面试)
1) 延展的概念
延展类别又称为扩展(Extendsion),Extendsion是category的一个特例。
其名字为匿名(为空)
@interface MyClass()
{
int _value;
}
-(void)setValue:(int)value;
@end
这种写法的类别叫匿名类别,又叫类扩展,所谓的扩展,其实就是为一个类添加额外的原来没有的变量、方法或者合成属性。
2) 可以在延展中定义实例变量,在延展中定义方法,在原类中进行实现,都是相对私有的,不能被子类访问
3) 延展的实现:
通过延展来实现方法的私有,延展的头文件独立。这种方法不能实现真正的方法私有,当在别的文件中引入延展的头文件,那么在这个文件中定义的类的对象就可以直接调用在延展中定义的所谓私用的方法。
延展文件的创建方法基本和分类相同,除了下面这里选择不同
创建完成后会看到延展文件
14. 类别和类延展的区别
1) 类别中只能增加方法
2) 类扩展不仅可以增加方法,还可以增加实例变量(或者合成属性),只是该实例变量默认是私有的
3) 类扩展中声明的方法没被实现,编译器会报警,但是在类别中的方法没被实现的话,编译器不会有任何警告。这时因为类扩展是在边一阶段被添加到类中,而类别是在运行时添加到类中。
4) 类扩展不能像类别那样拥有独立的实现部分,即类扩展所声明的方法必须依托对应类的实现部分来实现。
5) 定义在.m文件中的类扩展方法为私有的,定义在.h文件中的类扩展方法为公有的。类扩展是在.m文件中声明私有方法的非常好的方式。
6) 直接在原类中声明延展的方式
① 在.h文件中声明,此时的实例变量和方法都是公有的,可以被子类继承
#import <Foundation/Foundation.h>
@interface Person : NSObject
{
NSString *_name;
}
@end
@interface Person ()
@property (nonatomic,assign)int foot;
-(void)walk;
@end
② 在.m文件中或者新创建的延展文件中声明,此时的实例变量和方法都是私有的,不能被子类继承
#import "Person.h"
@interface Person ()
@property (nonatomic,assign)int hand;
-(void)write;
@end
@implementation Person
-(void)write{
NSLog(@"write");
}
@end
15. Block
1) block的概念
block类型是一个C级别的语法和运行机制。它与标准的C函数类似,不同之处在于,它除了有可执行代码以外,它还包含了与堆、栈 内存绑定的变量。因此,block对象包含着一组状态数据,这些数据在程序执行时用于对行为产生影响。
可以用block来写一些可以传到API中的函数语句,可选择性地存储,并可以使用多线程。作为一个回调,block特别的有用,因为block既包含了回调期间的代码,又包含了执行期间需要的数据。
由于OC和C++都是衍生自C,block被设计为可同时兼容这三种语言。
2) block的基本用法。
Block的基本格式:
返回值类型(^block名称)(参数类型列表)=^(形参列表){代码块};
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
/*
1.无参无返回值
格式:
void(^block变量名)() = ^(){
代码块;
};
*/
void (^myBlock1)() = ^(){
NSLog(@"This is myBlock1");
};
myBlock1();
/*
2.无参有返回值
格式:
返回值类型(^block变量名)()=^(){
代码块;
return 返回值;
};
*/
int (^myBlock2)() = ^(){
NSLog(@"This is myBlock2");
return 0;
};
myBlock2();
/*
3.有参无返回值
格式:
void (^block变量名)(int ,int )=^(int a,int b){
代码块;
};
*/
void (^myBlock3)(int ,int )=^(int a,intb){
NSLog(@"This is myBlock3,a + b=%d",a+b);
};
myBlock3(12,21);
/*
4.有参有返回值
格式:
返回值类型(^block变量名)(参数类型列表)=^(形参列表){
代码块;
return 返回值;
};
*/
int (^myBlock4)(int ,int ) = ^(int x,int y){
NSLog(@"This is myBlock4");
return x+y;
};
int sum = myBlock4(1,1);
NSLog(@"1 + 1 =%d",sum);
//block是一个变量,可以重新赋值
myBlock4 = ^(int a,int b){
return a*b;
};
NSLog(@"a * b =%d",myBlock4(10,10));
}
return 0;
}
3) block的typedef
//block的typedef
//定义格式:
//typedef 返回值类型 (^新的别名)(参数类型列表);
//这样就把这个别名定义为一种类型,可以去定义变量。
//如果是无参的,定义变量时形参列表的括号可以省去,直接用^{};
//别名 变量名;
/*
1.有参有返回值
格式:
typedef 返回值类型 (^newBlock1)(参数类型列表);
*/
typedef int(^Block1)(int ,int);
Block1 b1 = ^(inta,int b){
return a+b;
};
NSLog(@"1+2=%d",b1(1,2));
/*
2.有参无返回值
格式:
typedef void(^newBlock2)(参数类型列表);
*/
typedef void(^Block2)(int ,int);
Block2 b2 = ^(inta,int b){
NSLog(@"%d+%d=%d",a,b,a+b);
};
b2(2,3);
/*
3.无参有返回值
格式:
typedef 返回值类型 (^newBlock3)();
*/
typedef int(^Block3)();
Block3 b3 = ^(){
NSLog(@"Thisis Block3");
return 0;
};
b3();
/*
4.无参无返回值
格式:
typedef void(^newBlock4)();
*/
typedef void(^Block4)();
Block4 b4 = ^(){
NSLog(@"This is Block4");
};
b4();
//也可以同时定义多个变量
Block4 b5,b6,b7;
4) block访问外部变量(面试题)
① 第一种访问形式
void test3(){
//block 访问外部变量1
int m = 5;
NSLog(@"1.--->%p",&m);
m = 6;
//block以const的方式,把外部变量拷贝到其所在的内存空间(堆区)
//拷贝的是block定义之前变量的最终值,(这里是m=6),block定义之后对变量的重新赋值不会改变block拷贝的变量值.(block中m=6,后面的m=4和m=7修改的都是栈区的值)
void (^block)() = ^{
NSLog(@"2.m =%d",m);
NSLog(@"3.--->%p",&m);
};
m = 4;
NSLog(@"4.m = %d",m);
NSLog(@"5.--->%p",&m);
block();
m = 7;
NSLog(@"4.m =%d",m);
NSLog(@"5.--->%p",&m);
}
运行结果
② 第二种访问形式
void test4(){
//block 访问外部变量2
__block int m = 5;
NSLog(@"1.--->%p",&m);
//使用__block修饰变量,指示不再以const的方式进行拷贝,可以在block中进行赋值。
//在block定义之后访问的变量,都是block拷贝的变量,而不是栈区的变量。
void (^block)() = ^{
m = 10;
NSLog(@"4.m =%d",m);
NSLog(@"5.--->%p",&m);
};
m = 4;
NSLog(@"2.m =%d",m);
NSLog(@"3.--->%p",&m);
block();
m = 7;
NSLog(@"6.m = %d",m);
NSLog(@"7.--->%p",&m);
}
运行结果:
注意:静态变量和全局变量,在加和不加__block都会直接引用变量地址,也就意味着可以修改变量的值。
在不加__block 的情况下:
① 全局block:定义在函数外面的block是global的,另外如果函数内部的block没有捕获任何自动变量,那么它也是全局的。
② 栈block:区别为 是否引用了外部变量。
③ 堆block:则是对栈block copy而来,对全局block copy不会有任何作用,返回的依然是全局block。
5) block的使用场景
假设程序员每天的工作都是基本固定的,用block实现上班5天的具体行动
//用bloc作形参
void work(void (^workBlock)()){
NSLog(@"起起床");
NSLog(@"洗洗脸");
NSLog(@"去车站");
NSLog(@"坐坐车");
workBlock();
NSLog(@"去车站");
NSLog(@"坐车回家");
NSLog(@"吃吃饭");
NSLog(@"睡睡觉");
}
void workDay(int n){
//给workBlock起别名
typedef void(^workBlock)();
workBlock w;
switch (n) {
case 1:
w = ^{
NSLog(@"了解项目");
};
break;
case 2:
w = ^{
NSLog(@"分析项目");
};
break;
case 3:
w = ^{
NSLog(@"写项目");
};
break;
case 4:
w = ^{
NSLog(@"调试项目");
};
break;
case 5:
w = ^{
NSLog(@"离职");
};
break;
default:
break;
}
//调用工作方法
work(w);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
//循环控制每一天
for (int i=1; i<=5;i++) {
workDay(i);
}
}
return 0;
}
6) block作为函数的返回值
① 因为函数的返回类型必须是一个类型,而block是一个变量,所以使用typedef定义一个新的block类型
typedef void (^newType)();
② 使用新类型作为函数的返回值
newType test(){
newTypen = ^{
NSLog(@”hello”);
};
returnn;
}
③ 定义block类型的变量接收函数的返回值
newType n1 = test();
④ 执行block
n1();
7) block使用技巧和注意
① 使用助记符inlineBlock自动填充block框架,自定义助记符。
② 当block作为形参时,如果block有返回值,则实参处需要写上返回值类型。
test(^int(int x,int y)){}
调用时:
test(^int(int x,int y){
returnx+y;
});