你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> [IOS 開發]Objective - C 面向對象 - 方法 | 成員變量 | 隱藏封裝 | KVC | KVO | 初始化 | 多態

[IOS 開發]Objective - C 面向對象 - 方法 | 成員變量 | 隱藏封裝 | KVC | KVO | 初始化 | 多態

編輯:IOS開發綜合

 

 

 

 

 

 

一. Objective-C 方法詳解

 

 

 

 

1. 方法屬性

 

 

 

(1) OC 方法傳參機制



Object-C 方法傳參機制 : OC 中得參數傳遞都是值傳遞, 傳入參數的是參數的副本;

-- 基本類型 (值傳遞) : int 等基本類型直接傳入 這些基本類型的的副本;

-- 指針類型 (地址傳遞) : 使用指針變量作為參數, 傳遞的也是指針變量的副本, 但是這個副本本身的值是一個地址, 地址 變量 和 地址 變量的副本 還是指向同一個地址;

 

 

 

(2) OC 方法 與 傳統函數

 

 

方法 與 傳統函數 :

-- 結構化編程語言 : 整個軟件由一個函數構成, 如 C 語言, 一個 main 函數就是整個軟件架構;

-- 面向對象語言 : 整個軟件由 類 組成, 軟件中的 方法 必須屬於類, 不能獨立存在;

 

 

 

(3) 類方法 與 實例方法

 

 

方法定義 : 方法只能定義在類中, 不能獨立定義;

-- 類方法 : 使用 + 標識, 這個方法屬於類方法, 使用 [類 方法] 調用;

-- 實例方法 : 使用 - 標識, 該方法屬於實例方法, 使用 [對象 方法] 調用;

 

 

 

(4) 方法屬性

 

 

方法屬性 :

-- 不獨立存在 : 不能獨立定義, 只能在類中定義;

-- 類 對象 : 方法要麼屬於類, 要麼屬於對象;

-- 方法執行 : 方法不能獨立執行, 需要使用 類 或 對象作為調用者;

 

 

 

2. 形參個數可變的方法


 

 

形參可變方法簡介 :

-- 方法定義 : 最後一個形參後增加 逗號 和 三點 , ... ;

-- 示例 : NSLog() 函數可以傳入任意多參數, 該函數就是形參個數可變的方法;

 

示例代碼 :

-- 接口 :

 

/*************************************************************************
    > File Name: Varargs.h
    > Author: octopus
    > Mail: octopus_truth.163.com 
    > Created Time: 日  9/27 22:04:34 2015
 ************************************************************************/
#import 

@interface Varargs : NSObject

/*
 * 形參個數可變方法
 * 在 NSString * 類型參數後面加上 ,... 一個逗號 三個點號, 
 * 表明該方法還可以接受 n 個 NSString * 類型的參數
 */
-(void) varargs_test : (NSString *) str, ...;

@end

 

-- 實現 :

 

/*************************************************************************
    > File Name: Varargs.m
    > Author: octopus
    > Mail: octopus_truth.163.com 
    > Created Time: 日  9/27 22:24:55 2015
 ************************************************************************/

#import Varargs.h

@implementation Varargs

-(void) varargs_test : (NSString *) str, ...
{
	/*
	 * va_list 類型 : 用於指向可變參數列表的指針變量
	 */
	va_list argList;
	// str 如果為 null 的話, 地址就會為0
	if(str)
	{
		NSLog(@%@, str);
		/*
		 * 開始處理可變形參的列表, 
		 * 並讓指針變量指向可變形參列表的第一個參數
		 * 開始提取可變參數列表的參數
		 */
		va_start(argList, str);
		/*
		 * va_arg : 提取指針當前指向的參數, 
		 * 並將指針移動到下一個參數
		 */
		NSString * arg = va_arg(argList, id);
		//如果指針指向的參數 不為 nil , 則進入循環體
		while(arg)
		{
			NSLog(@%@, arg);
			arg = va_arg(argList, id);
		}
		/*
		 * var_end : 結束處理可變形參, 釋放指針變量
		 */
		va_end(argList);
	}
}

@end


 

-- main 函數 :

 

/*************************************************************************
    > File Name: VarargsTest.m
    > Author: octopus
    > Mail: octopus_truth.163.com 
    > Created Time: 日  9/27 22:33:09 2015
 ************************************************************************/

#import Varargs.h

int main(int argc, char * argv[])
{
	@autoreleasepool {
		Varargs * va = [[Varargs alloc]init];
		[va varargs_test : @參數一, @參數二, @參數三, @參數四, nil];
	}
}

-- 執行過程 : 執行 clang -fobjc-arc -framework Foundation Varargs.m VarargsTest.m 命令;

 

 

octopus-2:oc_object octopus$ clang -fobjc-arc -framework Foundation Varargs.m VarargsTest.m 
octopus-2:oc_object octopus$ ./a.out 
2015-09-30 08:58:55.177 a.out[2823:507] 參數一
2015-09-30 08:58:55.180 a.out[2823:507] 參數二
2015-09-30 08:58:55.180 a.out[2823:507] 參數三
2015-09-30 08:58:55.181 a.out[2823:507] 參數四
octopus-2:oc_object octopus$ 


 

 

 

二. Object-C 成員變量


 

 

 

 

1. 成員變量機制

 

 

 

 

(1) 變量分類

 

變量分類 : 成員變量, 局部變量, 全局變量;

-- 局部變量 : 在函數裡面定義的變量;

-- 全局變量 : 在函數外定義的變量;

-- 成員變量 : 在下面講解;

 

 

(2) 成員變量簡介

 

 

成員變量 : 在類 接口 或 實現 部分定義的變量, 都是實例變量, 不支持真正的類變量;

-- 實例變量 : 類對象被創建開始存在, 系統銷毀對象, 實例變量就會隨之銷毀;

-- 實例變量訪問 : 實例->實例變量;

 

 

(3) 成員變量 源碼示例

 

 

示例源碼 :

 

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com 
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/

#import 

@interface OCPerson : NSObject
{
	//定義實例變量
	@public
	NSString * _name;
	int _age;
}
@end
@implementation OCPerson
@end

int main(int argc, char * argv[])
{
	@autoreleasepool {
		//創建成員變量
		OCPerson *p =[[OCPerson alloc] init];
		//通過指針變量訪問兩個實例變量
		NSLog(@p->_name : %@ , p->_age : %d, p->_name, p->_age);

		//通過指針訪問變量為兩個成員變量賦值
		p->_name = @Jim;
		p->_age = 18;
		NSLog(@p->_name : %@ , p->_age : %d, p->_name, p->_age);

		OCPerson *p1 = [[OCPerson alloc] init];
		OCPerson *p2 = [[OCPerson alloc] init];
		p1->_name = @Tom;
		p2->_name = @Keen;
		NSLog(@p1->_name : %@ , p2->_name : %@, p1->_name, p2->_name);
	}
}

-- 執行結果 :

 

 

octopus-2:oc_object octopus$ clang -fobjc-arc -framework Foundation OCPerson.m 
octopus-2:oc_object octopus$ ./a.out 
2015-09-30 09:17:38.510 a.out[2858:507] p->_name : (null) , p->_age : 0
2015-09-30 09:17:38.513 a.out[2858:507] p->_name : Jim , p->_age : 18
2015-09-30 09:17:38.513 a.out[2858:507] p1->_name : Tom , p2->_name : Keen
octopus-2:oc_object octopus$ 


 

 

(4) 成員變量 源碼示例解析

 

 

示例解析 :

-- 成員變量初始化 : 創建對象時系統會為為成員變量賦初始值, 基礎類型 0, 指針類型 nil;

-- 初始化過程 : 執行 OCPerson *p1 = [[OCPerson alloc] init] 語句時 系統為 OCPerson 對象的成員變量分配內存空間, 並初始化, 並將對象賦給 p1 變量;

 

 

 

