1. ObjC一般要点
- OC中所有对象类型的变量都必须加上"*",在ObjC中对象其实就是一个指针.
- ObjC中使用[Object method]方式来进行方法调用,本质其实就是向object发送method消息.
- ObjC中方法分为静态方法和动态方法,动态方法就是对象方法,静态方法就是类方法.简单区别是静态方法不能使用对象实例属性,一般做工具方法.
- 公共成员(public)的调用可以使用"->"操作符.
- 成员变量和属性: 通常一个成员的访问不会直接通过成员变量 而是通过属性暴露给外界.ObjC中实现属性就是通过setter和getter方法来实现.
成员属性的声明方式:
- 如果只声明一个属性
@property a
: 编译器会使用"_a"作为属性的成员变量 .
(如果没有手动定义则系统会自动生成一个私有的成员变量_a; 如果已经定义了成员变量_a则系统使用定义的这个;注意:如果此时定义的成员变量是a,则此时系统依旧会自动生成一个成员变量_a,它跟自定义成员变量a没有任何关系); - 如果声明一个属性
@property a
,且仅使用@synthesize a
进行声明,过程中没有指定使用的成员变量,此时编译器会使用"a"作为属性的成员变量;
如果使用的是@synthesize a=_a
声明情况下:
如果定义了成员变量a,则使用这个自定义的;如果没有定义则会自动生成一个私有的成员变量a; 如果定义了的是 _a,没有任何关系; - 如果声明了一个
@property a
,使用@synthesize a = _a
实现; 这个过程已经指定了使用的成员变量, 此时会使用指定的成员变量作为属性变量_a.
@synthesize
实际的意义就是 自动生成属性的setter和getter方法。是不是很绕?所幸苹果在iOS6之后只需@property
声明就可自动生成setter和getter.
声明属性相对于@synthesize,还可以使用@dynamic关键字,这表示告诉编译器不自动生成setter和getter方法.避免编译期间的警告;
然后根据需要自己实现存取方法或是 在运行时动态绑定(主要用于在CoreData的实现NSManagedObject子类时使用,由Core Data框架在程序运行时的动态生成子类属性)如果你不是使用Core Data,那么忽略他吧.
自定义构造方法
-(instance)initWithName:(NSString*) name age:(int)age {
if(self = [super init]) { //继承父类的初始设置
self.name = name; // 这里就写对象本身实例时初始属性设置.
self.age = age;
}
return self ;
}
description方法
在C#中每个类都有一个ToString()方法, JAVA中叫toString(), 都是用于打印一个对象的信息;在ObjC中这个方法叫做description.默认输出类名和地址. 重写此方法用于调试;(注意:不能在此方法中打印输出self,死循环)
-(NSString *)description{
return [NSString stringWithFormat:@"{name:%@,age:%i}",self.name,self.age];
}
OC中的多继承
多继承是从多个直接基类派生 类的能力,可以更加直接为应用程序建模;
而ObjC不支持多继承,这是由于消息机制中名字查找发生在运行时而非编译时,很难解决多个基类可能导致的二义性问题;
不过我们可以使用间接实现多继承的目的:
三种方法: 代理,类别和消息转发.
1. 代理和协议
委托是ObjC中最常见的一种回调机制
2.类别
用分类直接添加方法,运行时添加实例变量
3. 消息转发:
当向someObject发送消息,但runtime system在当前类和父类中都找不到对应方法的实现时,runtime system并不会立即报错崩溃,而是依次执行以下步骤:

- 动态方法解析: 向当前类发送
resolveInstanceMethod:
信号, 检查是否动态向该类添加了方法.(@dynamic); - 快速消息转发: 检测该类是否实现了
forwardingTargetForSelector:
方法 ,如果实现就调用; 若该方法返回值对象非nil或非self, 则向该返回对象 重新发送消息.
- 标准消息转发: runtime发送
methodSignatureForSelector:
消息获取Selector对应的方法签名. 返回值非空则通过forwardInvocation:
转发消息,返回值为空就会向当前对象发送doesNotRecognizeSelector:
消息崩溃退出;
利用上述的过程的2,3两步完成消息转发的两种方式,下面具体说明:
3.1 快速消息转发
实现很简单,只需重写- (instances)forwardingTargetForSelector:(SEL)aSelector
,就可以让A类调用B类的方法,从而实现多继承的目的:
//例. Teacher类实现 将消息转发给Doctor,
-(instances)forwardingTargetForSelector:(SEL)aSelector {
Doctor *doctor = [[Doctor alloc] init];
if([doctor respondsToSelector:aSelector]) {
return doctor;
}
return nil;
}
虽然消息可以动态转发传递,但是编译器的静态检查不通过,需要给Teacher声明要调用的方法,但是Teacher没有实现,那么该如何声明呢? 下面有两种方式解决:
- 声明方法-类别: 给Teacher添加分类,在其中声明operator方法.
- 导入Doctor头文件, 进行类型强转: 告诉编译器去Doctor中找operator方法的声明,并在调用时强转类型:
Teacher *teacher = [[Teacher alloc]init];
[(Doctor *)teacher operate];
3.2 标准消息转发
标准消息转发是重写 methodSignatureForSelector: 和 forwardInvocation:
两个方法即可。
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
NSMethodSignature* signature = [super methodSignatureForSelector:aSelector];
if (signature==nil) {
signature = [someObj methodSignatureForSelector:aSelector];
}
NSUInteger argCount = [signature numberOfArguments];
for (NSInteger i=0 ; i
}
return signature;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
SEL seletor = [anInvocation selector];
if ([someObj respondsToSelector:seletor]) {
[anInvocation invokeWithTarget:someObj];
}
}
两种消息转发方式比较:
快速消息转发: 简单快速,但是仅能转发给一个对象. 标准消息转发:复杂,转发可控,可以实现多对象转发.
多态
- 多态: 不同对象以自己的方式响应相同的消息.
- 目的: 简化编程接口,容许在类与类之间重用一些习惯性的命名,而不用为每一个新加的函数想一个新名字.
- 原理:每个类都属于该类的名字空间,类定义中的名字和类定义外的名字不冲突. 动态绑定.
多态的具体实现:
在ObjC中是通过一个叫做selector的选取器实现的,在Object-C中selector有两个意思:
- 当给对象的源码消息时,用来指方法的名字;
- 在源码编译后代替方法名的唯一标识;
选取器特点:所有同名的方法拥有同样的选取器. 而所有选取器都各不相同;
- SEL和@selector区别:选择器的类型是SEL.而 @selector指示符是用来引用选择器的, 它返回类型是SEL.
- 例:
SEL response; response = @selector(load:)
- 通过字符串来得到选取器:
responseSEL = NSSelectorFromString(@"loadDataForTableView:");
- 通过选择器转换来得到方法名:
NSString *methodName = NSStringFromSelector(responseSEL);
- 例:
- 方法和选取器: 选取器确定的是方法名,不是方法的实现, 这是多态和动态绑定的基础.它使得向不同对象发送相同的消息成为现实.
- 方法返回值和参数类型: 消息机制是通过选取器找到方法的返回值类型和参数类型. 因此: 动态绑定需要同名方法的实现 拥有相同返回值类型和相同的参数类型;否则,运行时可能出现找不到对应方法的错误.(有一个例外,虽然同名静态方法和实例方法拥有相同的选取器,但是它们可以有不同的参数类型和返回值类型。)
网友评论