Objective-C 快速参考

Objective-C 快速参考

Objective-C是利用几个重要的观念和C一些语法构成,然后再配合Foundation Framework完成最基本的Objective-C的环境在iOS或OSX上执行的APP,所以在文中只针对Objective-C提供的特性去做说明,Foundation Framework就归类于Framework再做介绍。

呼叫方法 (Calling Methods)

基本方法

Objective-C在呼叫方法时是以讯息的方式传递,而方法如果有参数时就必需要以:冒号隔开。

//-----------start-----------
[object method];
[object methodWithParameter:parameter];
//------------end------------

有回传值的方法

方法的结果可以有回传值,但回传值只接受单一的。

//-----------start-----------
returnValue=[object method];
returnValue=[object methodWithParameter:parameter];
//------------end------------

多参数输入

在方法有很多参数必需传入时,每个参数都会有个名称,名时后同样用:隔开传入的参数值,参数与参数之间用空白做区隔,这是与一般语言较不一样的地方。

//-----------start-----------
[object methodWithParameter:parameter Parameter1:parameter1];
//------------end------------

所以在传入的参数数量不同情况下也可以使用相同名称的方法。

//-----------start-----------
[object methodWithParameter:parameter Parameter1:parameter1 Parameter1:parameter2];
//------------end------------

编译器会自动根据传入参数数量自动找到符合的的方法。

呼叫自已物件中的方法:

一个物件也具备方法呼叫自已物件的方法,在呼叫自已物件中的法时需要使用前缀字self来呼叫。

//-----------start-----------
    [self method];
//------------end------------

举个实际例子:

//-----------start-----------
- (void)method {
    [self method2:@"method1"];
}
//
- (void)method2:(NSString *)parameter {
    NSLog(@"%@", parameter);
}
@end
//------------end------------

巢状输入

巢状输入的方法式意思就是说,一个物件呼叫方法后得到的结果直接传入另一个物件的方法之中,这种方式在高阶语言都会提供。

//-----------start-----------
[object2 methodWithParameter:[object1 methodWithParameter:parameter]];
//------------end------------

宣告物件

宣告物件在Objective-C的惯利是需要先宣告记忆体配置之后再初始化物件,之后整个物件才能开始使用它的方法。

基本建立物件

宣告物件的记忆体配置使用方法alloc

//-----------start-----------
[object alloc];
//------------end------------

初始化物件最基本的是使用init,所以当你在自订的物件时,本身也要去作init这个方法。

//-----------start-----------
[object init];
//------------end------------

后来都是使用巢状输入的方式直接建立新的物件

//-----------start-----------
[[object alloc] init];
//------------end------------

之后Objective-C又提供更便民的方式,就是使用new方法。

//-----------start-----------
[object new]; //等于alloc + init
//------------end------------

带有初始值的建立物件

有些物件会提供initWith的前缀字开头的提供初始化后直接指定某个值为预设值,所以建立物件需要顺带建立初始值时,使用这个方法相当的便利,自行实作时也可以参照这样规则的前缀字来建立方法。

//-----------start-----------
[object initWithTopValue:123]; //初始化后会指定Top值为123
//------------end------------

多参数时也可以:

//-----------start-----------
[object initWithTopValue:123 Parameter1:parameter1]; //初始化后会指定Top值为123
//------------end------------

建立物件

建立物件时,Objective-C与C语言一样,会有个标头档.h与程式档.m,标头档记载着物件的介面与方法的名称及需要引入其他使用物件的标头档,程式档则记载着物件方法的程式行为以及变数的宣告。

介面实作 (Interface)

介面实作(interface)的内容是存在标头档.h之中,下面为最基本的介面

//-----------start-----------
// Test1.h
#import <UIKit/UIKit.h>

@interface Test1 : NSObject {
    NSString* text1;
}
@end
//------------end------------

增加方法的宣告

//-----------start-----------
// Test1.h
#import <UIKit/UIKit.h>

@interface Test1 : NSObject {
    NSString* text1;
}

- method;

@end

//------------end------------

实作区段 (Implementation)

实作区段(implementation)的内容是存在程式档.m中,之前已经实作介面,这里要配合介面来写程式内容,因有增加方法method,所以将功能写上。

//-----------start-----------
// Test1.m
#import "Test1.h"