2. 模擬類變量


 

 

前提 : Object-C 不支持 類變量, 但是可以使用 全局變量來模擬類變量;

 

static 關鍵字 : 不能修飾成員變量, 只能修飾 局部變量 和 全局變量;

-- 修飾局部變量 : 將局部變量存儲到靜態存儲區;

-- 修飾全局變量 : 限制全局變量只能在當前文件中訪問;

-- 修飾函數 : 限制該函數只能在當前源文件中訪問;

 

模擬類變量方式 : static 修飾全局變量, 提供一個類方法暴露該全局變量, 並提供一個類方法暴露該全局變量;

 

代碼示例 : 根據上面的示例代碼修改而來;

 

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com 
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/

#import 

@interface OCPerson : NSObject
{
	//定義實例變量
	@public
	NSString * _name;
	int _age;
}

/*
 * 聲明的 類方法 用於獲取類變量
 */
+ (NSString *) getInstance;
/*
 * 聲明的 類方法 用於設置類變量
 */
+ (void) setInstance : (NSString *) instanc;
@end
@implementation OCPerson

/*
 * 定義的類變量, 使用 static 修飾全局變量
 */
static NSString * _instance = nil;

/**
 * 獲取類變量
 */
+ (NSString *) getInstance
{
	return _instance;
}

/**
 * 設置類變量
 */
+(void) setInstance : (NSString *) instance
{
	_instance = instance;
}

@end

int main(int argc, char * argv[])
{
	@autoreleasepool {
		//創建成員變量
		OCPerson *p =[[OCPerson alloc] init];
		//通過指針變量訪問兩個實例變量
		NSLog(@p->_name : %@ , p->_age : %d, p->_name, p->_age);

		//通過指針訪問變量為兩個成員變量賦值
		p->_name = @Jim;
		p->_age = 18;
		NSLog(@p->_name : %@ , p->_age : %d, p->_name, p->_age);

		OCPerson *p1 = [[OCPerson alloc] init];
		OCPerson *p2 = [[OCPerson alloc] init];
		p1->_name = @Tom;
		p2->_name = @Keen;
		NSLog(@p1->_name : %@ , p2->_name : %@, p1->_name, p2->_name);

		[OCPerson setInstance : @INSTANCE];
		NSLog(@instance : %@, [OCPerson getInstance]);
	}
}

-- 執行結果 :

 

 

octopus-2:oc_object octopus$ clang -fobjc-arc -framework Foundation OCPerson.m 
octopus-2:oc_object octopus$ ./a.out 
2015-09-30 10:14:35.235 a.out[2955:507] p->_name : (null) , p->_age : 0
2015-09-30 10:14:35.237 a.out[2955:507] p->_name : Jim , p->_age : 18
2015-09-30 10:14:35.238 a.out[2955:507] p1->_name : Tom , p2->_name : Keen
2015-09-30 10:14:35.238 a.out[2955:507] instance : INSTANCE
octopus-2:oc_object octopus$ 


 

 

 

3. 單例模式


 

實現單例 : 定義一個 static 全局變量, 該變量用於保存自己創建的 Singleton 對象, 每次程序獲取該單例時, 判斷 static singleton 是否為nil, 全局變量為 nil 初始化一個實例並賦值給 static 變量;

 

單例模式源碼示例 :

 

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com 
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/

#import 

@interface OCPerson : NSObject
{
	//定義實例變量
	@public
	NSString * _name;
	int _age;
}

/*
 * 聲明的 類方法 用於獲取類變量
 */
+ (NSString *) getInstance;
/*
 * 聲明的 類方法 用於設置類變量
 */
+ (void) setInstance : (NSString *) instanc;

/**
 * 單例對象獲取方法
 */
+ (OCPerson *) getSingleton;

@end
@implementation OCPerson

/*
 * 定義的類變量, 使用 static 修飾全局變量
 */
static NSString * _instance = nil;

/*
 * 定義單例對象類
 */
static OCPerson * singleton = nil;

/**
 * 獲取類變量
 */
+ (NSString *) getInstance
{
	return _instance;
}

/**
 * 設置類變量
 */
+(void) setInstance : (NSString *) instance
{
	_instance = instance;
}

/**
 * 獲取單例對象
 */
+ (OCPerson *) getSingleton
{
	if(!singleton)
	{
		singleton = [[OCPerson alloc] init];
	}
	return singleton;
}

@end

int main(int argc, char * argv[])
{
	@autoreleasepool {
		//創建成員變量
		OCPerson *p =[[OCPerson alloc] init];
		//通過指針變量訪問兩個實例變量
		NSLog(@p->_name : %@ , p->_age : %d, p->_name, p->_age);

		//通過指針訪問變量為兩個成員變量賦值
		p->_name = @Jim;
		p->_age = 18;
		NSLog(@p->_name : %@ , p->_age : %d, p->_name, p->_age);

		/*
		 * 創建兩個變量
		 */
		OCPerson *p1 = [[OCPerson alloc] init];
		OCPerson *p2 = [[OCPerson alloc] init];
		p1->_name = @Tom;
		p2->_name = @Keen;
		NSLog(@p1->_name : %@ , p2->_name : %@, p1->_name, p2->_name);

		/*
		 * 設置並獲取靜態變量
		 */
		[OCPerson setInstance : @INSTANCE];
		NSLog(@instance : %@, [OCPerson getInstance]);
		
		/*
		 * 獲取兩次單例對象, 比較這兩個對象是否相等
		 */
		NSLog(@%d, [OCPerson getSingleton] == [OCPerson getSingleton]);
	}
}

-- 執行效果 :

 

 

octopus-2:oc_object octopus$ clang -fobjc-arc -framework Foundation OCPerson.m 
octopus-2:oc_object octopus$ ./a.out 
2015-09-30 10:31:38.935 a.out[2974:507] p->_name : (null) , p->_age : 0
2015-09-30 10:31:38.937 a.out[2974:507] p->_name : Jim , p->_age : 18
2015-09-30 10:31:38.938 a.out[2974:507] p1->_name : Tom , p2->_name : Keen
2015-09-30 10:31:38.939 a.out[2974:507] instance : INSTANCE
2015-09-30 10:31:38.939 a.out[2974:507] 1


 

 

 

三. 隱藏 封裝


 

 

 

 

 

1. 封裝簡介

 

 

封裝 : 面向對象三個特性 封裝, 繼承, 多態;

-- 概念 : 將對象的 狀態信息 隱藏在對象內部, 不允許外界 直接訪問內部信息, 外部只能通過 類提供的方法 來實現對內部信息的訪問 操作;

 

 

 

2. 訪問控制符使用詳解

 

 

 

 

(1) 訪問控制符界別范圍

 

訪問控制符控制級別 : @private < (@package | @protected) < @public ;

-- @private : 只能在當前類訪問, 用於徹底隱藏成員變量, 類實現部分定義的成員變量默認是 @private ;

-- @package : 只能在當前映像訪問, 可以在當前類 或者 當前映像的 任意位置訪問, 用於部分隱藏成員變量;

-- @protected : 子類訪問, 可以在當前類, 子類 任意位置訪問, 類接口部分定義的成員變量默認使用 @protected 訪問;

-- @public : 可以在任意位置訪問;

 

訪問控制符范圍 :

 

  @private @package @protected @public 同一個類中 可訪問 可訪問 可訪問 可訪問 通映像中   可訪問   可訪問 子類中     可訪問 可訪問 全局任意位置       可訪問

 

 

 

(2) 訪問控制符注意點 和 原則


 

訪問控制符注意點 :

-- 注意 : 訪問控制符只能控制成員變量是否可以被其它類訪問, 不能用於修飾局部變量;

