文章目录
  1. 1. 语法
    1. 1.1. 方法定义
    2. 1.2. 方法调用
    3. 1.3. 属性和内部变量
    4. 1.4. Protocol
    5. 1.5. Category
    6. 1.6. Block
    7. 1.7. id

拿到MBP也有3个礼拜了

上个礼拜总算有时间好好看了一下 Objective-C

Objective-C 可以说是一个静态语言,但又有很多动态特性.这点上感觉和ActionScript很象,不过ActionScript是纯脚本语言了

在这里也记录一下,用其他语言来类比它的一些特性和实现,会以Java为主结合C/C++,ActionScript等,方便兄弟们学习更快掌握 和 同乐

语法

Objective-C是C的超集,即在Objective-C(.m文件)里面是能完全写C的代码的,而且也能和Objective-C相互调用.

对于刚接触Objective-C的兄弟们,一般都会对其语法颇为不爽,这里列一下不爽的几点

  1. 参数前面还跟了个玩意儿
  2. self是咋回事
  3. 方法调用都是用[],眼花缭乱有木有
  4. 好多 : 冒号有木有5. 编译不报错有木有

这里就来慢慢解释

方法定义

Objective-C的方法定义是很有意思的

举个例子:

- (id)initWithTitle:(NSString *)title message:(NSString *)message;

-(减号) 代表这个方法是个实例方法

如果是+(加号) 代表这个是类方法 相当于Java的static方法(但还是有些不同的下面会讲到)

而一开始很多人会以为方法名是initWithTitle ,这个时候大家的认识就开始错误了,实际上整个方法名应该是initWithTitle:message: 这在后面使用@selector的时候就很明确了

做个对比就是

- (id)initWithTitle:(NSString *)title error:(NSString *)message;

这个方法和上面的方法实际上是两个不同名字的方法,这个方法全名应该是initWithTitle:error:

虽然他们有相同的initWithTitle和 相同的参数类型,但是由于message和error也是方法名的一部分所以就导致这是两个方法.

而方法里面 : (冒号)后面跟着的就是参数了 ,比如里面的title和message就在起方法实现里面作为传参被使用

Objective-C 为什么这么定义方法就是为了提高可读性,能够在方法定义的时候对参数就进行描述..

所以类似上面方法的正确看法就是

- (id)initWithTitle:(NSString *)title message:(NSString *)message;

|方法的作用 |需要title这个参数 |需要message这个参数 |

大家以后写方法的时候需要注意这种写法.

还有Objective还有Objective-C虽然实现了面向对象,但毕竟不是Objective-C++(.mm),所以Objective-C是不支持方法重载的,

比如

