一. Objective-C 對象簡單處理
1. 包裝類
(1) 包裝類簡介
NSValue 和 NSNumber :
-- 通用包裝類 NSValue : NSValue 包裝單個 short, int, long, float, char, id, 指針 等資料;
-- NSNumber 包裝類 : 用于包裝 C 語言資料類型;
NSNumber 方法 :
-- "+ numberWithXxx :" : 将特定類型的值包裝成 NSNumber;
-- "- initWithXxx :" : 先建立一個 NSNumber 對象, 再用一個基本類型的值來初始化 NSNumber;
-- "- xxxValue :" : 傳回 NSNumber 對象包裝的基本類型的值;
(2) 包裝類代碼示例
代碼示例 :
/*************************************************************************
> File Name: OCNSNumberDemo.m
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 六 10/ 3 12:50:15 2015
************************************************************************/
#import <Foundation/Foundation.h>
int main(int argc, char * argv[])
{
@autoreleasepool {
NSNumber * num_int = [NSNumber numberWithInt : 10];
NSNumber * num_double = [NSNumber numberWithDouble : 10];
NSNumber * num_char = [NSNumber numberWithChar : 'A'];
NSLog(@"number_int : %d, number_double : %g, num_char : %c",
[num_int intValue], [num_double doubleValue], [num_char charValue]);
NSNumber * num_int1 = [[NSNumber alloc] initWithInt : 10];
NSNumber * num_double1 = [[NSNumber alloc] initWithDouble : 10];
NSNumber * num_char1 = [[NSNumber alloc] initWithChar : 'A'];
NSLog(@"number_int1 : %d, number_double1 : %g, num_char1 : %c",
[num_int1 intValue], [num_double1 doubleValue], [num_char1 charValue]);
}
}
-- 執行結果 :
localhost:oc_object octopus$ clang -fobjc-arc -framework Foundation OCNSNumberDemo.m
localhost:oc_object octopus$ ./a.out
2015-10-03 13:00:46.465 a.out[887:507] number_int : 10, number_double : 10, num_char : A
2015-10-03 13:00:46.468 a.out[887:507] number_int1 : 10, number_double1 : 10, num_char1 : A
2. description 方法
(1) description 方法簡介
description 方法簡介 : 類似于 Java 中 Object 的 toString() 方法;
-- 方法來源 : description 是 NSObject 中定義的, 所有的方法都有該方法;
-- 預設方法 : description 預設方法傳回 <類名: 位址>;
-- 輸出對象 : NSLog() 函數輸出一個對象, 其實輸出的是該對象的 description 方法;
-- 示例 : OCPerson * person, 列印 [person description] 和 person 輸出結果是一樣的;
(2) description 示例代碼
示例代碼 :
/*************************************************************************
> File Name: OCDescriptionDemo.m
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 六 10/ 3 14:25:28 2015
************************************************************************/
#import <Foundation/Foundation.h>
@interface OCDescriptionDemo : NSObject
@property (nonatomic, copy) NSString * name;
@property (nonatomic, assign) int age;
- (id) initWithNameAndAge : (NSString *) set_name setAge : (int) set_age;
@end
@implementation OCDescriptionDemo
@synthesize name;
@synthesize age;
- (id) initWithNameAndAge : (NSString *) set_name setAge : (int) set_age
{
self.name = set_name;
self.age = set_age;
return self;
}
- (NSString *) description
{
NSString * des = [NSString stringWithFormat :
@"<OCDescription[name = %@, age = %d]>", self.name, self.age];
return des;
}
@end
int main(int argc, char * argv[])
{
@autoreleasepool {
OCDescriptionDemo * description = [[OCDescriptionDemo alloc] initWithNameAndAge : @"Tom" setAge : 18];
NSLog(@"%@", description);
}
}
-- 執行結果 :
localhost:oc_object octopus$ clang -fobjc-arc -framework Foundation OCDescriptionDemo.m
localhost:oc_object octopus$ ./a.out
2015-10-03 14:50:18.665 a.out[970:507] <OCDescription[name = Tom, age = 18]>
3. == 或 isEqual : 方法
(1) "==" 運算符
"==" 簡介 :
-- 作用 : 判斷兩個變量是否相等;
-- 前提 : 兩個變量都是基本類型, 兩個變量相等傳回 true; 指針類型變量比較位址沒有任何意義;
(2) 常量池
常量池 :
-- 作用 : 保證相同的字元串常量至右一個, 不能出現多個相同的副本;
-- 例外 : 使用 [NSString stringWithFormat] 方法建立的字元串不會放入常量池;
(3) isEqual 方法
"isEqual" 方法簡介 :
-- 來源 : isEqual 方法是 NSObject 類提供的執行個體方法, 用于判斷相同類型的兩個變量是否相等;
-- 預設 : 預設方法還是比較位址, 需要開發者重寫這個方法;
-- NSString 的 isEqual 方法 : NSString 的 isEqual 方法是判斷兩個字元串是否相等, 包含的字元串相同就會傳回 true;
-- isEqualToString 方法 : 方法 : NSString 中定義的 isEqualToString 方法用于判斷目前字元串 與 另一個字元串的字元串序列是否相等;
重寫 isEqual 方法标準 :
-- 自反性 : 對象 x, [x isEqual : x] 必須傳回 true;
-- 對稱性 : 對象 x 和 y, 如果 [x isEqual : y] 傳回值 必須與 [y isEqual : x] 傳回值相同;
-- 傳遞性 : 對象 x , y 和 z, [x isEqual : y] = true, [y isEqual : z] = true, 那麼 x z 也相等;
-- 一緻性 : x , y 對象無論調用多少次, 傳回值結果都應該保持一緻;
-- nil 對比 : 如果 x 不是 nil, [x isEqual : nil] 必須傳回 false;
(4) "==" 和 "isEqual" 示例源碼
示例源碼 :
/*************************************************************************
> File Name: OCEqual.m
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 六 10/ 3 16:07:56 2015
************************************************************************/
#import <Foundation/Foundation.h>
@interface OCEqual : NSObject
@property (nonatomic, copy) NSString * name;
@property (nonatomic, assign) int age;
- (id) initWithName : (NSString *) set_name setAge : (int) set_age;
@end
@implementation OCEqual
@synthesize name;
@synthesize age;
- (id) initWithName : (NSString *) set_name setAge : (int) set_age
{
self.name = set_name;
self.age = set_age;
return self;
}
- (BOOL) isEqual : (id) other
{
if(self == other)
return YES;
if(other != nil && [other isMemberOfClass : OCEqual.class])
{
OCEqual * equal = (OCEqual *) other;
return [self.name isEqual : equal.name] && (self.age == equal.age);
}
return NO;
}
@end
int main(int argc, char * argv[])
{
@autoreleasepool {
int int_a = 10;
int double_a = 10.0;
NSLog(@"int_a == double_a : %d", (int_a == double_a));
NSString * str_a = @"Octopus";
NSString * str_b = @"Octopus";
NSString * str_c = [NSString stringWithFormat : @"Octopus"];
NSString * str_d = [NSString stringWithFormat : @"Octopus"];
NSLog(@"str_a == str_b : %d, str_c == str_d : %d, [str_c isEqual : str_d] : %d",
str_a == str_b, str_c == str_d, [str_c isEqual : str_d]);
OCEqual * equal_a = [[OCEqual alloc] initWithName : @"Tom" setAge : 18];
OCEqual * equal_b = [[OCEqual alloc] initWithName : @"Jerry" setAge : 20];
OCEqual * equal_c = [[OCEqual alloc] initWithName : @"Jerry" setAge : 20];
NSLog(@"[equal_a isEqual : equal_b] : %d, [equal_b isEqual : equal_c] : %d",
[equal_a isEqual : equal_b], [equal_b isEqual : equal_c]);
}
}
-- 執行結果 :
localhost:oc_object octopus$ clang -fobjc-arc -framework Foundation OCEqual.m
localhost:oc_object octopus$ ./a.out
2015-10-03 16:58:35.690 a.out[1168:507] int_a == double_a : 1
2015-10-03 16:58:35.693 a.out[1168:507] str_a == str_b : 1, str_c == str_d : 0, [str_c isEqual : str_d] : 1
2015-10-03 16:58:35.693 a.out[1168:507] [equal_a isEqual : equal_b] : 0, [equal_b isEqual : equal_c] : 1
二. 類别 與 擴充
1. Category 類别
(1) 擴充類簇需求
類簇擴充需求 : 開發過程中有時需要擴充類行為;
-- 繼承 : 通過繼承, 子類在父類基礎上添加方法 或者 重寫父類方法;
-- 問題 : 如果想要為父類增加一個方法, 子類同時也繼承這些方法, 此時使用繼承就滿足不了這個功能了;
-- 類簇 : OC 中沒有接口, 需要接口時, 就會選擇定義一個父類, 以該父類派生 N 個子類, 該系列的類被成為 類簇;
-- 類簇擴充方法 : 為父類增加方法, 類簇中得子類同時也增加該方法, 擴充類簇中得父類是最合适的方法;
(2) Category 類别
類别 (category) 簡介 :
-- 作用 : 為現有類添加方法, 不需要通路原有類代碼, 不需要繼承;
-- 有點 : 動态地為現有類添加方法, 将類定義子產品化 分布到多個檔案中;
(3) Category 類别 接口 文法格式
類别 (category) 接口部分文法格式 :
-- 接口檔案類命名 : "類名+類别名.h", 如 要擴充 OCPerson 類, 類别名為 SB, 那麼接口檔案名就是 "OCPerson+SB.h";
-- 示例 :
@interface 已有類 (類别名)
//方法定義
...
@end
-- 類别名 : 必須是項目中沒有的類, 定義類别時使用的類名, 必須是已有的類;
-- 圓括号 : 類别名 定義在 需要擴充的已有類之後, 必須使用圓括号括起來;
-- 定義内容 : 類别中一般情況下隻定義方法;
(4) Category 類别 實作類 文法格式
類别 (category) 實作部分文法格式 :
-- 實作類檔案命名 : "類名+類别名.m", 如 要擴充 OCPerson 類, 類别名為 SB, 那麼接口檔案名就是 "OCPerson+SB.m";
-- 示例 :
@implementation 已有類 (類别名)
//方法定義
...
@end
(5) Category 類别 注意點
注意事項 :
-- 影響範圍 : 通過 category 添加新方法後, 會影響到 指定的被擴充的類, 同時也會影響到其子類;
-- 多個類别 : 一個類可以 對應多個類别, 這些類别都可以為類增加方法定義;
-- 類别優點 : 進行子產品化設計, 調用私有方法, 實作非正式協定;
(6) Category 擴充 NSNumber 示例
NSNumber 擴充示例 : 為其添加一個計算圓面積的方法;
-- NSNumber+SB.h :
/*************************************************************************
> File Name: NSNumber+SB.h
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 六 10/ 3 18:58:53 2015
************************************************************************/
#import <Foundation/Foundation.h>
@interface NSNumber (SB)
- (NSNumber *) circleAera : (double) radius;
@end
-- NSNumber+SB.m :
/*************************************************************************
> File Name: NSNumber+SB.m
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 六 10/ 3 19:02:05 2015
************************************************************************/
#import "NSNumber+SB.h"
@implementation NSNumber (SB)
- (NSNumber *) circleAera : (double) radius
{
double aera = 3.1415926 * radius * radius;
return [NSNumber numberWithDouble : aera];
}
@end
-- NSNumber+SBTest.m :
/*************************************************************************
> File Name: NSNumber+SBTest.m
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 六 10/ 3 19:08:19 2015
************************************************************************/
#import <Foundation/Foundation.h>
#import "NSNumber+SB.h"
int main(int argc, char * argv[])
{
@autoreleasepool {
NSNumber * num = [NSNumber numberWithInt : 3];
NSNumber * circleAera = [num circleAera : 1];
NSLog(@"%@", circleAera);
}
}
-- 執行結果 :
localhost:oc_object octopus$ clang -fobjc-arc -framework Foundation NSNumber+SB.m NSNumber+SBTest.m
localhost:oc_object octopus$ ./a.out
2015-10-03 19:18:13.625 a.out[1333:507] 3.1415926
2. Category 類别實際用法
(1) 類的子產品化設計
子產品化設計簡介 :
-- 實作部分唯一 : 定義一個類是, 使用 "類名.h" 定義接口部分, 使用 "類名.m" 定義實作部分, 不能将實作部分定義在多個 ".m" 字尾 檔案中;
-- 檔案臃腫 : 如果類很大, 将所有的代碼放在一個 "類名.m" 檔案中, 非常難維護;
(2) 調用私有方法
私有方法調用簡介 :
-- 私有方法 : 接口中沒有定義, 在實作部分定義的方法是 私有方法, 不允許被外部調用;
-- 調用私有方法一 : 使用 NSObject 的 "performSelector :"執行調用, 也是可以調用私有方法的, 不過此方法會避開文法檢查, 導緻未知問題;
(3) 調用私有方法 代碼示例
代碼示例 :
-- OCPrivate.h :
/*************************************************************************
> File Name: OCPrivate.h
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 日 10/ 4 06:55:34 2015
************************************************************************/
#import <Foundation/Foundation.h>
@interface OCPrivate : NSObject
@property (nonatomic, copy) NSString * name;
-(void) info;
@end
-- OCPrivate.m :
/*************************************************************************
> File Name: OCPrivate.m
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 日 10/ 4 06:57:48 2015
************************************************************************/
#import "OCPrivate.h"
@implementation OCPrivate
@synthesize name;
- (void) info
{
NSLog(@"name : %@", self.name);
}
- (void) speak
{
NSLog(@"Hello World !");
}
@end
-- OCPrivate+SB.h :
/*************************************************************************
> File Name: OCPrivate+SB.h
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 日 10/ 4 07:19:35 2015
************************************************************************/
#import "OCPrivate.h"
@interface OCPrivate (SB)
- (void) speak;
@end
-- OCPrivateTest.m :
/*************************************************************************
> File Name: OCPrivateTest.m
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 日 10/ 4 07:22:04 2015
************************************************************************/
#import "OCPrivate+SB.h"
int main(int argc, char * argv[])
{
@autoreleasepool
{
OCPrivate * priva = [[OCPrivate alloc] init];
priva.name = @"Tom";
[priva info];
[priva speak];
}
}
3. extension 擴充
(1) extension 簡介
extension 簡介 :
-- 作用 : 擴充相當于匿名類别;
-- 文法 :
@interface 已有類 ()
{
//執行個體變量 ...
}
// 方法定義 ...
@end
-- 用法 : 定義兩個頭檔案, OCExtension.h 和 OCExtension+speak.h, OCExtension.m 導入 OCExtension+speak.h 頭檔案;
(2) extension 源碼示例
源碼示例 :
-- OCExtension.h :
/*************************************************************************
> File Name: OCExtension.h
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 日 10/ 4 08:18:43 2015
************************************************************************/
#import <Foundation/Foundation.h>
@interface OCExtension : NSObject
@property (nonatomic, copy) NSString * name;
@property (nonatomic, assign) int age;
- (void) info;
@end
-- OCExtension+speak.h :
/*************************************************************************
> File Name: OCExtension+speak.h
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 日 10/ 4 08:31:37 2015
************************************************************************/
#import "OCExtension.h"
@interface OCExtension ()
@property (nonatomic, copy) NSString * home;
- (void) speak : (NSString *) content;
@end
-- OCExtension.m :
/*************************************************************************
> File Name: OCExtension.m
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 日 10/ 4 08:27:50 2015
************************************************************************/
#import "OCExtension+speak.h"
@implementation OCExtension
@synthesize name;
@synthesize age;
@synthesize home;
- (void) info
{
NSLog(@"info : name : %@ , age : %d", self.name, self.age);
}
- (void) speak : (NSString *) content
{
NSLog(@"%@ speak %@", self.name, content);
}
@end
-- OCExtensionTest.m :
/*************************************************************************
> File Name: OCExtensionTest.m
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 日 10/ 4 13:20:42 2015
************************************************************************/
#import "OCExtension+speak.h"
int main(int argc, char * argv[])
{
@autoreleasepool {
OCExtension * extension = [[OCExtension alloc] init];
extension.name = @"Tom";
extension.age = 18;
extension.home = @"China";
[extension info];
[extension speak : @"Are you fucking kidding me"];
}
}
三. 協定 與 委托
1. 類别實作非正式協定
(1) 非正式協定簡介
協定簡介 :
-- 作用 : OC 中得協定作用相當于其它語言中得接口;
-- 協定表現 : 協定定義的是 多個類 共同的行為規範, 通常定義一組公用方法, 這些方法都沒有實作, 方法由類來實作;
非正式協定簡介 :
-- 建立 NSObject 類别 : 以 NSObject 為基礎, 為 NSObject 建立類别, 為該類别指定新增方法, 即給所有的 NSObject 子類增加了新方法;
-- 實作 NSObject 類别 : 實作 NSObject 類别時, 實作該列别下地所有方法, 即之前在 NSObject 類别中定義的方法;
(2) 非正式協定代碼示例
非正式協定代碼示例 :
-- NSObject+speak.h : 為 NSObject 定義的類别接口, 所有的繼承 NSObject 的類都必須實作該類别中得抽象方法;
/*************************************************************************
> File Name: NSObject+speak.h
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 一 10/ 5 08:55:48 2015
************************************************************************/
#import <Foundation/Foundation.h>
@interface NSObject (speak)
- (void) speak;
@end
-- OCNSObjectProtocal.h : NSObject 子類接口, 該接口繼承 NSObject 類, 注意 需要導入 NSObject+speak.h 頭檔案;
/*************************************************************************
> File Name: OCNSObjectProtocal.h
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 一 10/ 5 09:00:37 2015
************************************************************************/
#import "NSObject+speak.h"
@interface OCNSObjectProtocal : NSObject
@end
-- OCNSObjectProtocal.m : OCNSObjectProtocal 實作類, 在該實作類中必須實作 類别中定義的 speak 方法;
/*************************************************************************
> File Name: OCNSObjectProtocal.m
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 一 10/ 5 09:02:03 2015
************************************************************************/
#import "OCNSObjectProtocal.h"
@implementation OCNSObjectProtocal
- (void) speak
{
NSLog(@"Speak Hello World");
}
@end
-- OCNSObjectProtocalTest.m : 測試類, 測試以上代碼是否可以執行;
/*************************************************************************
> File Name: OCNSObjectProtocalTest.m
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 一 10/ 5 09:04:31 2015
************************************************************************/
#import "OCNSObjectProtocal.h"
int main(int argc, char * argv[])
{
@autoreleasepool {
OCNSObjectProtocal * obj = [[OCNSObjectProtocal alloc] init];
[obj speak];
}
}
-- 執行結果 :
bogon:oc_object octopus$ clang -fobjc-arc -framework Foundation OCNSObjectProtocal.m OCNSObjectProtocalTest.m
bogon:oc_object octopus$ ./a.out
2015-10-05 09:06:44.895 a.out[2100:507] Speak Hello World
2. 定義正式協定
(1) 正式協定文法
正式協定文法 :
-- 文法 :
@protocol 協定名稱 <父類協定1, 父類協定2 ...>
// N 個協定方法
@end
-- 協定名稱規範 : 采用與類名相同的命名規則;
-- 繼承規則 : 一個協定 可以有 多個父類協定, 協定隻能繼承協定, 不能繼承類;
-- 方法規則 : 協定中隻能定義抽象方法, 不能定義方法實作, 既可以定義類方法, 也可以定義執行個體方法;
(2) 實作協定
實作協定文法 :
-- 文法 :
@interface 類名 : 父類 <協定1, 協定2...>
-- 對應關系 : 一個類可以實作多個協定;
(3) 聲明協定變量
變量聲明 :
-- 使用原變量聲明 : "變量名 * 對象名" , 如 "OCCat * cat";
-- 使用協定定義 : "NSObject <協定1, 協定2 ...> * 對象名", 如 "NSObject<OCProtocolCat> * cat";
-- 使用 id 類型定義 : "id<OCProtocolCat> 對象名", 如 "id<OCProtocolCat> cat", 注意此處沒有指針辨別;
(4) 正式協定實作代碼
代碼示例 :
-- OCAnimalProtocol.h : 最基礎的協定1;
/*************************************************************************
> File Name: OCAnimalProtocol.h
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 一 10/ 5 09:25:41 2015
************************************************************************/
#import <Foundation/Foundation.h>
@protocol OCAnimalProtocol
- (void) name;
- (void) age;
@end
-- OCProtocolBord.h : 最基礎的協定2;
/*************************************************************************
> File Name: OCProtocolBord.h
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 一 10/ 5 09:40:08 2015
************************************************************************/
#import <Foundation/Foundation.h>
@protocol OCProtocolBord
- (void) fly;
@end
-- OCProtocolCat.h : 該協定實作了 上面的兩個協定;
/*************************************************************************
> File Name: OCProtocolCat.h
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 一 10/ 5 09:45:17 2015
************************************************************************/
#import <Foundation/Foundation.h>
#import "OCAnimalProtocol.h"
#import "OCProtocolBord.h"
@protocol OCProtocolCat <OCAnimalProtocol, OCProtocolBord>
- (void) purr;
@end
-- OCCat.h : OCCat 類接口部分, 生命了該類 實作協定 OCProtocolCat 協定;
/*************************************************************************
> File Name: OCCat.h
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 一 10/ 5 09:50:21 2015
************************************************************************/
#import "OCProtocolCat.h"
@interface OCCat : NSObject <OCProtocolCat>
@end
-- OCCat.m : OCCat 類實作部分;
/*************************************************************************
> File Name: OCCat.m
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 一 10/ 5 09:52:45 2015
************************************************************************/
#import "OCCat.h"
@implementation OCCat
- (void) name
{
NSLog(@"name : cat");
}
- (void) age
{
NSLog(@"age : 18");
}
- (void) fly
{
NSLog(@"cat fly");
}
- (void) purr
{
NSLog(@"cat purr");
}
@end
-- OCProtocolTest.m : 以上類的功能測試類;
/*************************************************************************
> File Name: OCProtocolTest.m
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 一 10/ 5 10:14:38 2015
************************************************************************/
#import <Foundation/Foundation.h>
#import "OCCat.h"
int main(int argc, char * argv[])
{
@autoreleasepool {
OCCat * cat = [[OCCat alloc] init];
[cat name];
[cat age];
[cat fly];
[cat purr];
NSObject<OCProtocolCat> * cat1 = [[OCCat alloc] init];
[cat1 name];
[cat1 age];
[cat1 fly];
[cat1 purr];
id<OCAnimalProtocol> cat2 = [[OCCat alloc] init];
[cat2 name];
[cat2 age];
}
}
-- 執行結果 :
bogon:6.4 octopus$ clang -fobjc-arc -framework Foundation OCCat.m OCProtocolTest.m
bogon:6.4 octopus$ ./a.out
2015-10-05 10:24:20.099 a.out[2271:507] name : cat
2015-10-05 10:24:20.101 a.out[2271:507] age : 18
2015-10-05 10:24:20.102 a.out[2271:507] cat fly
2015-10-05 10:24:20.102 a.out[2271:507] cat purr
2015-10-05 10:24:20.102 a.out[2271:507] name : cat
2015-10-05 10:24:20.103 a.out[2271:507] age : 18
2015-10-05 10:24:20.103 a.out[2271:507] cat fly
2015-10-05 10:24:20.104 a.out[2271:507] cat purr
2015-10-05 10:24:20.104 a.out[2271:507] name : cat
2015-10-05 10:24:20.104 a.out[2271:507] age : 18
3. 委托
委托概念 : 定義協定的類 将 定義協定的方法 委托給 實作協定的類;
-- 好處 : 類具有更好地通用性, 具體的動作交給實作類完成;
建立工程 :
-- 歡迎界面, 選擇 Create a new xcode project;
-- 建立一個 OS 下地 Cocoa Application :
-- 建立 工程 :
項目中得源檔案 :
-- main.m : main() 函數入口;
-- OCAppDelegate.h : OCAppDelegate 類接口檔案;
-- OCAppDelegate.m : OCAppDelegate 類實作部分;
代碼示例 :
-- 前置操作 : 删除 MainMenu.xib 檔案, 删除 Hello-Info.plist 中的 MainMenu 選項;
-- OCAppDelegate.h :
//
// OCAppDelegate.h
// Hello
//
// Created by octopus on 15-10-5.
// Copyright (c) 2015年 www.octopus.org.cn. All rights reserved.
//
#import <Cocoa/Cocoa.h>
//該接口 實作 NSApplicationDelegate 協定
@interface OCAppDelegate : NSObject <NSApplicationDelegate>
//定義視窗
@property (strong) NSWindow *window;
@end
-- OCAppDelegate.m :
//
// OCAppDelegate.m
// Hello
//
// Created by octopus on 15-10-5.
// Copyright (c) 2015年 www.octopus.org.cn. All rights reserved.
//
#import "OCAppDelegate.h"
@implementation OCAppDelegate
@synthesize window;
//應用加載完成時回調的方法
- (void)applicationWillFinishLaunching:(NSNotification *)notification
{
//設定視窗屬性
self.window = [[NSWindow alloc] initWithContentRect :
NSMakeRect(300, 300, 320, 200)
styleMask:(NSTitledWindowMask | NSMiniaturizableWindowMask |NSClosableWindowMask)
backing:NSBackingStoreBuffered
defer:NO];
self.window.title = @"Hello World";
//設定文本框屬性
NSTextField * label = [[NSTextField alloc] initWithFrame:NSMakeRect(60, 120, 200, 60)];
[label setSelectable:YES];
[label setBezeled:YES];
[label setDrawsBackground:YES];
[label setStringValue:@"HELLO WORLD"];
//設定按鈕屬性
NSButton * button = [[NSButton alloc] initWithFrame:NSMakeRect(120, 40, 80, 30)];
button.title = @"OCTOPUS";
[button setBezelStyle:NSRoundedBezelStyle];
[button setBounds:NSMakeRect(120, 40, 80, 30)];
//将 文本框 和 按鈕 添加到視窗中
[self.window.contentView addSubview:label];
[self.window.contentView addSubview:button];
}
//加載完成時回調的方法
- (void) applicationDidFinishLaunching:(NSNotification *)notification
{
//顯示視窗
[self.window makeKeyAndOrderFront:self];
}
@end
-- main.m :
//
// main.m
// Hello
//
// Created by octopus on 15-10-5.
// Copyright (c) 2015年 www.octopus.org.cn. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import "OCAppDelegate.h"
int main(int argc, const char * argv[])
{
@autoreleasepool {
//建立一個實作了 NSApplicationDelegate 協定的對象
OCAppDelegate * delegate = [[OCAppDelegate alloc] init];
//擷取 NSApplication 單例對象
[NSApplication sharedApplication];
//設定代理, 将處理方法委托給 delegate
[NSApp setDelegate : delegate];
//開始運作程式
return NSApplicationMain(argc, argv);
}
}
-- 運作結果 :
四. 異常處理
1. @try ... @catch ... @finally ... 異常捕捉
(1) Objective-C 異常機制
Objective-C 異常機制 :
-- 作用 : 開發者将引發異常的代碼放在 @try 代碼塊中, 程式出現異常 使用 @catch 代碼塊進行捕捉;
-- 每個代碼塊作用 : @try 代碼塊存放可能出現異常的代碼, @catch 代碼塊 異常處理邏輯, @finally 代碼塊回收資源;
-- 文法示例 :
@try
{
// 業務邏輯
}
@catch (異常類型名1 ex)
{
//異常處理代碼
}
@catch (異常類型名2 ex)
{
//異常處理代碼
}
// 可以捕捉 N 個 異常 ...
@finally
{
//回收資源
}
(2) Objective-C 異常處理過程
異常處理過程 :
-- 生成異常對象 : @try 中出現異常, 系統會生成一個異常對象, 該對象送出到系統中 系統就會抛出異常;
-- 異常處理流程 : 運作環境接收到 異常對象時, 如果存在能處理該異常對象的 @catch 代碼塊, 就将該異常對象交給 @catch 處理, 該過程就是捕獲異常, 如果沒有 @catch 代碼塊處理異常, 程式就會終止;
-- @catch 代碼塊捕獲過程 : 運作環境接收到 異常對象 時, 會依次判斷該異常對象類型是否是 @catch 代碼塊中異常或其子類執行個體, 如果比對成功, 被比對的 @catch 就會處理該異常, 都則就會跟下一個 @catch 代碼塊對比;
-- @catch 處理異常 : 系統将異常對象傳遞給 @catch 形參, @catch 通過該形參擷取異常對象詳細資訊;
其它注意點 :
-- @try 與 @catch 對應關系 : 一個 @try 代碼塊 可以對應 多個 @catch 代碼塊;
-- {} 省略問題 : 異常捕獲的 @try @catch @finally 的花括号不可省略;
NSException 異常類 :
-- 簡介 : NSException 是 OC 中所有異常的父類;
-- 位置永遠在最後 : @catch 代碼塊捕獲異常時檢視 異常對象類型是否是 捕獲的異常類型 或者其子類, 一旦放在開頭, 後面的異常永遠不可能捕獲;
(3) 異常資訊通路
異常資訊通路 :
-- name : 傳回異常的詳細名稱;
-- reason : 傳回異常引發的原因;
-- userInfo : 傳回異常的使用者資訊, 一個 NSDictionary 對象;
(4) 使用 finally 回收資源
回收實體資源 : @try 代碼塊中打開實體資源, 資料庫 網絡連接配接 檔案等, 都需要回收, 在 @finally 中回收最好;
-- 回收位置分析 : 如果再 @try 中回收, 出現異常, 異常後面的代碼無法執行, @catch 中回收, 如果不出現異常, 該代碼塊就不會執行; 是以 finally 中是必執行的代碼, 在這裡回收最合适;
(5) 異常代碼示例
異常代碼示例 :
-- OCAnimal.h : 定義協定;
/*************************************************************************
> File Name: OCAnimal.h
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 一 10/ 5 16:30:02 2015
************************************************************************/
#import <Foundation/Foundation.h>
@protocol OCAnimal
@optional
- (void) run;
@end
-- OCCat.h : 定義 OCCat 接口;
/*************************************************************************
> File Name: OCCat.h
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 一 10/ 5 16:33:59 2015
************************************************************************/
#import "OCAnimal.h"
@interface OCCat : NSObject <OCAnimal>
@end
-- OCCat.m : 定義 OCCat 實作類;
/*************************************************************************
> File Name: OCCat.m
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 一 10/ 5 16:36:36 2015
************************************************************************/
#import "OCCat.h"
@implementation OCCat
@end
-- OCCatTest.m : 測試類;
/*************************************************************************
> File Name: OCCatTest.m
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 一 10/ 5 16:38:01 2015
************************************************************************/
#import "OCCat.h"
int main(int argc, char * argv[])
{
@autoreleasepool {
OCCat * cat = [[OCCat alloc] init];
[cat run];
}
}
-- 執行結果 :
bogon:6.5 octopus$ clang -fobjc-arc -framework Foundation OCCat.m OCCatTest.m
bogon:6.5 octopus$ ./a.out
2015-10-05 16:39:23.589 a.out[2985:507] -[OCCat run]: unrecognized selector sent to instance 0x7fd7a3401870
2015-10-05 16:39:23.611 a.out[2985:507] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[OCCat run]: unrecognized selector sent to instance 0x7fd7a3401870'
*** First throw call stack:
(
0 CoreFoundation 0x00007fff903dd25c __exceptionPreprocess + 172
1 libobjc.A.dylib 0x00007fff8ecdbe75 objc_exception_throw + 43
2 CoreFoundation 0x00007fff903e012d -[NSObject(NSObject) doesNotRecognizeSelector:] + 205
3 CoreFoundation 0x00007fff9033b044 ___forwarding___ + 452
4 CoreFoundation 0x00007fff9033adf8 _CF_forwarding_prep_0 + 120
5 a.out 0x0000000108575efd main + 109
6 libdyld.dylib 0x00007fff851f35fd start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
Abort trap: 6
(6) 異常捕獲代碼示例
異常捕擷取示例 : 該示例扔使用上面的 OCAnimal.h, OCCat.h, OCCat.m 示例;
-- OCCatTest.m :
/*************************************************************************
> File Name: OCCatTest.m
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 一 10/ 5 16:38:01 2015
************************************************************************/
#import "OCCat.h"
int main(int argc, char * argv[])
{
@autoreleasepool {
@try
{
OCCat * cat = [[OCCat alloc] init];
[cat run];
}
@catch (NSException * ex)
{
NSLog(@"exception name : %@, reason : %@", ex.name, ex.reason);
}
@finally
{
NSLog(@"finally execute");
}
NSLog(@"success");
}
}
-- 執行結果 :
bogon:6.5 octopus$ clang -fobjc-arc -framework Foundation OCCat.m OCCatTest.m
bogon:6.5 octopus$ ./a.out
2015-10-05 16:53:46.850 a.out[3008:507] -[OCCat run]: unrecognized selector sent to instance 0x7f884bc018b0
2015-10-05 16:53:46.853 a.out[3008:507] exception name : NSInvalidArgumentException, reason : -[OCCat run]: unrecognized selector sent to instance 0x7f884bc018b0
2015-10-05 16:53:46.853 a.out[3008:507] finally execute
2015-10-05 16:53:46.854 a.out[3008:507] success
2. 抛出自定義異常
(1) 自定義異常文法
自定義異常抛出 :
-- 文法 :
@throw 異常對象;
(2) 自定義異常代碼示例
自定義異常代碼示例 :
-- OCException.h 接口 :
/*************************************************************************
> File Name: OCException.h
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 一 10/ 5 16:58:24 2015
************************************************************************/
#import <Foundation/Foundation.h>
@interface OCException : NSException
@end
-- OCException.m 實作類 :
/*************************************************************************
> File Name: OCException.m
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 一 10/ 5 17:03:15 2015
************************************************************************/
#import "OCException.h"
@implementation OCException
@end
-- OCCatTest.m 測試類 :
/*************************************************************************
> File Name: OCCatTest.m
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 一 10/ 5 16:38:01 2015
************************************************************************/
#import "OCException.h"
int main(int argc, char * argv[])
{
@autoreleasepool
{
@throw [[OCException alloc]
initWithName : @"OCException"
reason : @"this reason is imporant"
userInfo : nil];
}
}
-- 執行結果 :
bogon:6.5 octopus$ clang -fobjc-arc -framework Foundation OCException.m OCCatTest.m
bogon:6.5 octopus$ ./a.out
2015-10-05 17:04:12.432 a.out[3040:507] *** Terminating app due to uncaught exception 'OCException', reason: 'this reason is imporant'
*** First throw call stack:
(
0 CoreFoundation 0x00007fff903dd25c __exceptionPreprocess + 172
1 libobjc.A.dylib 0x00007fff8ecdbe75 objc_exception_throw + 43
2 a.out 0x00000001062abef7 main + 135
3 libdyld.dylib 0x00007fff851f35fd start + 1
)
libc++abi.dylib: terminating with uncaught exception of type OCException
Abort trap: 6
五. Objective-C 反射
1. 擷取 Class
(1) 程式 與 環境 互動方式
程式 與 運作環境互動方式 :
-- 通過 OC 源碼 : 編寫 OC 源碼, 編譯器編譯, 運作在運作環境中;
-- 通過 NSObject 動态程式設計 : NSObject 是所有類的基類, 所有對象都可以直接調用 NSObject 方法;
-- 調用 運作時函數 動态程式設計 : 運作時系統是動态庫, 可以直接調用這些動态共享庫;
(2) 擷取 Class 方式
擷取 Class 方式 :
-- 通過類名 : 使用 "Class NSClassFromString (NSString * aClassName)" 函數擷取 Class 對象, 傳入 類名 字元串;
-- class 類方法 : 調用類方法 class, 調用方式 [NSString class];
-- class 對象方法 : 調用對象的 class 方法, 調用方式 [@"hello" class];
-- 推薦使用第二種方式 : 代碼更安全, 編譯階段就可以檢查 Class 是否存在, 程式性能高;
(3) 擷取 Class 代碼示例
代碼示例 :
/*************************************************************************
> File Name: OCGetClass.m
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 一 10/ 5 23:31:51 2015
************************************************************************/
#import <Foundation/Foundation.h>
int main(int argc, char * argv[])
{
@autoreleasepool {
//通過 NSClassFromString 方法傳入 類名字元串 擷取 Class 對象
Class clazz = NSClassFromString(@"NSDate");
NSLog(@"%@", clazz);
id date = [[clazz alloc] init];
NSLog(@"date : %@", date);
NSString * str = @"hello";
// 通過調用 類 或 對象的 class 方法
NSLog(@"[str class] : %@, [NSString class] : %@", [str class], [NSString class]);
// 通過調用 類 或 對象的 getter 方法擷取, 即用 . 方法擷取
NSLog(@"str.class : %@, NSString.class : %@", str.class, NSString.class);
}
}
-- 執行結果 :
bogon:6.6 octopus$ clang -fobjc-arc -framework Foundation OCGetClass.m
bogon:6.6 octopus$ ./a.out
2015-10-05 23:39:28.692 a.out[3237:507] NSDate
2015-10-05 23:39:28.699 a.out[3237:507] date : 2015-10-05 15:39:28 +0000
2015-10-05 23:39:28.700 a.out[3237:507] [str class] : __NSCFConstantString, [NSString class] : NSString
2015-10-05 23:39:28.700 a.out[3237:507] str.class : __NSCFConstantString, NSString.class : NSString
2. 檢查繼承關系
(1) 繼承關系判斷
繼承關系判斷方法 :
-- 判斷類 : isMemberOfClass 方法, 傳入 Class 對象, 判斷該對象是否是 Class 對象對應類的執行個體;
-- 判斷類或子類 : isKindOfClass 方法, 傳入 Class 對象, 判斷該對象是否是 Class 對象對應類 或 子類的執行個體;
-- 判斷協定 : conformsToProtocol 犯法, 傳入 Protocol 參數, 傳入方法 "@protocol(協定名稱)" 或者 "Protocol * NSProtocolFromString(NSString * @"協定名稱")" 兩種方法擷取協定參數;
(2) 繼承關系判斷代碼示例
源碼示例 :
-- OCAnimal.h : 定義協定;
/*************************************************************************
> File Name: OCAnimal.h
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 一 10/ 5 23:54:14 2015
************************************************************************/
#import <Foundation/Foundation.h>
@protocol OCAnimal
- (void) name;
@end
-- OCCat.h : 定義接口;
/*************************************************************************
> File Name: OCCat.h
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 一 10/ 5 23:56:16 2015
************************************************************************/
#import "OCAnimal.h"
@interface OCCat : NSObject <OCAnimal>
@end
-- OCCat.m : 定義實作類;
/*************************************************************************
> File Name: OCCat.m
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 一 10/ 5 23:58:47 2015
************************************************************************/
#import "OCCat.h"
@implementation OCCat
- (void) name
{
NSLog(@"My name is Tom");
}
@end
-- OCCatMain.m : 測試類;
/*************************************************************************
> File Name: OCCatMain.m
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 二 10/ 6 00:00:01 2015
************************************************************************/
#import "OCCat.h"
int main(int argc, char * argv[])
{
@autoreleasepool {
OCCat * cat = [[OCCat alloc] init];
NSLog(@"%@", cat.class);
NSLog(@"cat isMemberOfClass OCCat : %d", [cat isMemberOfClass : OCCat.class]);
NSLog(@"cat isMemberOfClass NSObject : %d", [cat isMemberOfClass : NSObject.class]);
NSLog(@"cat isKindOfClass OCCat : %d", [cat isKindOfClass : OCCat.class]);
NSLog(@"cat isKindOfClass OCCat : %d", [cat isKindOfClass : NSObject.class]);
NSLog(@"cat conformsToProtocol OCAnimal : %d", [cat conformsToProtocol : @protocol(OCAnimal)]);
}
}
-- 執行結果 :
bogon:6.6 octopus$ clang -fobjc-arc -framework Foundation OCCat.m OCCatMain.m
bogon:6.6 octopus$ ./a.out
2015-10-06 00:07:56.838 a.out[3337:507] OCCat
2015-10-06 00:07:56.840 a.out[3337:507] cat isMemberOfClass OCCat : 1
2015-10-06 00:07:56.840 a.out[3337:507] cat isMemberOfClass NSObject : 0
2015-10-06 00:07:56.841 a.out[3337:507] cat isKindOfClass OCCat : 1
2015-10-06 00:07:56.841 a.out[3337:507] cat isKindOfClass OCCat : 1
2015-10-06 00:07:56.842 a.out[3337:507] cat conformsToProtocol OCAnimal : 1
3. 動态調用方法
(1) 動态調用成員變量
KVC 機制 : 通過該機制可以動态調用對象的 getter 和 setter 方法, 不論 該變量定義位置 (接口 | 實作) 和 使用何種通路控制符 (private | public), 都可以使用 KVC 通路;
(2) 判斷方法是否可調用
判斷對象是否可以調用方法 : NSObject 中定義了 respondsToSelector : 方法, 該方法傳入 SEL 參數, 該參數代表方法, 如果可以調用 傳回 YES, 反之 傳回 NO;
擷取 SEL 對象方法 :
-- 指令擷取 : 使用 @selector 指令擷取目前類中指定的方法, 參數是 完整的方法簽名關鍵字, 隻有方法名不夠;
@selector(setAge:) withObject
-- 方法擷取 : 使用 SEL NSSelectorFromString(NSString * aSelectorName) 函數, 根據方法簽名關鍵字字元串擷取對應方法;
NSSelectorFromString(@"setAge:")
(3) SEL 動态調用方法
動态調用對象方法 :
-- 動态調用一 : NSObject 的 performSelector : 方法可以調用方法, 需要傳入 SEL 對象, 傳入參數 可以通過 withObject: 标簽傳入參數;
[student performSelector : @selector(setAge:) withObject : [NSNumber numberWithInt : 18]];
[student performSelector : NSSelectorFromString(@"setAge:") withObject : [NSNumber numberWithInt : 19]];
-- 動态調用二 : objc_msgSend(receiver, selector, ...) 函數調用方法, 參數一 方法調用者, 參數二 調用的方法, 剩餘參數 方法參數;
objc_msgSend (student, @selector(setAge:), [NSNumber numberWithInt : 20]);
objc_msgSend (student, NSSelectorFromString(@"setAge:"), [NSNumber numberWithInt : 21]);
-- 注意 : 使用第二種方法需要導入包 "#import <objc/message.h>", 傳回浮點數時調用 objc_msgSend_fpret(), 傳回結構體資料時 使用 objc_msgSend_stret() 函數;
(4) IMP 動态調用方法
IMP 動态調用方法 簡介 :
-- 擷取 IMP 對象 : NSObject 定義了 "- (IMP) methodForSelector : (SEL) aSelector :" 方法, 該方法傳入 SEL 參數, 傳回 IMP 對象;
-- IMP 作用 : IMP 是 OC 方法函數指針變量, 代表函數入口, 通過 IMP 也可以調用函數;
-- IMP 調用方法文法 : "傳回值類型 (*指針變量) (id, SEL, ...)" , 參數一 方法調用者, 參數二 方法 SEL 對象, 後面參數是方法參數;
(5) 動态調用方法 示例代碼
示例代碼 :
-- OCStudent.m : 接口實作類主函數一體;
/*************************************************************************
> File Name: OCStudent.m
> Author: octopus
> Mail: octopus_truth.163.com
> Created Time: 二 10/ 6 11:19:49 2015
************************************************************************/
#import <Foundation/Foundation.h>
#import <objc/message.h>
@interface OCStudent : NSObject
@end
@implementation OCStudent
- (void) setAge : (NSNumber *) age
{
int age_num = [age intValue];
NSLog(@"age is : %d", age_num);
}
@end
int main(int argc, char * argv[])
{
@autoreleasepool
{
OCStudent * student = [[OCStudent alloc] init];
[student performSelector : @selector(setAge:) withObject : [NSNumber numberWithInt : 18]];
[student performSelector : NSSelectorFromString(@"setAge:") withObject : [NSNumber numberWithInt : 19]];
objc_msgSend (student, @selector(setAge:), [NSNumber numberWithInt : 20]);
objc_msgSend (student, NSSelectorFromString(@"setAge:"), [NSNumber numberWithInt : 21]);
}
}
-- 執行結果 : 有報警;
bogon:6.6 octopus$ clang -fobjc-arc -framework Foundation OCStudent.m
OCStudent.m:29:12: warning: performSelector may cause a leak because its selector is unknown [-Warc-performSelector-leaks]
[student performSelector : NSSelectorFromString(@"setAge:") withObject : [NSNumber numberWithInt : 19]];
^
OCStudent.m:29:30: note: used here
[student performSelector : NSSelectorFromString(@"setAge:") withObject : [NSNumber numberWithInt : 19]];
^
1 warning generated.
bogon:6.6 octopus$ ./a.out
2015-10-06 12:00:41.669 a.out[747:507] age is : 18
2015-10-06 12:00:41.671 a.out[747:507] age is : 19
2015-10-06 12:00:41.671 a.out[747:507] age is : 20
2015-10-06 12:00:41.672 a.out[747:507] age is : 21