-- 訪問控制符控制范圍 : 從訪問控制符出現位置開始 到 下一個訪問控制符 或者 花括號之間的成員變量;

-- getter 和 setter 方法 : 去掉成員變量的下劃線前綴, _name 對應 setName() name();

 

基本原則 :

-- 修飾成員變量方法 : 類中 99% 的變量都應該使用 @private 控制, 用於輔助實現類其它方法的工具方法也要使用 @private 修飾, 定義在實現類內部;

-- 子類訪問 : 父類希望其成員變量能被子類訪問, 使用 @protected 控制該成員變量;

-- 接口默認 public 方法 : 暴露給其它類自由調用的方法, 在類接口中定義, 在類實現中實現它們;

 

 

 

(3) 訪問控制符源碼示例


 

示例源碼 :

 

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com 
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/

#import 

@interface OCPerson : NSObject
{
	/*
	 * 定義示例變量, 將這些變量定義成 private 類型
	 * 該實例變量只能在 OCPerson.m 中使用
	 */
	@private
	NSString * _name;
	int _age;
}

/*
 * 下面得四個方法是存取方法
 */
- (void) setName : (NSString *) name;
- (NSString *) name;
- (void) setAge : (int) age;
- (int) age;

/*
 * 聲明的 類方法 用於獲取類變量
 */
+ (NSString *) getInstance;
/*
 * 聲明的 類方法 用於設置類變量
 */
+ (void) setInstance : (NSString *) instanc;

/**
 * 單例對象獲取方法
 */
+ (OCPerson *) getSingleton;

@end
@implementation OCPerson

/*
 * 定義的類變量, 使用 static 修飾全局變量
 */
static NSString * _instance = nil;

/*
 * 定義單例對象類
 */
static OCPerson * singleton = nil;


- (void) setName : (NSString *) name
{
	_name = name;
}

- (NSString *) name
{
	return _name;
}

- (void) setAge : (int) age
{
	_age = age;
}

- (int) age
{
	return _age;
}




/**
 * 獲取類變量
 */
+ (NSString *) getInstance
{
	return _instance;
}

/**
 * 設置類變量
 */
+(void) setInstance : (NSString *) instance
{
	_instance = instance;
}

/**
 * 獲取單例對象
 */
+ (OCPerson *) getSingleton
{
	if(!singleton)
	{
		singleton = [[OCPerson alloc] init];
	}
	return singleton;
}

@end

int main(int argc, char * argv[])
{
	@autoreleasepool {
		/*
		 * 獲取單例對象
		 */
		OCPerson * p = [OCPerson getSingleton];

		/*
		 * 使用 setter 方法設置 _name 和 _age 成員變量
		 */
		[p setAge : 18];
		[p setName : @Tom];

		/*
		 * 使用 getter 方法 age() 和 name() 獲取 _age 和 _name 成員變量值
		 */
		NSLog(@name : %@ , age : %d, [p name], [p age]);
	}
}

-- 執行結果 :

 

 

octopus-2:oc_object octopus$ clang -fobjc-arc -framework Foundation OCPerson.m 
octopus-2:oc_object octopus$ ./a.out 
2015-09-30 11:45:13.251 a.out[3096:507] name : Tom , age : 18


 

 

 

3. @package 訪問控制符簡介

 

 

 

常用的訪問控制符 : @private 將成員變量限制在當前類內部, @public 徹底暴露成員變量, @protected 讓成員變量在子類中可以訪問;

 

映像 : 編譯後生成的框架 和 執行文件, 編譯後 @package 修飾的成員變量 在這些 框架 和 可執行文件中可以被任意訪問;

-- 示例 : 我們之前經常使用類似命令 clang -fobjc-arc -framework Foundation OCPerson.m , 該命令生成一個 a.out 文件, 該 a.out 就是一個映像;

 

 

 

4. 合成存取方法

 

 

 

(1) @property 和 @synthesize 指令


 

自動 合成 getter 和 setter 方法 :

-- 合成方法 : 接口部分使用 @property 指令定義屬性, 如 @property int age; 實現部分使用 @synthesize 聲明, 如 @synthesize age;

-- 生成成員變量 : 使用 @property 和 @synthesize 指令聲明屬性後, 會合成對應的 getter 和 setter 方法, 自動在類實現部分定義一個與 getter 方法同名的成員變量;

 

@synthesize 指令 : @property NSString * name; @synthesize name = _name;

-- 上面的代碼作用 : 使用 @property 合成的存取方法對應的是 _name, 不是 name;

-- @synthesize 語法 : @synthesize propertyName [= fileName];

-- 默認成員變量 : @synthesize 不指定 filedName 時, 成員變量默認為屬性名添加下劃線; 如 @property NSString * name; @synthesize name; 默認對應的成員變量是 name; 如果 @synthesize name = _name; 默認對應的成員變量是 _name;

 

 

示例源碼 :

 

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com 
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/

#import 

@interface OCPerson : NSObject
/*
 * 定義示例變量, 將這些變量定義成 private 類型
 * 該實例變量只能在 OCPerson.m 中使用
 */
@property (nonatomic) NSString * name;
@property int age;


/*
 * 聲明的 類方法 用於獲取類變量
 */
+ (NSString *) getInstance;
/*
 * 聲明的 類方法 用於設置類變量
 */
+ (void) setInstance : (NSString *) instanc;

/**
 * 單例對象獲取方法
 */
+ (OCPerson *) getSingleton;

@end
@implementation OCPerson

/*
 * 定義的類變量, 使用 static 修飾全局變量
 */
static NSString * _instance = nil;

/*
 * 定義單例對象類
 */
static OCPerson * singleton = nil;

/*
 * 如果 沒有後面的 = _name 和 = _age 提示,默認的成員變量 是 name 和 age
 * 當前設置對應的默認成員變量是 _name 和 _age
 */
@synthesize name = _name;
@synthesize age = _age;


- (void) setName : (NSString *) name
{
	//默認的成員變量是 _name
	self->_name = [NSString stringWithFormat : @++%@++, name];
}

/**
 * 獲取類變量
 */
+ (NSString *) getInstance
{
	return _instance;
}

/**
 * 設置類變量
 */
+(void) setInstance : (NSString *) instance
{
	_instance = instance;
}

/**
 * 獲取單例對象
 */
+ (OCPerson *) getSingleton
{
	if(!singleton)
	{
		singleton = [[OCPerson alloc] init];
	}
	return singleton;
}

@end

int main(int argc, char * argv[])
{
	@autoreleasepool {
		/*
		 * 獲取單例對象
		 */
		OCPerson * p = [OCPerson getSingleton];

		/*
		 * 使用 setter 方法設置 _name 和 _age 成員變量
		 */
		[p setAge : 18];
		[p setName : @Tom];

		/*
		 * 使用 getter 方法 age() 和 name() 獲取 _age 和 _name 成員變量值
		 */
		NSLog(@name : %@ , age : %d, [p name], [p age]);
	}
}

-- 執行結果 :

 

 

octopus-2:oc_object octopus$ ./a.out 
2015-09-30 13:29:26.323 a.out[3198:507] name : ++Tom++ , age : 18


 

 

 

5. @Property 特殊指示符

 

 

 

(1) assign 特殊指示符

 

 

assign 指示符 :

-- 作用 : 指定對屬性只是簡單賦值, 不更改引用計數, 主要適用於 NSInteger int short double 結構體 等數據類型;

-- 引用計數 : 對象的引用計數大於 0 時, 該對象不會被回收, 基礎數據類型不存在回收問題, 可以使用 assign 指示符;

 

 

(2) atomic (nonatomic) 特殊指示符

 

 

atomic (nonatomic) 指示符 :

-- 作用 : 指定合成的存取方法是否是原子操作, 即線程是否安全;

