ARC概念及原理
1、指针分类
1)强指针:默认下,所有的指针都是强指针,关键字strong
2)弱指针:__weak关键字修饰的指针 (两个下划线_之间没有空格,看似一条线)
2、什么是ARC
Automatic Reference Counting,自动引用计数。使用ARC,不需要用retain,release和autorelease等关键字,编译器会自动插入这些关键字,所以底层还是MRC实现的
ARC与其他语言的“垃圾回收”机制不同:1)ARC:编译器特性
2)垃圾回收:运行时特性
3、ARC工作原理及判断准则
原理:ARC是OC得编译器特性,不是运行时特性或垃圾回收机制,ARC做的只不过是在代码编译时为你自动在核实的位置插入release或autorelease
ARC的判断准则:只要没有强指针指向对象,对象就会被释放
注意:当时使用ARC时,要暂时忘记“引用计数器”(retainCount),因为判断的标准变了
ARC下,循环引用问题:一端使用weak,一端使用strong
在ARC下,@property的set方法参数
没有retain,使用weak和strong
1、原子性和读写,和MRC下一样
2、MRC ARC
assign assign
retain strong(强指针)weak(弱指针)
copy copy
一般在UI控件使用weak,其他OC对象用strong
ARC特点总结
1、不允许调用release,retain和retainCount
2、允许重写dealloc方法,但不允许调用[super dealloc];
3、@property的set方法参数
OC对象:strong相当于retain
weak相当于assign
非OC对象(基本类型):assign
ARC注意事项
1、ARC中,只要弱指针指向的对象不在了,就直接把弱指针左清空(赋值为nil)操作
2、__weak Person *p = [Person new]; // 不合理,对象一创建就被释放掉,对象释放之后,指针设置为nil
ARC的兼容和转换
1、ARC模式下如何兼容非ARC的类
TARGETS-》Build Phases-》Compile sources-》选择MRC的类,双击-》写入参数-fno-objc-arc
转变为非ARC:-fno-objc-arc
转变为ARC:-f-objc-arc
2、MRC转换ARC
Edit->Refactor->Convert to object-c ARC->choose targets->左边ARC 右边MRC 对比->save
分类(Category)概念及使用流程
1、分类的概念及作用
category有很多翻译:分类、类别、类目
OC特有的语法,其他语言没有的语法
分类的作用:1)在不修改原有类的基础上增加新的方法
2)一个庞大的类可以分模块开发
3)一个庞大的类可以由多个人来编写,更有利于团队合作
使用分类的目的:1)对现有类进行扩展:
比如,你可以扩展Cocoa touch框架的类,你在类别中增加的方法会被子类多继承,而且在运行时跟
其他的方法没有区别
2)作为子类的替代手段:
不需要定义和使用一个子类
3)对类中的方法归类:
利用category把一个庞大的类划分为小块来分别进行开发,从而更好地对类的方法进行更新和维护
使用分类的步骤:先声明分类—》实现分类—》使用分类
注意:
1)分类的命名规则:类名+扩展方法
2)分类的接口声明与类的定义十分相似,但分类不继承父类,只需要带一个括号,表明该分类的主要用途
2、分类的声明和实现
使用步骤:
1)声明一个分类
格式:@interface 待扩展的类名 (分类的名称)
@end
//
给
Person
类增加新的方法
@interface
Person (base)
//
吃
-(
void
) eat;
//
跑
-(
void
) run;
@end
2)实现这个分类
格式:@implementation 待扩展的类 (分类的名称)
@end
//Person
类新增方法的实现
@implementation
Person (base)
//
吃
-(
void
) eat
{
NSLog
(
@"
吃
"
);
}
//
跑
-(
void
) run
{
NSLog
(
@"
跑
"
);
}
@end
3)使用分类中的方法
和类的一模一样
创建分类category文件,实现多人开发:new File->Object-C File->File:分类名称
type:category
class:待扩展的类
3、分类的注意事项
1)分类只能增加方法,不能增加成员变量、@property
2)分类的方法中可以访问原来的成员变量
3)在分类中存在和类中同名的方法,优先访问分类
4)在多个类别中,有同名的方法时,执行的是最后编译的哪个类别的同名方法(在Compile sources里面看,可以改变编译顺序)
分类 > 原类
最后编译的 > 分类
分类(category)非正式协议
非正式协议:通常定义为Foundation中NSObject类或它的子类的类别,本质上是类别
characterAtIndex方法: 取得字符串对应位置的字符,返回的是unichar类型
1、传一个字符串
-(
void
)countNum:(
NSString
*) str {
// 计数
int count = 0 ;
// 循环控制
for ( int i = 0 ; i < str. length ; i++) {
// 取得字符串的每一个字符 unichar 类型
unichar ch = [str characterAtIndex :i];
// 使用 ‘’ ,判断是否是阿拉伯数字
if (ch >= '0' && ch <= '9' ) {
count++;
}
}
NSLog ( @" 字符串 %@ 有 %d 个数字 " , str, count);
// 计数
int count = 0 ;
// 循环控制
for ( int i = 0 ; i < str. length ; i++) {
// 取得字符串的每一个字符 unichar 类型
unichar ch = [str characterAtIndex :i];
// 使用 ‘’ ,判断是否是阿拉伯数字
if (ch >= '0' && ch <= '9' ) {
count++;
}
}
NSLog ( @" 字符串 %@ 有 %d 个数字 " , str, count);
}
2、使用self,更简洁
-(
void
)countNum {
// 计数
int count = 0 ;
// 循环控制
for ( int i = 0 ; i < self . length ; i++) {
// 取得字符串的每一个字符 unichar 类型
unichar ch = [ self characterAtIndex :i];
// 使用 ‘’ ,判断是否是阿拉伯数字
if (ch >= '0' && ch <= '9' ) {
count++;
}
}
NSLog ( @" 字符串 %@ 有 %d 个数字 " , self , count);
// 计数
int count = 0 ;
// 循环控制
for ( int i = 0 ; i < self . length ; i++) {
// 取得字符串的每一个字符 unichar 类型
unichar ch = [ self characterAtIndex :i];
// 使用 ‘’ ,判断是否是阿拉伯数字
if (ch >= '0' && ch <= '9' ) {
count++;
}
}
NSLog ( @" 字符串 %@ 有 %d 个数字 " , self , count);
}
分类(category)的延展
延展类别又称为扩展(Extendsion),Extendsion是category的一个特例
其名字为匿名(为空),并且新添加的方法一定要予以实现(category没有这个限制)。
@interface XXX ()
{
//增加的变量等
}
@end
这种写法的类别叫匿名类别,又叫扩展。所为扩展,就是为一个类增加原来没有的比那两、方法或者合成属性。
//
类的延展
/
扩展
/
延展分类
// 特点:
//1 )可以在延展中,增加变量
//2 )不可以在 @implementation 类名 () ---> 实现匿名类别的方法 , 在类的实现中去实现
// 特点:
//1 )可以在延展中,增加变量
//2 )不可以在 @implementation 类名 () ---> 实现匿名类别的方法 , 在类的实现中去实现
//3
)可以直接将匿名类别放在
.m
文件中,实现新增方法的相对私有化,但是引入头文件,依旧可以使用
//4
)可以在
new File->Object-C File->File:
新增的方法名
// type : Extendsion
// class :待扩展的类
// type : Extendsion
// class :待扩展的类
//
以后不管是实现还是使用直接调用头文件,相对私有化(在自身类,不在子类或其它类)
@interface
Person
()
//
匿名,匿名类别
{
float _weight;
}
-( void )run;
{
float _weight;
}
-( void )run;
@end
类别和类扩展的区别:
1、类扩展不仅可以增加方法,还可以增加实例变量,只是默认为私有类型的
2、新增的方法如不实现,类扩展会报警,而类别不会警告。类扩展是在编译阶段被加入到类中,而类别是运行时添加类中
3、不可以在@implementation 类名() --->实现匿名类别的方法,在类的实现中去实现
4、在.m中的类扩展为私有的,在.h中的为共有的。类扩展是在.m文件中声明私有方法的一种非常好的方式
block的概念及基本使用
1、block的基本概念
用^操作符来声明一个block变量
block同函数一样,分为有参无返回值,有参有返回值,无参无返回值,无参有返回值
block定义格式:返回类型 (^block名)(参数类型及个数) = ^(形参列表){
代码块
// 当有返回值时,必须写return语句
};
参数类型及个数:可只声明类型
形参列表:必须写类型+参数名,当没有参数时,可省略此括号。
block使用格式:block名( );
block的typedef
1、函数指针
返回值类型 (*指针名)(形参列表)
技巧:1)把函数的声明拷贝过来
2)把函数名替换为(*函数指针变量名)
3)形参名可写可不写
用法:指针名 = 函数名;
函数指针别名:typedef 返回值类型(*别名)(形参列表);
别名 f1,f2;//f1,f2的实际类型就是
类型(*f1)(形参列表);
2、block的typedef
格式:typedef 返回值类型 (^新别名)(形参列表);
//block
别名
typedef void (^myBlock)();
myBlock b1;
b1 = ^{
NSLog ( @"phantom" );
};
typedef void (^myBlock)();
myBlock b1;
b1 = ^{
NSLog ( @"phantom" );
};
b1();
3、block访问外部变量
1)可以访问,但是地址不一样
int
m =
10
;
NSLog(@"m = %d", m);
NSLog
(
@"m->addr = %p"
, &m);
//
栈区,高地址
//
当定义
block
的时候,
block
会把外部变量的值复制一份存放到
block
所在的内存中
void
(^block1)() = ^{
NSLog
(
@"in block m = %d"
, m);
//
可以访问
m
的值
NSLog
(
@"in block m->addr = %p"
, &m);
//
堆区,后六位改变;常量区,后四位
};
block1();
2)m的值不能被修改(m = 100;),以const的方式拷贝过去的。但是可以在代码块里,重新定义int m = 100;//栈区
3)block块内部变量是只读的,若加__block int m = 10;则可以在block块里重新赋值(m = 100;),然后不管在块内还是外部m的值都是100
注意:1)全局block:定义在函数外面的block是global;另外如果函数内部的block,但是没有捕获任何自动变量,那么也是全局的
2)栈block:是否引用了外部变量
3)堆block:则是对栈block copy而来。对全局block copy不会起任何作用,返回的依然是全局block
4、block应用
1)
block可以作为函数参数,block类型的变量workBlock,则传递形式void (^workBlock)()
2)block作为函数返回值:1、使用typedef给block定义新的类型
2、用新定义的类型作为函数返回值
3、定义block变量接受函数返回的结果
4、执行block
#import
<Foundation/Foundation.h>
typedef
void
(^newBlock)();//重新定义新的类型
newBlock test() {
newBlock w1 = ^{
NSLog ( @"XXX" );
};
return w1;//返回w1
}
int main( int argc, const char * argv[]) {
int main( int argc, const char * argv[]) {
@autoreleasepool {
newBlock n1 = test();//n1接收得w1
n1();
//执行w1
}
return 0 ;
return 0 ;
}
5、block的使用技巧
1)助记符:inlineBlock
2)创建自己的助记符,{},<##>,删除delete
3)可以在形参部分,加上参数名称,方便下面调用