---------------------- Java培训、.Net培训、Android培训、IOS培训、期待与您交流! ----------------------
1. Category
a. 简介
(1) 在不改变原来类模型的前提下,给类扩充一些方法 : a. 继承 b. Category
(2) 继承 和 Category 区别 :
1) 使用思想不同,继承是子类在父类的基础之上拓展功能,子类与父类的逻辑关系明显,Category多用于团队分模块开发,在现有基础之上增加新功能。
2) Category 不能修改成员变量的数目,而继承中子类可以增加成员变量的数目
3) Category重写原生方法后可能会导致原生方法无法访问,而继承重写父类方法仍然可以访问父类的方法。
b. 简单使用
// Person.h
#import <Foundation/Foundation.h>
@interface Person : NSObject
- (void) test();
@end
// Person.m
#import "Person.h"
@implementation Person
- (void) test
{
NSLog(@"test...");
}
@end
// Person+Person1.h
#import "Person.h"
@interface Person (Person1)
- (void) test1();
@end
// Person+Person1.m
#import "Person+Person1.h"
@implementation Person (Person1)
- (void) test1
{
NSLog(@"test1...");
}
@end
// main.m
#import "Person.h"
#import "Person+Person1.h"
int main(){
Person *p = [[Person alloc] init];
// 优先去分类中查找,然后再去原来类中找,最后再去父类中找
[p test]; // test...
[p test1]; // test1...
return 0;
}
c. 总结
(1) Category 好处是一个庞大的类可以分模块来开发,更利于团队开发
(2) 类对象调用方法的时候优先去分类中查找,然后再去原来类中找,最后再去父类中找
(3) 不建议覆盖原有的方法,因为会导致源有方法无法使用。
(4) 覆盖后该使用哪个方法决定于编译的优先级,查看编译的优先级:build phases->compile sources查看优先级,分类之间同名方法的优先级取决与分类的.m文件编译的顺序,比较靠后的优先级比较高
2. NSLog输出补充
a. 预处理宏符号
关键字 | NSLog中的占位符 | 描述 |
__func__ | %s | 当前方法签名 |
__LINE__ | %d | 此代码在源文件中的第几行 |
__FILE | %s | 该源文件的全路径 |
__PRETTY_FUNCTION__ | %s | 功能跟 __func__ 一样,不过此符号用于C++源文件中 |
b. 如何获得常用的输出字符串
表达式 | 占位符 | 描述 |
NSStringFromSelector(__cmd) | %@ | 当前方法签名字符串 |
NSStringFromClass([self class]) | %@ | 当前类名字符串 |
[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] | %@ | 当前源文件的名字字符串 |
[NSThread callStackSymbols] | %@ | 获得程序运行时栈的可读设置数组,只适用于调试 |
3. SEL
a. SEL类型的定义
OC提供了一种新类型数据对类方法进行操作,源码中的原生定义如下:
typedef struct objc_selector *SEL;
a. 类方法存储位置
对象调用方法都是通过isa指针去类对象中寻找,因此对象方法存储的位置是类对象中 :
(1) 每个类的方法列表都存储在类对象中
(2) 每个方法都有一个与之对应的SEL类型的对象
(3) 根据一个SEL对象就可以找到方法的地址,进而调用方法
c. SEL对象的创建
SEL s = @selector(test);
SEL s2 = NSSelectorFromString(@"test");
4. @property
@property是编译器特性,它可以帮助我们自动生成一些代码,其中包括可以自动生成成员变量,自动生成成员变量的setter 和 getter.
a. @property会生成什么代码
(1) @property 如果不加任何参数会根据情况生成 属性 和 普通的 setter,getter
1) 如果已经定义了要生成的属性(默认私有),那么编译器不会去定义
2) 如果已经声明了或者定义了要生成 setter 和 getter 将不会去 声明 和 定义
3) 生成的代码取决于 @property 后面的变量名
#import <Foundation/Foundation.h>
@interface Cat : NSObject
// @property自动生成如下代码
/*
{
@private
int _age;
}
-(void) setAge:(int)age;
- (int) age;
*/
// 注意 : 生成的属性名称是在前面加上一条下划线 : _age
@property int age;
@end
@implementation Cat
// @property自动生成如下代码
/* 普通的setter 和 getter
-(void) setAge:(int)age
{
_age = age;
}
- (int) age
{
return _age;
}
*/
@end
b. @property 的参数以及作用
(1) set方法内存管理相关参数:
retain: release旧值,retain新值
assign: 直接赋值 (缺损值)
copy: 释放旧值,copy新值,此属性只对那些实行了NSCopying协议的对象类型有效
// retain
@property (retain) NSString *name;
/* 生成代码
- (void) setName:(NSString *)name
{
if ( _name != name ){
[_name release];
_name = [name retain];
}
}
*/
// assign
@property (assign) NSString *name;
/* 生成代码
- (void) setName:(NSString *)name
{
_name = name;
}
*/
// copy
@property (copy) NSString *name;
/* 生成代码
- (void) setName:(NSString *)name
{
if ( _name != name ){
[_name release];
_name = [name copy];
}
}
*/
(2) set方法是否生成:
readonly: 只生成get方法。
readwrite: set方法和get方法都生成(缺损值)
(3) 多线程管理:
noatomic: 性能高,但线程不安全
atomic: 性能低,但线程安全
(4) set方法和get方法的方法名字:
setter : 决定了set方法的名称,一定要有个冒号 :
getter : 决定了get方法的名称(一般用在BOOL类型)
// setter
@property (setter = setNewAge:) int age;
/* 生成代码
- (void) setNewAge:(int)age
{
_age = age;
}
*/
// getter
@property (getter = isOk:) BOOL ok;
/* 生成代码
- (BOOL) isOk
{
return _ok;
}
*/
c. 使用注意
(1) 通常情况下为了性能高一般会加上 noatomic 参数
(2) 对于基本数据类型使用 assign OC对象类型使用 retain
(3) 为了提高可读性建议加上 readwrite 或者 readonly 参数
// 基本数据类型
@property (noatomic,readwrite,assign) int age;
// OC对象类型
@property (noatomic,readwrite,retain) NSString *name;
5. block
a. 简介
代码块本质上和变量类似。不同的是,代码块存储的数据是一个函数体。使用代码块是,你可以像调用其他标准函数一样,传入参数数,并得到返回值。
block的声明与函数指针声明很像,不过block有它特有的标志(^),如下图 :
b. 简单使用
#import <Foundation/Foundation.h>
int main(){
void (^print)(NSString *str);
print = ^(NSString *str){
NSLog(str);
};
print(@"hello world");
return 0;
}
c. block的递归调用
#import <Foundation/Foundation.h>
// 注意递归调用block前要把block的函数体定义出来
int main(){
int (^accumulate)(int) = ^(int a){
int sum = a;
if ( a > 0 ){
a--;
sum += accumulate( a-- );
} else {
return sum;
}
};
NSLog(@"accumulate 5 = %d",accumulate(5));
return 0;
}
d. block访问外部变量
#import <Foundation/Foundation.h>
/*
block代码块访问block之外的变量如果不加任何处理编译会报错
因此如果block要访问外部变量,请给变量加上__block关键字
*/
// 这里结合typedef使用block
typedef int (^MyBlock)(int);
int main(){
__block int a = 10;
MyBlock addWithA; // block的声明
addWithA= ^(int b){
b += a;
return b;
};
int result = addWithA(10);
NSLog(@"a + b = %d",result);
return 0;
}
6. @protocol
a. 简介
(1) @protocol,简称协议,主要是用来方便程序员之间交流的一种OC特性,它与java中的接口相似,它的组成是 @protocol + 方法列表 + @end;
(2) 它的结构和使用模板如下 :
1.协议的定义
@protocol 协议名称 <NSObject>
@required
// 方法声明列表....
@optiuonal
// 方法声明列表
@end
2.如何遵守协议
1> 类遵守协议
@interface 类名 : 父类名 <协议名称1, 协议名称2>
@end
2> 协议遵守协议
@protocol 协议名称 <其他协议名称1, 其他协议名称2>
@end
(3) 协议提供两个关键字用来限定方法是否强制实现 :
1) @required (默认),强制实现,如果不实现会报警告.由于OC是弱语法,因此如果类没有实现required方法编译也不会报错
2) @optional , 可以不实现,如果不实现也不会包警告
b. 使用
(1) 对于协议的定可以放在其他文件中 也可以单独新建文件存放,一般建议会单独新建文件存放
(2) 如果要求类要遵守某个协议,那么必须让这个协议遵守<NSObject>协议,否则该类的实例会失去NSOject的某些方法。
(3) 协议也可以对类的成员变量进行限定
// 以下代码暂时不考虑内存管理
/*------------------------------------------- ClassProtocol.h -------------------------------------------*/
#import <Foundation/Foundation.h>
@protocol ClassProtocol <NSObject>
@required
- (void) test;
@optional
- (void) test1;
@end
/************************************************************************************************************/
/*------------------------------------------- PropertyProtocol.h -------------------------------------------*/
#import <Foundation/Foundation.h>
@protocol ClassProtocol <PropertyProtocol>
- (void) test2;
@end
/************************************************************************************************************/
/*------------------------------------------- Dog.h -------------------------------------------*/
#import <Foundation/Foundation.h>
@protocol PropertyProtocol <NSObject>
@interface Dog: NSObject <PropertyProtocol>
@end
/************************************************************************************************************/
/*------------------------------------------- Dog.m -------------------------------------------*/
#import "Dog.h"
#import "PropertyProtocol.h"
@implementation Dog
- (void) test2
{
NSLog(@"PropertyProtocol default required -- test2"); // 实现 PropertyProtocol的 默认 required 方法
}
@end
/************************************************************************************************************/
/*------------------------------------------- Person.h -------------------------------------------*/
#import <Foundation/Foundation.h>
#import "Dog.h"
@protocol ClassProtocol // 告诉编译器,Test是一个协议,在使用该协议具体定义的函数时候才把该协议文件import进来,提高编译效率
@protocol PropertyProtocol
@interface Person : NSObject <ClassProtocol>
@property (nonatomic,retain) Dog<PropertyProtocol> *dog; // 协议对成员变量限定
// @property (nonatomic,retain) id<PropertyProtocol> *dog; //等价于上一句,不过可能会失去Dog特有的方法
@end
/************************************************************************************************************/
/*------------------------------------------- Person.m -------------------------------------------*/
#import "Person.h"
#import "ClassProtocol.h"
@implementation Person
- (void) test
{
NSLog(@"ClassProtocol required -- test"); // 实现 ClassProtocol 的 required 方法
}
- (void) test1
{
NSLog(@"ClassProtocol optional -- test1"); // 实现 ClassProtocol 的 optional 方法
}
@end
/************************************************************************************************************/
/*------------------------------------------- main.m -------------------------------------------*/
#import "Person.h"
#import "Dog.h"
#import "ClassProtocol.h"
#import "PropertyProtocol.h"
int main{
Person<ClassProtocol> *p = [[Person alloc] init]; // 显式实例化一个遵守 ClassProtocol 的Person对象
// id<ClassProtocol> *p = [[Person alloc] init]; // 显式实例化一个遵守 ClassProtocol 的 OC 对象,该对象只保留了 ClassProtocol 里面的方法,失去了Person特有的方法
Dog *dog = [[Dog alloc] init]; // 实例化一个遵守 PropertyProtocol 的Dog对象
[dog test2]; // 调用实现协议的方法
[p setDog:dog]; // 对协议限定的成员变量赋值,如果dog变量不遵守 PropertyProtocol 编译会报错
[p test];
[p test1];
return 0;
}
/************************************************************************************************************/
---------------------- Java培训、.Net培训、Android培训、IOS培训、期待与您交流! ----------------------
详情请查看:http://edu.csdn.net/heima