-- atomic : 合成的存取方法都是線程安全的, 一個線程調用存取方法時, 其它方法無法調用存取方法, 避免多線程並發破壞對象的數據完整性;

-- nonatomic : 用於提高存取方法的訪問性能, atomic 線程安全會造成性能下降;

 

 

 

(3) copy 特殊指示符

 

 

copy 指示符 :

-- 作用 : 如果使用 copy 指示符, 當調用 setter 方法對成員變量賦值時, 現將被賦值對象復制一個副本, 再將該副本賦給成員變量;

-- 引用計數 : copy 會將原成員變量所引用計數 -1;

-- 適用情況 : 成員變量類型是指針類型時, 被賦值的對象有可能在賦值之後被修改, 如果不想讓被賦值對象被修改影響成員變量, 可以使用 copy 指示符;

-- 代碼示例 :

 

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com 
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/

#import 

@interface OCPerson : NSObject
/*
 * 定義示例變量, 將這些變量定義成 private 類型
 * 該實例變量只能在 OCPerson.m 中使用
 */
@property (nonatomic) NSString * name;
@property (nonatomic, copy) NSString * describe;
@property int age;
@end
@implementation OCPerson

/*
 * 如果 沒有後面的 = _name 和 = _age 提示,默認的成員變量 是 name 和 age
 * 當前設置對應的默認成員變量是 _name 和 _age
 */
@synthesize name = _name;
@synthesize describe = _describe;
@synthesize age = _age;

@end

int main(int argc, char * argv[])
{
	@autoreleasepool {
		//初始化對象
		OCPerson * p = [[OCPerson alloc] init];
		//創建一個字符串
		NSMutableString *str = [NSMutableString stringWithString : @Tom];
		//將字符串設置給對象的 name 屬性
		[p setName:str];
		[p setDescribe:str];
		//打印出name成員變量值
		NSLog(@1.name : %@, describe : %@, [p name], [p describe]);
		
		//修改被賦值的對象
		[str appendString : @ Hax];
		//打印出name成員變量值
		NSLog(@2.name : %@, describe : %@, [p name], [p describe]);
	}
}

-- 執行結果 :

 

 

octopus-2:oc_object octopus$ clang -fobjc-arc -framework Foundation OCPerson.m 
octopus-2:oc_object octopus$ ./a.out 
2015-09-30 18:05:48.170 a.out[3295:507] 1.name : Tom, describe : Tom
2015-09-30 18:05:48.172 a.out[3295:507] 2.name : Tom Hax, describe : Tom

 

 

 

(4) getter setter 特殊指示符


 

getter(setter) 指示符 :

-- 作用 : getter 和 setter 方法指定自定義方法名;

-- 示例 : getter = han 指定 getter 方法為 han, setter = octopus 指定 setter 方法為 octopus;

-- 源碼示例 :

 

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com 
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/

#import 

@interface OCPerson : NSObject
/*
 * 定義示例變量, 將這些變量定義成 private 類型
 * 該實例變量只能在 OCPerson.m 中使用
 */
@property (nonatomic) NSString * name;
@property (nonatomic, copy) NSString * describe;
@property (assign, nonatomic, getter = han, setter = octopus:) int age;
@end
@implementation OCPerson

/*
 * 如果 沒有後面的 = _name 和 = _age 提示,默認的成員變量 是 name 和 age
 * 當前設置對應的默認成員變量是 _name 和 _age
 */
@synthesize name = _name;
@synthesize describe = _describe;
@synthesize age = _age;

@end

int main(int argc, char * argv[])
{
	@autoreleasepool {
		OCPerson * p = [[OCPerson alloc] init];
		NSMutableString *str = [NSMutableString stringWithString : @Tom];
		[p setName:str];
		[p setDescribe:str];
		NSLog(@1.name : %@, describe : %@, [p name], [p describe]);
		
		[str appendString : @ Hax];
		NSLog(@2.name : %@, describe : %@, [p name], [p describe]);

		[p octopus:18];
		NSLog(@age : %d, [p han]);
	}
}

-- 執行結果 :

 

 

octopus-2:oc_object octopus$ clang -fobjc-arc -framework Foundation OCPerson.m 
octopus-2:oc_object octopus$ ./a.out 
2015-09-30 18:28:16.132 a.out[3329:507] 1.name : Tom, describe : Tom
2015-09-30 18:28:16.135 a.out[3329:507] 2.name : Tom Hax, describe : Tom
2015-09-30 18:28:16.135 a.out[3329:507] age : 18

 

 

(5) readonly readwrite 特殊指示符


 

readonly : 系統只合成 getter 方法, 不再合成 setter 方法;

readwrite : 需要合成 setter getter 方法;

 

 

(6) retain 特殊指示符


 

retain :

-- 作用 : retain 定義屬性, 將某個對象賦值給該屬性時, 該屬性原來所引用的對象引用計數 -1, 被賦值對象 (成員變量) 引用計數 +1;

-- 使用場景 : 在未啟用 ARC 機制情況下, 常用, 啟用後不常用;

-- 源碼示例 : 不能使用 @autoreleasepool ARC 機制, 需要關閉該機制;

 

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com 
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/

#import 

@interface OCPerson : NSObject
@property (nonatomic, retain) NSDate * date;
@end
@implementation OCPerson
@synthesize date;
@end

int main(int argc, char * argv[])
{
	OCPerson * p = [[OCPerson alloc] init];
	NSDate * date = [[NSDate alloc] init];

	NSLog(@date retainCount : %ld, _date retainCount : %ld, date.retainCount, [p date].retainCount);
	[p setDate : date];
	NSLog(@date retainCount : %ld, _date retainCount : %ld, date.retainCount, [p date].retainCount);
	[date release];
	NSLog(@date retainCount : %ld, _date retainCount : %ld, date.retainCount, [p date].retainCount);
}

-- 執行結果 : 注意編譯時不能使用 -fobjc-arc 參數;

 

 

octopus-2:oc_object octopus$ clang -framework Foundation OCPerson.m 
octopus-2:oc_object octopus$ ./a.out 
2015-09-30 18:59:11.602 a.out[3400:507] date retainCount : 1, _date retainCount : 0
2015-09-30 18:59:11.604 a.out[3400:507] date retainCount : 2, _date retainCount : 2
2015-09-30 18:59:11.605 a.out[3400:507] date retainCount : 1, _date retainCount : 1

 

 

 

(7) strong weak 特殊指示符


 

strong 指示符 : 指定該屬性對賦值對象持有強引用, 只要該強引用指向被賦值的對象, 那麼該對象就不會自動回收;

weak 指示符 : 指定該屬性對被賦值對象持有弱引用, 弱引用指向被賦值的對象, 該對象可能被回收;

 

 

(8) unsafe_unretained 特殊指示符


 

unsafe_unretained 指示符 : 與 weak 指示符基本相似, 對於被 unsafe_unretained 指向的對象也可能會被回收; 被 unsafe_unretained 修飾的指示的指針變量, 該指針不會被賦值為 nil, 可能導致程序崩潰;

 

 

 

5. 使用 . 語法訪問屬性

 

 

 

(1) 變量分類

 

點 . 使用 :

-- 使用前提 : 使用 @property @synthesize 合成 setter 和 getter 方法; 實際上 也允許使用 . 語法訪問屬性 和 對屬性賦值;

-- 本質 : 點語法是一種簡單寫法, 其本質仍然是 getter 和 setter 方法;

-- 獲取屬性值 : 只要對象有 getter 方法, 程序可以使用 點 語法獲取屬性值;

-- 設置屬性值 : 只要對象 setter 方法, 程序可以使用 點 語法獲取屬性值;

-- 源碼示例 :

 

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com 
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/

#import 

@interface OCPerson : NSObject
@property (nonatomic, copy) NSString * name;
@property (nonatomic, assign) int age;
@end
@implementation OCPerson
@synthesize name;
@synthesize age;
@end