@implementation Test1

- (NSString*) method {
    return text1; //将text1内容回传
}

@end

//------------end------------

自动产生存取变数 (Property)

Objective-C中,如果要存取物件中的变数必需要实作写(setter)、读(getter)的方法,无法像某些语言可以直接存取,但一般来说每次都要自行实作实在很麻烦,所以Objective-C提供了Property这个方试来自动建立,但自动建立时会依照它内定的方式来命名,所以必需要配合Synthesize自动建立符合自已命名的变数名称。

上述是使用method方法来取得text1的内容,下面在标头档中使用 Property 直接自动产生存取方式。

//-----------start-----------
// Test1.h
#import <UIKit/UIKit.h>

@interface Test1 : NSObject {
    NSString* text1;
}
@property (nonatomic,strong) NSString* text1;

@end
//------------end------------

先看Property宣告前必需要知道的属性:

@property (属性1, 属性2) type propertyName;

属性可以填入的如下做个简单的解释:

  • strong 使物件保持存在不会失去参考计数
  • weak 物件失去参考时会设置成nil
  • assign 一般的指定,无参考计数

strong、assign为ARC开启时才会被要求使用

  • copy Make copy on assign 直接拷贝一个新的,参考计数为1,但释放原来的参考计数(减1)
  • nonatomic Make not threadsafe, increase perf 建立一个不在执行绪中使用 *readwrite 可以读取与写入,允许建立getter与setter
  • readonly 只能读取,只能建立getter

例子中使用(nonatomic,strong),一般ARC宣告都使用这类的宣告。我们所宣告的text1因为它部分程式内容是自动产生的,依照编译器的命名习惯,它会在变数名称前面加上_,所以我们要存取时就必需使用_text1名称,例子:

//-----------start-----------
_text11 = @"1234" //重新给与新的值

NSLog ("%@",_text1); //取得值并将内容印出
//------------end------------

在使用上如果很不方便的话,可以在程式档中加上合成的方式让编译器自动合成为text1名称方法来使用。

//-----------start-----------
// Test1.m
#import "Test1.h"

@implementation Test1

@synthesize text1;

@end
//------------end------------

完成后就可以使用text1来存取变数。

//-----------start-----------
text11 = @"1234" //重新给与新的值
NSLog ("%@",text1); //取得值并将内容印出
//------------end------------

或是可以用现在最常用的存取来使用,假设aTest1被宣告成Test1物件类型,使用的方式就可以像下面这样:

//-----------start-----------
aTest1.text11 = @"1234" //重新给与新的值
NSLog ("%@",aTest1.text1); //取得值并将内容印出
//------------end------------

早期没有这个方式可能就要自行建立setter/getter的方法,使用时的规则如:

//-----------start-----------
// Test1.h
#import <UIKit/UIKit.h>

@interface Test1 : NSObject {

}

-(void) setText1: (NSString*) d
-(NSString*) text1
-
@end
//------------end------------

//-----------start-----------
// Test1.m
#import "Test1.h"

@implementation Test1 {
    NSString* text1;
}

-(void) setText1: (NSString*) d
{
    text1 = d;
}

-(NSString*) text1
{
    return text1;
}
@end
//------------end------------

//-----------start-----------
[aTest1 setText1:@"1234"] //重新给与新的值
NSLog ("%@",[aTest1 text1]); //取得值并将内容印出
//------------end------------

原有物件直接增加方法 (Category)

Objective-C提供一个不需要用继承的方式可以在原有物件上增加方法的功能,这种感觉很像替原有的物件增加功能,当你的专案需要对某个物件只是增加功能,不想要用继承的方式去改变原本使用物件的名称以及重新实作某些方法就可以使用。

一样,都是有标头档与程式档,只差在档名有命名规则,实际名称为CNSString,在Xcode中会以你要在要增加的物件名称当做前缀字(Prefix),这个例子就如下面的:

//-----------start-----------
//NSString+CNSString.h
#import <Foundation/Foundation.h>

@interface NSString (CNSString)

-(void)printToNSLog;

@end
//------------end------------

要在NSString增加方法,所以档名会是NSString+CNSString

//-----------start-----------
//NSString+CNSString.m
#import "NSString+CNSString.h"

@implementation NSString (CNSString)