- (id)initWithTitle:(NSString *)title message:(NSStringessage;
- (id)initWithTitle:(NSString *)title message:(NSNumber *)message;

这两个方法同时定义会直接编译失败

方法调用

Objective-C的方法调用是用[]来表示的

比如 [self addSubview:loadingView];

这个就是表示调用self (self就是相当于Java中的this关键字) 的addSubview:方法 ,并传入loadingView这个参数

就其内部实现就是就其内部实现就是调用objc_msgSend这个方法 来实现调用

我们来看看objc_msgSend的实现

http://www.mulle-kybernetik.com/artikel/Optimization/opti-9.html

id  c_objc_msgSend( struct objc_class /* ahem */ *self, SEL _cmd, ...)
{
struct objc_class    *cls;
struct objc_cache    *cache;
unsigned int         hash;
struct objc_method   *method;
unsigned int         index;

if( self)
{
    cls   = self->isa;
    cache = cls->cache;
    hash  = cache->mask;
    index = (unsigned int) _cmd & hash;

    do
    {
        method = cache->buckets[ index];
        if( ! method)
            goto recache;
        index = (index + 1) & cache->mask;
    }
    while( method->method_name != _cmd);
    return( (*method->method_imp)( (id) self, _cmd));
}
return( (id) self);

recache:
/* ... */
return( 0);
}

可以看到方法调用并不是msg base的(当时我就被objc_msgSend这个方法名给骗了),而是vtable是实现,并且是lazy load的

那么Objective-C的调用有那些特性呢

特性一在于Object特性一在于Objective-C的方法绑定都是在运行时确定的而不是编译时

特性二在于发消息是可以被动态解析,甚至转发的.而调用则做不到这点

即你可以向一个 对一个对象 调用其没有实现的方法.而如果你设置了其动态解析货转发,那么这个方法还是会被执行的

具体可以看

http://www.apple.com.cn/developer/mac/library/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/chapter_5_section_1.html#//apple_ref/doc/uid/TP40008048-CH102-SW1

http://www.apple.com.cn/developer/mac/library/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/chapter_6_section_1.html#//apple_ref/doc/uid/TP40008048-CH105-SW1

所以整个消息被执行过程所以整个消息被执行过程就是

  1. 先去找这个对象有没有被实现的这个消息
  2. 没有实现就去找有没有被动态解析
  3. 没有再看有没有被设置可以消息转发
  4. 最后都没有则报错

可以看到利用这些特性可以很容易的实现 动态代理

特性三因为是发消息那么这个消息就有可能不是被同步执行的,还可以被异步执行

即[self addSubview:loadingView];还可以写成

[self performSelectorInBackground:@selector(addSubview:l) withObject:loadingView]

此时这个方法会被另外一个现场异步执行

那么我们看看NSObject(类似Java中的基类Object)的send Messages的相关方法

Sending Messages
-performSelector:withObject:afterDelay:
-performSelector:withObject:afterDelay:inModes:
-performSelectorOnMainThread:withObject:waitUntilDone:
-performSelectorOnMainThread:withObject:waitUntilDone:modes:
-performSelector:onThread:withObject:waitUntilDone:
-performSelector:onThread:withObject:waitUntilDone:modes:
-performSelectorInBackground:withObject:

可以看到performSelector可以选择不同的线程来执行,甚至还能延迟执行

不过具体会在后面的线程章节来细说

那我们再来看看类方法

比如+(id)initWithTitle:(NSString *)title message:(NSString *)message;

就是个类方法 调用的时候 类似[NSObject initWithTitle:title message:message]这样调用

看到这个就明白类方法就是绑定在类上面的消息实现

那么这个时候在这个实现里面还是可以使用self的,只不过这个self不是指向类实例,而是类本身

那我们再说说self:

在其他语言比如Java里面this,表示的是当前实例,于实例绑定,所以在static方法里面是不能出现this的,因为没有实例.

而在Objective-C里面self不是和实例绑定,而是和消息绑定,实际上self本身也是个传递参数,只不过是隐藏的而已,所以这点上和python很想,self传递的就是这个消息绑定的对象(类本身也是对象),实际上和self一起传递的隐藏参数还有一个 就是_cmd , _cmd代表的就是消息本身即SEL

在做消息动态解析的时候就是很明显的使用这两个参数了 因为在IMP(这个就是消息的真正实现里面),最开始的两个参数就是 self和_cmd,比如

void dynamicMethodIMP(id self, SEL _cmd) {
    // implementation ....
}

属性和内部变量

Objective-C的属性定义 使用@property来实现

Objective-C的内部变量可在紧跟@interface@implementation的{}中来定义

如:

@interface RemoteContext : NSObject {
@private
    NSMutableDictionary *_extra;
    NSMutableDictionary *_eventListener;
}
      @property(retain, nonatomic) NSString *host;
@property(copy, nonatomic) NSString *api;
@property(copy, nonatomic) NSString *version;
@property(retain, nonatomic) NSObject *parameter;
@property(readonly, nonatomic) NSMutableDictionary *extra;
@property(retain, nonatomic) ClientInfo *clientInfo;
@property id <RemoteHandlerProtocol> handler;
@end

其中如

NSMutableDictionary *_extra;

@property(readonly, nonatomic) NSMutableDictionary *extra;

是同一份数据

_extra是对对象内部的访问

extra是对象外部来访问

实际上对于@property 的属性 就算你未为他声明内部变量

编译器也会直接帮你生成一个内部变量以_开头加上你的属性名

比如

@property(retain, nonatomic) NSString *host;

会自动帮你生成

NSString *_host;

这样的内部变量 当你能在对象内部直接访问

然后就是@synthesize关键字

@implementation RemoteContext {
}
@synthesize api = _api;

就是相当于帮你对api这个property生成getter和setter方法

getter方法是

-(NSString*) api;

setter方法是

-(void)setApi:(NSString *)api;

而getter和setter方法名的自动生成也是可以被自定义的在

@property(getter=getApi) NSString *api;

就会生成-(NSString*) getApi;这样的getter方法

关于@property 里面的 其他关键字 比如 retain,assign涉及到Objective-C的内存管理,会在后面的内存管理篇来细讲

相对于@synthesize还有一个动态生成getter/setter的关键字@dynamic

这里@synthesize是在编译时生成getter/setter方法而@dynamic则是在运行时通过上面讲到的方法动态解析来生成getter/setter方法

Protocol

Protocol就是协议, Protocol可以单纯的认为就是Java中的interface

即可以预先定义好有哪些方法,需要可能被实现,而需要被遵守的类则可能需要实现这些预先定义好的方法

如:

@protocol RemoteHandlerProtocol <NSObject>
@optional
- (void)preProcess:(RemoteContext *)context;
- (void)process:(RemoteContext *)context;
- (void)processResult:(RemoteContext *)context;
- (void)response:(RemoteContext *)context;
@required
- (void)request:(RemoteContext *)remoteContext;
@end

协议RemoteHandlerProtocol继承NSObject这个协议

@optional下面的方法表示可以不被实现

@required下面的方法则必须被实现

那些可以不被实现的方法 ,需要被调用的时候怎么办的

那需要通过[self respondsToSelector:@selector(printTest)] 这个调用来判断是否存在这个方法的实现 然后再调用

而很多在定义 形参或属性或变量是 用id<Protocol> 来表示形参或属性或变量都必须是实现Protocol的才行

Category

Category的作用就是在已存在的类上为其再新加新的方法 实际上就是对已存在的类作mixin

而要用到Category中定义的方法 直接#import这个Category的头文件就可以了

Block

Block是在iOS5中才实现的语言特性,整个来说就是一种闭包的实现,我挺喜欢的.

由于iOS都是使用Protocol + delegate模式来实现事件的响应 , 导致事件的绑定和事件的响应是分开的,导致代码上下文是不连贯的

而Block的出现则可以缓解这个问题

id

id 是一个typedef

      typedef struct objc_object {
    Class isa;
} *id;

可以看到id是一个指针,它指向一个拥有Class isa;的结构体

很多资料和书上都把id和NSObject * 作等价处理

但实际上还是有很多不一样的 毕竟不是typedef NSObject * id;

typedef struct objc_object {
     Class isa;
 } *id;

这样的定义表示,任何拥有Class的对象结构都可以被认为是id , 当然原始类型是不行了

id相比NSObject还有更神奇的就是 对id 发任何消息 都不会出现 编译错误 甚至这个消息是完全没有被实现的

即对id作方法调用在编译时是完全不做校验的,全在运行时来动态绑定实现,当然 如果使用了id<Protocol>还是会在编译时检验消失是否在Protocol中被定义

文章目录
  1. 1. 语法
    1. 1.1. 方法定义
    2. 1.2. 方法调用
    3. 1.3. 属性和内部变量
    4. 1.4. Protocol
    5. 1.5. Category
    6. 1.6. Block
    7. 1.7. id