int main(int argc, char * argv[])
{
	@autoreleasepool
	{
		OCPerson * p = [[OCPerson alloc] init];
		p.name = @Tom;
		p.age = 18;

		NSLog(@name : %@, age : %d, p.name, p.age);
	}
}


 

-- 執行結果 :

 

octopus-2:oc_object octopus$ clang -fobjc-arc -framework Foundation OCPerson.m 
octopus-2:oc_object octopus$ ./a.out 
2015-09-30 19:34:59.849 a.out[3451:507] name : Tom, age : 18


 

 

 

四. 鍵值編碼 (KVC)


 

 

 

 

1. 簡單地 KVC

 

 

 

 

(1) KVC 簡介


 

KVC 簡介 :

-- 引入 : Object-C 可以通過 getter setter 方法操作屬性, 還可以 以字符串形式間接操作屬性, 該方式是 Key Value Coding (KVC);

-- KVC 使用前提 : 最好在接口部分使用 @property 實現類部分使用 @synthesize 合成存取方法, 也可以只定義 _屬性名 或 屬性名 成員變量, 之後才能成功使用 KVC;

 

操作屬性方法 :

-- 未指定屬性設定值 : setValue : 屬性值 forKey : 屬性名 ;

-- 獲取指定屬性值 : valueForKey : 屬性名 ;

 

 

(2) setValue : forKey 執行機制

 

 

setValue : 屬性值 forKey 屬性名 執行機制 :

-- 調用 setter 方法 : 優先考慮調用 setName : 屬性值, 通過 setter 方法完成賦值;

-- 尋找 _屬性名 變量 : 如果沒有 setter 方法, 系統會搜索 _屬性名 成員變量, 只要有 _屬性名 成員變量, 無論實在 接口 還是在實現類定義, 無論使用什麼訪問控制符, 系統都會對該變量賦值;

-- 尋找 屬性名 變量 : 如果既沒有 setter 方法, 也沒有 _屬性名 成員變量, 系統會搜索 屬性名 成員變量, 不管定義在什麼位置(接口 實現), 也不管是用什麼訪問控制符修飾的, 系統都會對該變量賦值;

-- 引發異常 : 上面三種都沒有成功, 系統執行 該對象的 setValue : forUndefinedKey : 方法;

 

 

(3) valueForKey : 執行機制


 

valueForKey : 屬性名 執行機制 :

-- 調用 getter 方法 : 優先考慮調用 getter 方法, 即 屬性名() 方法獲取返回值;

-- 尋找 _屬性名 成員變量 : 如果沒有 getter 方法, 系統會搜索 _屬性名 成員變量, 不管該成員變量的定義位置 和 用什麼訪問控制符修飾, 都返回該 _屬性名 成員變量值;

-- 尋找 屬性名 成員變量 : 如果既沒有找到 getter 方法, 也沒有找到 _屬性名 成員變量, 系統會搜索 屬性名 成員變量, 不管該成員變量的定義位置 和 用什麼訪問控制符修飾, 都返回 屬性名 成員變量的值;

-- 引發異常 : 上面三種都沒有成功, 系統會執行對象的 valueForUndefinedKey :;

 

 

(4) KVC 簡單用法源碼示例


 

源碼示例 :

 

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com 
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/

#import 

@interface OCPerson : NSObject
{
	@private 
	int _age;
	@public 
	NSString * family;
}
@property (nonatomic, copy) NSString * name;
@end
@implementation OCPerson
@synthesize name;
@end

int main(int argc, char * argv[])
{
	@autoreleasepool
	{
		OCPerson * p = [[OCPerson alloc] init];
		[p setValue : @Tom forKey : @name];
		[p setValue : [NSNumber numberWithInt : 18] forKey : @age];
		[p setValue : @Hax forKey : @family];

		NSLog(@name : %@, age : %@, family : %@, 
			[p valueForKey : @name], 
			[p valueForKey : @age],
			[p valueForKey : @family]);
	}
}

-- 執行結果 :

 

 

octopus-2:oc_object octopus$ clang -fobjc-arc -framework Foundation OCPerson.m 
octopus-2:oc_object octopus$ ./a.out 
2015-09-30 21:59:44.909 a.out[3566:507] name : Tom, age : 18, family : Hax


 

 

2. Key 不存在的情況處理

 

 

 

(1) 情況簡介

 

前提 : KVC 操作時, 如果遇到 既沒有 getter setter 方法, 也沒有 _屬性值 或者 屬性值 成員變量時, KVC 會調用 setValue : forUndefinedKey : 和 valueForUndefinedKey : 方法, 系統只是定義了兩個默認方法, 但是並沒有執行實際有意義的內容;

 

 

(2) 異常代碼示例


 

示例代碼 :

 

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com 
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/

#import 

@interface OCPerson : NSObject
@property (nonatomic, copy) NSString * name;
@end
@implementation OCPerson
@synthesize name;
@end

int main(int argc, char * argv[])
{
	@autoreleasepool
	{
		OCPerson * p = [[OCPerson alloc] init];
		[p setValue : @Jack forKey : @son];
		[p valueForKey : @son];
	}
}

-- 執行結果 :

 

 