-(void)printToNSLog
{
    NSLog(@"%@",[self description]);
}

@end

//------------end------------

使用上面只要将标头档引入(NSString+CNSString.h),再像一般的方式宣告一个NSString,就能使用新增的方法printToNSLog

//-----------start-----------
//ViewController.h
#import <UIKit/UIKit.h>
#import "NSString+CNSString.h"

@interface ViewController : UIViewController


@end
//------------end------------

//-----------start-----------
//ViewController.m
#import "ViewController.h"

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    NSString *n1=@"1234";
    [n1 printToNSLog]; //直接使用并将内容印至Log视窗
}

@end

//------------end------------

建立协议 (Protocol)

Objective-C protocol的方式,与Java提供的Interface类似,当你要宣告时,它必需要在@interface之前,通常架构像这样:

//-----------start-----------
//ReadWriteValue.h
@protocol ReadWriteValue
:
-(void)didReadValue:(int)value;
-(void)didWroteValue;
:
@end
//------------end------------

物件使用protocol时需要用<名称>去告知使用protocol,当然也要记得引入标头档,再利用protocol宣告的协议,程式需要时执行协议的命令。

//-----------start-----------
//Temperature.h
#import "ReadWriteValue.h"
@interface Temperature:NSObject <ReadWriteValue>
{
    id thisObject;
}
-(void)setDelegate:(id)delegate;
-(void)toReadValue;
-(void)toWroteValue:(int)value
:
:
:
@end

//Temperature.m
#import "Temperature.h"
@implementation Templature

-(void)setDelegate:(id)delegate
{
    thisObject = delegate;
}

-(void)toReadValue
{
if ([thisObject respondsToSelector: @selector (didReadValue:value)] == YES)
[thisObject didReadValue];

}

-(void)toWroteValue:(int)value
{
if ([thisObject respondsToSelector: @selector (didWroteValue)] == YES)
[thisObject didWroteValue];

}

:
@end
//------------end------------

以上就是产生protocol的方法,但在使用的物件里有协议时,宣告好物件后,就必需要在implementation中实作protocol所要求的协议,并将它实作出来,下面例子利用Temperature物件来完成读取温度与写入设定功能,并建立TaiwanTemperature物件来取得台湾的温度,当温度读取完成时就会呼叫协议中的-(void)didReadValue:(int)value,告知程式读取温度已完成并将温度值传入协议的方法,所以只要实作它就可以取得温度的值。

//-----------start-----------
//TaiwanTemperature.h
@interface TaiwanTemperature:NSObject <ReadWriteValue>
{

}
@end


//TaiwanTemperature.m
#import "TaiwanTemperature.h"
@implementation TaiwanTemperature
{

}

-(void)viewDidLoad
{
    Temperature *temperature=[[Temperature alloc] init];
    [temperature toReadValue];
    [temperature toWroteValue:1];
}

-(void)didReadValue:(int)value
{
    NSLog ("%i",value);//印出温度值
}

-(void)didWroteValue;
{
    NSLog ("is wrote");//完成写入设定时会呼叫的协议方法
}

@end


//------------end------------

 私有与公开宣告

私有与公开变数其中的差别在于宣告时放置的位子,如果在.h中宣告的内容为对外公开的,在其他程式要引用物件宣告的内容时可以使用它,反之如果在.m中宣告则不管物件封装成Library或是其他,它都会与程式在非公开的范围,除非改于.h标头档中提供。

// Test1.h
#import <UIKit/UIKit.h>

@interface Test1 : NSObject {
// 定义公开的变数
    NSString *string1;
}
// 定义公开的 properties
// 定义公开的 methods
@property (nonatomic,strong) NSString* text1;
-(void) test1
//
- method;

@end

// Test1.m
#import "Test1.h"

@implementation Test1 {
    NSString *string2;
// 定义私有的常例变数
}
:
:

@end

提醒

整篇文章内容以ARC为主,看完后可以知道Objective-C语言的基本架构。在这环境中许多的语法其实与C很相近,所以只需要先理解基本架构就能开始使用Objective-C的环境,然而官方也提供ProgrammingWithObjectiveC 电子书,里面有此篇未介绍的基本语法及范例供参考,之后也会陆续的直接介绍语法的内容。(2014.04.20)