octopus-2:oc_object octopus$ clang -fobjc-arc -framework Foundation OCPerson.m 
octopus-2:oc_object octopus$ ./a.out 
2015-09-30 22:21:58.642 a.out[3611:507] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key son.'
*** First throw call stack:
(
	0   CoreFoundation                      0x00007fff8e37625c __exceptionPreprocess + 172
	1   libobjc.A.dylib                     0x00007fff8cc74e75 objc_exception_throw + 43
	2   CoreFoundation                      0x00007fff8e375e09 -[NSException raise] + 9
	3   Foundation                          0x00007fff8d83d72e -[NSObject(NSKeyValueCoding) setValue:forKey:] + 389
	4   a.out                               0x000000010e9fae15 main + 117
	5   libdyld.dylib                       0x00007fff8318c5fd start + 1
	6   ???                                 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
Abort trap: 6


 

 

 

(3) 處理 setValue : forKey 異常代碼示例


 

代碼示例 :

 

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com 
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/

#import 

@interface OCPerson : NSObject
@property (nonatomic, copy) NSString * name;
@end
@implementation OCPerson
@synthesize name;
-(void) setValue : (id) value forUndefinedKey : (id) key
{
	NSLog(@You want set value %@ to %@ key, value, key);
}
-(void) valueForUndefinedKey : (id) key
{
	NSLog(@You want get the value of %@ key, key);
}
@end

int main(int argc, char * argv[])
{
	@autoreleasepool
	{
		OCPerson * p = [[OCPerson alloc] init];
		[p setValue : @Jack forKey : @son];
		[p valueForKey : @son];
	}
}

-- 執行結果 :

 

 

octopus-2:oc_object octopus$ clang -fobjc-arc -framework Foundation OCPerson.m 
octopus-2:oc_object octopus$ ./a.out 
2015-09-30 22:27:17.699 a.out[3623:507] You want set value Jack to son key
2015-09-30 22:27:17.703 a.out[3623:507] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ valueForUndefinedKey:]: this class is not key value coding-compliant for the key son.'
*** First throw call stack:
(
	0   CoreFoundation                      0x00007fff8e37625c __exceptionPreprocess + 172
	1   libobjc.A.dylib                     0x00007fff8cc74e75 objc_exception_throw + 43
	2   CoreFoundation                      0x00007fff8e375e09 -[NSException raise] + 9
	3   Foundation                          0x00007fff8d90e14e -[NSObject(NSKeyValueCoding) valueForUndefinedKey:] + 235
	4   Foundation                          0x00007fff8d825466 -[NSObject(NSKeyValueCoding) valueForKey:] + 381
	5   a.out                               0x00000001049d6ddc main + 140
	6   libdyld.dylib                       0x00007fff8318c5fd start + 1
	7   ???                                 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
Abort trap: 6


 

 

(4) 處理 valueForKey 異常代碼示例


 

代碼示例 :

 

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com 
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/

#import 

@interface OCPerson : NSObject
@property (nonatomic, copy) NSString * name;
@end
@implementation OCPerson
@synthesize name;
-(void) setValue : (id) value forUndefinedKey : (id) key
{
	NSLog(@You want set value %@ to %@ key, value, key);
}
-(id) valueForUndefinedKey : (id) key
{
	NSLog(@You want get the value of %@ key, key);
	return nil;
}
@end

int main(int argc, char * argv[])
{
	@autoreleasepool
	{
		OCPerson * p = [[OCPerson alloc] init];
		[p valueForKey : @son];
	}
}

-- 執行結果 :

 

 

octopus-2:oc_object octopus$ clang -fobjc-arc -framework Foundation OCPerson.m
octopus-2:oc_object octopus$ ./a.out 
2015-09-30 22:33:43.557 a.out[3652:507] You want get the value of son key


 

 

 

2. 處理 nil 值

 

 

 

(1) 情況簡介


問題引入 : 使用 KVC 設置對象屬性, 如果屬性是指針類型, 設置 nil 值完全正確, 如果為 基本類型 int short 類型設置了 nil 會出現異常;

-- 異常信息 : 為基本類型設置 nil 會爆出 NSInvalidArgumentException 異常, int 類型不能接受 nil 值;

-- setNilValueForKey : 方法 : 為成員變量設置 nil 時, 如果該成員變量不接受 nil 值, 系統會自動執行 setNilValueForKey 方法;

-- 定制 setNilValueForKey : 方法 : 重寫了該方法之後, 如果再試圖給 不接受 nil 值的變量賦值 nil, 就會自動調用該方法;

 

 

(2) 異常示例代碼

 

 

示例代碼 :

 

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com 
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/

#import 

@interface OCPerson : NSObject
@property (nonatomic, copy) NSString * name;
@property (assign, nonatomic) int age;
@end
@implementation OCPerson
@synthesize name;
@synthesize age;
-(void) setValue : (id) value forUndefinedKey : (id) key
{
	NSLog(@You want set value %@ to %@ key, value, key);
}
-(id) valueForUndefinedKey : (id) key
{
	NSLog(@You want get the value of %@ key, key);
	return nil;
}
@end

int main(int argc, char * argv[])
{
	@autoreleasepool
	{
		OCPerson * p = [[OCPerson alloc] init];
		[p setValue : nil forKey : @age];
		NSLog(@age : %@, [p valueForKey : @age]);
	}
}

-- 執行結果 :

 

 

octopus-2:oc_object octopus$ clang -fobjc-arc -framework Foundation OCPerson.m
octopus-2:oc_object octopus$ ./a.out 
2015-09-30 23:11:56.652 a.out[3920:507] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '[ setNilValueForKey]: could not set nil as the value for the key age.'
*** First throw call stack:
(
	0   CoreFoundation                      0x00007fff8e37625c __exceptionPreprocess + 172
	1   libobjc.A.dylib                     0x00007fff8cc74e75 objc_exception_throw + 43
	2   CoreFoundation                      0x00007fff8e37610c +[NSException raise:format:] + 204
	3   Foundation                          0x00007fff8d90e2a7 -[NSObject(NSKeyValueCoding) setNilValueForKey:] + 73
	4   Foundation                          0x00007fff8d83d72e -[NSObject(NSKeyValueCoding) setValue:forKey:] + 389
	5   a.out                               0x000000010da0ad38 main + 120
	6   libdyld.dylib                       0x00007fff8318c5fd start + 1
	7   ???                                 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
Abort trap: 6


 

 

(3) 異常處理示例代碼


 

異常處理示例代碼 :

 

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com 
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/

#import 

@interface OCPerson : NSObject
@property (nonatomic, copy) NSString * name;
@property (assign, nonatomic) int age;
@end
@implementation OCPerson
@synthesize name;
@synthesize age;
-(void) setValue : (id) value forUndefinedKey : (id) key
{
	NSLog(@You want set value %@ to %@ key, value, key);
}
-(id) valueForUndefinedKey : (id) key
{
	NSLog(@You want get the value of %@ key, key);
	return nil;
}
-(void) setNilValueForKey : (id) key
{
	if([key isEqualToString : @age])
	{
		age = 0;
	}else
	{
		[super setNilValueForKey : key];
	}
}
@end

int main(int argc, char * argv[])
{
	@autoreleasepool
	{
		OCPerson * p = [[OCPerson alloc] init];
		[p setValue : nil forKey : @age];
		NSLog(@age : %@, [p valueForKey : @age]);
	}
}

-- 執行結果 :

 

 

octopus-2:oc_object octopus$ clang -fobjc-arc -framework Foundation OCPerson.m
octopus-2:oc_object octopus$ ./a.out 
2015-09-30 23:19:27.980 a.out[3934:507] age : 0


 

 

 

3. Key 路徑

 

 

 

(1) 復合屬性


復合屬性 : 在類 OCStudent 中 定義了 OCPerson 成員變量, 如果我們想要訪問 OCPerson 中得 name 成員變量, 就需要先訪問 OCPerson 成員變量, 再訪問 name 成員變量;

 

Key 路徑操作方法 :

-- setValue : forKeyPath : 方法 : 根據 key 路徑設置屬性值;

-- valueForKeyPath : 方法 : 根據 key 路徑獲取屬性值;

 

KVC 優缺點 :

-- 缺點 : KVC 操作對象性能要比使用 getter 和 setter 方法要差;

-- 優點 : KVC 使用會使程序更加簡潔, 適合提煉通用代碼, 具有極高的靈活性;

 

 

 

(2) Key 路徑示例代碼


 

 

示例代碼 :

-- OCPerson.h :

 

/*************************************************************************
    > File Name: OCPerson.h
    > Author: octopus
    > Mail: octopus_truth.163.com 
    > Created Time: 三  9/30 23:39:06 2015
 ************************************************************************/
#import 

@interface OCPerson : NSObject
@property (nonatomic, copy) NSString * name;
@property (assign, nonatomic) int age;
@end

-- OCPerson.m :

 

 

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com 
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/
#import OCPerson.h

@implementation OCPerson
@synthesize name;
@synthesize age;
-(void) setValue : (id) value forUndefinedKey : (id) key
{
	NSLog(@You want set value %@ to %@ key, value, key);
}
-(id) valueForUndefinedKey : (id) key
{
	NSLog(@You want get the value of %@ key, key);
	return nil;
}
-(void) setNilValueForKey : (id) key
{
	if([key isEqualToString : @age])
	{
		age = 0;
	}else
	{
		[super setNilValueForKey : key];
	}
}
@end

-- OCOrder.h :

 

 

/*************************************************************************
    > File Name: OCOrder.h
    > Author: octopus
    > Mail: octopus_truth.163.com 
    > Created Time: 三  9/30 23:37:51 2015
 ************************************************************************/
#import OCPerson.h
@interface OCOrder : NSObject
@property (nonatomic, strong) OCPerson * person;
@end

-- OCOrder.m :

 

 

/*************************************************************************
    > File Name: OCOrder.m
    > Author: octopus
    > Mail: octopus_truth.163.com 
    > Created Time: 三  9/30 23:43:08 2015
 ************************************************************************/
#import OCOrder.h
@implementation OCOrder
@synthesize person;
@end

int main(int argc, char * argv[])
{
	@autoreleasepool
	{
		OCOrder * order = [[OCOrder alloc] init];
		OCPerson * p = [[OCPerson alloc] init];

		[order setValue : p forKey : @person];

		[order setValue : @Tom forKeyPath : @person.name];
		NSLog(@name : %@, [order valueForKeyPath : @person.name]);
	}
}

-- 執行結果 :

 

 

octopus-2:oc_object octopus$ clang -fobjc-arc -framework Foundation OCPerson.m OCOrder.m 
octopus-2:oc_object octopus$ ./a.out 
2015-09-30 23:56:44.338 a.out[4031:507] name : Tom


 

 


 

五. 鍵值監聽 (KVO)


 

 

 

 

1. KVO 簡介

 

 

IOS 需求 :

-- 數據模型組件 : 負責維護應用程序的狀態數據;

-- 視圖組件 : 負責顯示數據模型組件內部的狀態數據;

-- 需求 : 數據模型組件數據發生變化時, 視圖組件能動態更新;

 

KVO (Key Value Observing) 鍵值監聽 :

-- 適用前提 : 只要是 NSObject 子類就可以使用 KVO;

-- 注冊監聽器用於監聽 key 路徑 : addObserver : forKeyPath : options : context;

-- 為 Key 路徑刪除指定監聽器 : removeObserver : forKeyPath : 或者 removeObserver : forKeyPath :;

-- 回調 : 數據發生變化時, 會回調 observerValueForKeyPath : ofObject : change : context 方法;

 

KVO 編程步驟 :

-- 注冊監聽器 : 為被監聽對象注冊監聽器, 使用 addObserver : forKeyPath : options : context;

-- 重寫監聽器方法 : 重寫 observeValueForKeyPath : ofObject : change : contex : 方法;

 

 

 

2. KVO 代碼示例


 

 

KVO 代碼示例 :

 

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com 
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/
#import 
@interface OCPerson : NSObject
@property (nonatomic, weak) NSString * name;
@property (nonatomic, assign) int age;
@end

@implementation OCPerson
@synthesize name;
@synthesize age;

- (void) setName : (NSString *) str
{
	self->name = str;
	[self addObserver : self forKeyPath : @name options : NSKeyValueObservingOptionNew context : nil];
}

- (void) setAge : (int) old
{
	self->age = old;
	[self addObserver : self forKeyPath : @age options : NSKeyValueObservingOptionNew context : nil];
}

- (void) observeValueForKeyPath : (NSString *) keyPath 
					   ofObject : (id) object
						 change : (NSDictionary *) change
						context : (void *) context
{
	NSLog(@keyPath : %@, object : %@, change : %@, keyPath, object, [change objectForKey : @new]);
}

- (void) dealloc
{
	NSLog(@dealloc callback);
	[self removeObserver : self forKeyPath : @name];
	[self removeObserver : self forKeyPath : @age];
}

@end

int main(int argc, char * argv[])
{
	@autoreleasepool
	{
		OCPerson * person = [[OCPerson alloc] init];
		person.age = 20;
		person.name = @Tom;

		person.age = 18;
		person.name = @Jerry;

		[person removeObserver : person forKeyPath : @name];
		[person removeObserver : person forKeyPath : @age];
	}
}

-- 執行結果 :

 

 

bogon:oc_object octopus$ clang -fobjc-arc -framework Foundation OCPerson.m
bogon:oc_object octopus$ ./a.out 
2015-10-01 20:36:39.722 a.out[734:507] keyPath : age, object : , change : 18
2015-10-01 20:36:39.724 a.out[734:507] keyPath : name, object : , change : Jerry
2015-10-01 20:36:39.724 a.out[734:507] dealloc callback


 

 

 

六. Object-C 對象初始化

 

 

 

 

 

 

1. 實例空間分配過程


 

創建對象語法 : [[類名 alloc] init] 語法, [類名 new] 語法, 每次創建對象都要調用該對象的 alloc 方法為對象分配內存空間;

-- alloc 方法 : alloc 方法 是在 NSObject 中定義的, 所有的 OC 對象都是 NSObject 的子類, 所有的類都可以調用 alloc 方法為所有的實例變量分配內存;

-- init 方法 : 來自 NSObject 中定義, 所有的對象調用 init 方法進行初始化, 將每個成員變量內存空間賦值為 0, 所有的整型變量所在空間都重置為 0, 浮點型變量 0.0, BOOL 型變量 NO, 指針型 nil;

-- 注意 : 使用 [類名 init] 創建的對象也可以執行, 但是沒有進行 init 初始化, 可能出現未知結果;

 

 

 

2. 初始化方法 與 對象初始化


 

 

 

(1) 重寫初始化方法


 

初始化方法種類 :

-- 默認初始化 : NSObject 提供的 默認的 init 方法為所有成員變量賦值 0 初始值;

-- 常用初始化 : 重寫 NSObject 的 init 方法, 可以加入任意的自定義初始化過程;

 

- (id) init
{
	if(self = [super init])
	{
		self.name = @Tom;
		self.age = 18;
		self.address = @DC;
	}
	return self;
}

 

 

-- 默認初始化調用 :

 

OCPerson * person1 = [[OCPerson alloc] init];

 

-- 多個初始化方法 : 還可以自定義 initXxx 方法, 可以根據參數執行自定義初始化;

 

- (id) initWithHobby : (NSString *) hobby_value
{
	
	if(self = [super init])
	{
		self.name = @Jerry;
		self.age = 20;
		self.address = @ST;
		self.hobby = hobby_value;;
	}
	return self;
}

 

 

-- 其它初始化方法調用 :

 

OCPerson * person2 = [[OCPerson alloc] initWithHobby : @football];



 

 

 

(2) 初始化方法示例代碼


 

 

示例代碼 :

 

/*************************************************************************
    > File Name: OCPerson.m
    > Author: octopus
    > Mail: octopus_truth.163.com 
    > Created Time: 三  9/30 09:10:09 2015
 ************************************************************************/
#import 
@interface OCPerson : NSObject
@property (nonatomic, copy) NSString * name;
@property (nonatomic, assign) int age;
@property (nonatomic, copy) NSString * address;
@property (nonatomic, copy) NSString * hobby;

- (id) initWithHobby : (NSString *) hobby_value;
@end

@implementation OCPerson
@synthesize name;
@synthesize age;
@synthesize address;
@synthesize hobby;

- (id) init
{
	if(self = [super init])
	{
		self.name = @Tom;
		self.age = 18;
		self.address = @DC;
	}
	return self;
}

- (id) initWithHobby : (NSString *) hobby_value
{
	
	if(self = [super init])
	{
		self.name = @Jerry;
		self.age = 20;
		self.address = @ST;
		self.hobby = hobby_value;;
	}
	return self;
}

@end

int main(int argc, char * argv[])
{
	@autoreleasepool
	{
		OCPerson * person1 = [[OCPerson alloc] init];
		NSLog(@name : %@, age : %d, address : %@, hobby : %@, person1.name, person1.age, person1.address, person1.hobby);

		OCPerson * person2 = [[OCPerson alloc] initWithHobby : @football];
		NSLog(@name : %@, age : %d, address : %@, hobby : %@, person2.name, person2.age, person2.address, person2.hobby);
	}
}


 

-- 執行結果 :

 

bogon:oc_object octopus$ clang -fobjc-arc -framework Foundation OCPerson.m
bogon:oc_object octopus$ ./a.out 
2015-10-01 21:47:06.409 a.out[832:507] name : Tom, age : 18, address : DC, hobby : (null)
2015-10-01 21:47:06.410 a.out[832:507] name : Jerry, age : 20, address : ST, hobby : football


 

 

 

 

七. 類的繼承


 

 

 

 

 

1. OC 類繼承簡介

 

 

繼承簡介 :
-- OC 繼承 : OC 繼承是單繼承, 一個子類只能有一個父類, 這點與 Java 相同;
-- 子類繼承父類格式 : 只需要在接口部分聲明類時, 在類名後面加上 : SuperClass 即可;

@interface SubClass : SuperClass
{
	//成員變量
}
//方法
@end
-- 子類收獲 : 子類擴展父類時, 子類可以得到父類的 全部方法 和 全部成員變量;


super 關鍵字 :
-- 作用 : 在子類方法調用父類被覆蓋的實例方法, 該關鍵字用於限定對象調用其從父類獲得的屬性 和 方法;
-- 注意 : super 關鍵字不能出現在 類方法中, 因為類方法執行是不依靠對象的;
-- self 對比 : self 也不能出現在類方法中;

 

 

 

 

2. OC 類繼承 源碼示例


 

 

源碼示例 :

-- OCAnimal.h : 父類的接口頭文件, 定義了兩個屬性;

 

/*************************************************************************
    > File Name: OCAnimal.h
    > Author: octopus
    > Mail: octopus_truth.163.com 
    > Created Time: 五 10/ 2 13:21:19 2015
 ************************************************************************/
#import 

@interface OCAnimal : NSObject
@property (nonatomic, copy) NSString * name;
@property (assign, nonatomic) int age;

-(void) info;
@end

-- OCAnimal.m : 父類的實現類, 聲明了接口中得兩個屬性, 實現了一個方法;

 

 

/*************************************************************************
    > File Name: OCAnimal.m
    > Author: octopus
    > Mail: octopus_truth.163.com 
    > Created Time: 五 10/ 2 13:23:37 2015
 ************************************************************************/
#import OCAnimal.h

@implementation OCAnimal
@synthesize name;
@synthesize age;

- (void) info
{
	NSLog(@name : %@, age : %d, name, age);
}

@end

-- OCCat.h : 子類的接口頭文件, 定義了新的成員變量 和 方法;

 

 

/*************************************************************************
    > File Name: OCCat.h
    > Author: octopus
    > Mail: octopus_truth.163.com 
    > Created Time: 五 10/ 2 13:28:22 2015
 ************************************************************************/
#import OCAnimal.h

@interface OCCat : OCAnimal
@property (nonatomic, copy) NSString * color;
- (void) purr;
@end

-- OCCat.m : 子類的實現, 此外還 重寫了 父類 info 方法;

 

 

/*************************************************************************
    > File Name: OCCat.m
    > Author: octopus
    > Mail: octopus_truth.163.com 
    > Created Time: 五 10/ 2 13:34:05 2015
 ************************************************************************/
#import OCCat.h

@implementation OCCat
@synthesize color;

- (void) info
{
	[super info];
	NSLog(@color : %@, color);
}

- (void) purr
{
	NSLog(@%@ 喵喵, super.name);
}

@end

-- OCCatTest.m : 子類測試類;

 

 

/*************************************************************************
    > File Name: OCCatTest.m
    > Author: octopus
    > Mail: octopus_truth.163.com 
    > Created Time: 五 10/ 2 13:37:16 2015
 ************************************************************************/
#import OCCat.h

int main(int argc, char * argv[])
{
	@autoreleasepool
	{
		OCCat * cat = [[OCCat alloc] init];
		cat.name = @小花;
		cat.age = 18;
		cat.color = @red;
		[cat info];
		[cat purr];
	}
}

 

 

-- 執行結果 :

 

octopus-2:oc_object octopus$ clang -fobjc-arc -framework Foundation OCAnimal.m OCCat.m OCCatTest.m 
octopus-2:oc_object octopus$ ./a.out 
2015-10-02 13:43:08.575 a.out[696:507] name : 小花, age : 18
2015-10-02 13:43:08.576 a.out[696:507] color : red
2015-10-02 13:43:08.577 a.out[696:507] 小花 喵喵



 

 

 

 

八. OC 多態

 

 

 

 

1. OC 類繼承簡介


 

 

(1) 編譯運行時類型

 

 

指針變量類型 : 如果編譯時與運行時類型不同, 就會產生多態;
-- 編譯時類型 : 由聲明該變量時使用的類型決定;
-- 運行時類型 : 由實際賦值給該變量的類型決定;

 

 

(2) 賦值多態



賦值多態 : 子類可以在任意位置替換父類 (裡氏替換);
-- 多態出現 : 子類賦值給父類時, 編譯時類型是父類, 運行時類型是子類;
-- 調用重寫方法 : 調用子類重寫父類的方法時, 執行的是父類方法;
-- 多態 : 相同類型的變量調用同一個方法, 會出現不同的特征, 這就是多態;

 

 

 

(3) 指針變量強制類型轉換



指針變量強制類型轉換 :
-- 問題出現 : 將子類賦值給父類類型對象時, 就不能再使用父類對象調用子類的方法和屬性, 此時需要將父類類型對象強制轉換為子類類型對象;
-- 類型轉換方法 : (類型名稱 *) 對象名,
-- 將父類轉為子類 : 這種強轉只是改變指針變量的編譯時類型, 變量指向的實際類型不會發生改變;
-- 判斷類型 : 轉換時需要進行類型判斷對象類型, 否則容易出錯;


(4) 判斷指針變量的實際類型



判斷指針變量實際類型 :
-- 判斷對象是否是 clazz 類對象 : - (BOOL) isMemberOfClass : clazz;
-- 判斷對象是否是 clazz 類 或 子類 實例 : - (BOOL) isKindOfClass : clazz :;

-- 判斷當前是否是 clazz 子類 : + (BOOL) isSubclassOfClass : clazz :

 

 

 

2. 多態代碼示例


 

代碼示例 :

-- OCCatTest.m : 該類與上面的 OCAnimal.h, OCAnimal.m, OCCat.h, OCCat.m 四個類同時使用;

 

/*************************************************************************
    > File Name: OCCatTest.m
    > Author: octopus
    > Mail: octopus_truth.163.com 
    > Created Time: 五 10/ 2 13:37:16 2015
 ************************************************************************/
#import OCCat.h

int main(int argc, char * argv[])
{
	@autoreleasepool
	{
		//多態示例
		OCAnimal * animal = [[OCAnimal alloc]init];
		animal.name = @Jerry;
		animal.age = 18;
		NSLog(@animal.name : %@, animal.age : %d, animal.name, animal.age);

		OCCat * cat = [[OCCat alloc]init];
		cat.name = @Tom;
		cat.age = 20;
		NSLog(@cat.name : %@, cat.age : %d, cat.name, cat.age);

		OCAnimal * father = [[OCCat alloc]init];
		father.name = @Kit;
		father.age = 25;;
		NSLog(@father.name : %@, father.age : %d, father.name, father.age);

		//[father purr]; //父類不能調用子類方法, 編譯類型與方法不符, 編譯不通過
		
		/*
		 * 類型轉換示例
		 * 如果使用 [father purr] 編譯時就會報錯
		 * 將 father 強轉為 OCCat * 類型, 編譯運行就沒問題了, 如下
		 */
		[(OCCat *)father purr];

		BOOL isAnimalObject = [animal isKindOfClass : [NSObject class]];
		NSLog(@isAnimalObject : %d, isAnimalObject);
	}
}

-- 執行結果 :

 

 

localhost:oc_object octopus$ clang -fobjc-arc -framework Foundation OCAnimal.m OCCat.m OCCatTest.m 
localhost:oc_object octopus$ ./a.out 
2015-10-03 07:51:07.415 a.out[712:507] animal.name : Jerry, animal.age : 18
2015-10-03 07:51:07.417 a.out[712:507] cat.name : Tom, cat.age : 20
2015-10-03 07:51:07.417 a.out[712:507] father.name : Kit, father.age : 25
2015-10-03 07:51:07.417 a.out[712:507] Kit 喵喵
2015-10-03 07:51:07.418 a.out[712:507] isAnimalObject : 1

 

 

 

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved