【IOS學習基礎】OC類的相關

幾天前忽然在別人的類的.m文件中看到這麼一句代碼:@synthesize xxxx = _xxxx; 當時愣是沒理解啥意思,事後才緩過神來發現原來是把一些類的基礎知識忘記了,雖然不用過多去深究之前的一些舊東西,但可是既然遇到了,仍是複習一下。json

1、類與對象

  1.類:類是定義同一類全部屬性和方法的藍圖或原型。設計模式

  2.對象:用來描述客觀事物的一個實體,由具體的屬性和方法構成。多線程

  3.類與對象的關係:類是用來製做無數實體(對象)的工程圖紙。架構

  4.類的特徵:屬性app

  5.類的行爲:方法框架

2、封裝

  1.類就是封裝,封裝了屬性與方法。它是一種思想,其核心就是「暴露出必要的內容給外部用屬性方法私有,而對於內部細節,使用者不用去關心」。函數

  咱們聲明一個Teacher類:學習

#import <Foundation/Foundation.h>
@interface Teacher : NSObject {
  // @public 公共的 NSString
*_name; //成員變量(實例變量) 默認@protected修飾(受保護的)

  /* 順帶一提:@protected、@public、@private、@package
   * @public 成員變量能夠被在任何地方訪問。
   * @protected 成員變量能被聲明它的類和子類訪問(默認)
   * @private 成員變量只能在聲明它的類中訪問(默認如今用@Property關鍵字生成的成員變量是這個)
   * @package 一個@package成員變量在實現這個類的可執行文件鏡像中其實是@public的,可是在外面就是@private。
   */ }
@end

    然而,咱們此時在外面根本沒法訪問到這個受保護的成員變量。ui

  訪問該成員變量的方法:atom

  1> 打開上面 「@public」的註釋,在外面以"->「方式訪問

Teacher *tec = [[Teacher alloc] init]; tec->_name = @"姓名";

  2> 封裝set、get方法進行間接訪問,在.h文件中加上方法聲明,在.m文件中實現

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

  這樣,即可以在外面經過調用方法的方式來間接訪問該成員變量,其實下面講的@property關鍵字就是幫你解決了這個set、get方法的訪問

讓其在外面以"."的方式(本質上是調用set和get方法)間接訪問。

Teacher *tec = [[Teacher alloc] init]; [tec setName:@"testName"]; NSLog(@"%@",[tec getName]);

  總結一下成員變量和成員屬性:

    ①成員變量即實例變量

    ②成員屬性用於間接訪問類中的成員變量

    ③假如你想讓類的一個特性私有,只在本類訪問,就定義成成員變量;假如你想在類以外訪問該類的一個特性,你就將其定義成成員屬性。  

3、@property和@synthesize關鍵字

  1.在之前,咱們常常能夠看到這種類的聲明

#import <Foundation/Foundation.h>

@interface Teacher : NSObject { NSString *_name; //成員變量 } @property (nonatomic,copy)NSString *name; //成員屬性 @end

#import "Teacher.h"

@implementation Teacher @synthesize name = _name; @end

  這是在以前的編譯器特性中:

  @property幫咱們聲明了name屬性的set和get方法

  @synthesize name = _name則表示對@property聲明的屬性name實現set和get方法,後面的」 = _name「表示實現的時候訪問成員變量_name

  2.而如今咱們爲類聲明一個屬性則一般只須要一句話(我通常都用這種,方便):

@property (nonatomic,copy)NSString *name; //這裏指在.h文件中聲明

  這是在新的編譯器特性中:

  @property直接幫助咱們幹了三件事:

    ①當該name屬性的成員變量未被指定時,會生成一個默認爲_name的成員變量,可是該屬性爲@private(私有的),本身寫的則是@protected(受保護的)

    ②聲明name屬性的set、get方法  

    ③實現name屬性的set、get方法

  注:若是你想要用本身生成的成員變量,則用以前的那種格式,在.h文件中增長一個成員變量,在.m文件中加上@synasize xxx = _xxx。

  成員變量用」_「下劃線開頭命名的好處:

    ①區分紅員變量和屬性

    ②防止局部變量和成員變量命名衝突

4、關於繼承的幾句話

  1.繼承:創建類之間的關係,實現代碼的重用性,方便系統擴展。

  2.繼承是爲了不多個類似的類中相同的成員變量反覆定義而延伸的一個特性。

  3.繼承有單根性與傳遞性

  4.被繼承的類稱之爲父類或基類,繼承的類稱之爲子類或派生類

  5.super關鍵字:self用於訪問本類成員,而super則是用於在子類中訪問父類成員  

5、分類catagory

  分類:①在不改變原來類的基礎上,爲類增長一些方法,來對類進行一個擴展。

     ②在開發中,咱們通常對都是爲系統提供的類添加分類。還有就是將本身的類分模塊,將實現不一樣功能的方法寫在不一樣的分類中,一個類能夠有無限個分類。

  1.分類又稱爲非正式協議:NSObject類及其子類的一個類別(catagory)。

#import "Teacher.h"

@interface Teacher (Log) +(void)log; @end

#import "Teacher+Log.h"

@implementation Teacher (Log) +(void)log
{
  NSLog(@"分類");
}
@end

  如上面所寫的分類,咱們爲Teacher類添加了一個分類"Teacher+Log.h",分類中聲明並實現了類方法+(void)log

  2.類的延展:匿名分類。

  在Teacher.m文件中

#import "Teacher.h"

@interface Teacher () //這種寫法就是匿名分類 @property (nonatomic,copy)NSString *nickName; //匿名分類中能夠添加屬性,可是該屬性默認爲@private,只能在本類中使用 +(void)log; //聲明瞭一個類方法log @end

@implementation Teacher @synthesize name = _name; //實現該類方法 +(void)log { NSLog(@"匿名分類"); } @end

   接着咱們調用log方法,出現以下打印

   

  這裏證實:分類(非正式協議)會重寫本類及其類擴展(匿名分類)的方法。

  3.catagory中匿名分類容許添加屬性並會自動生成成員變量和set、get方法;可是在非正式協議中,容許你用@property聲明一個屬性,不會爲你提供set、get方法以及成員變量,若是你直接使用的話,形成崩潰。以下

  1> 我爲Teacher + MyProperty.h分類頭文件中增長了一個屬性nickName;

#import "Teacher.h"

@interface Teacher (MyProperty) @property (nonatomic,copy)NSString *nickName; @end

  2>使用(缺乏set、get方法致使崩潰)

Teacher *tec = [[Teacher alloc] init]; tec.nickName = @"nickNmae"; NSLog(@"%@",tec.nickName); // 打印
2016-01-24 16:58:13.656 分類[2002:159549] -[Teacher setNickName:]: unrecognized selector sent to instance 0x100213e10
2016-01-24 16:58:13.658 分類[2002:159549] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Teacher setNickName:]: unrecognized selector sent to instance 0x100213e10'
*** First throw call stack:……

  3>如何在非正式協議(分類)中添加屬性【oc針對這一現象,提供了一個解決方案:關聯對象(Associated Object)】,在分類的.m文件中寫上以下代碼

#import "Teacher+MyProperty.h"
#import <objc/runtime.h>

static void *strKey = &strKey; @implementation Teacher (MyProperty) -(void)setNickName:(NSString *)nickName { objc_setAssociatedObject(self, &strKey, nickName, OBJC_ASSOCIATION_COPY); } -(NSString *)nickName { return objc_getAssociatedObject(self, &strKey); } @end

6、協議protocol

  1.有非正式協議,天然也有正式協議,protocol即是,用於聲明一大堆方法,等待實現,只要某個類遵照了某個協議,那麼這個類就擁有了該協議的全部方法聲明。(注意:協議只存在.h聲明文件)其格式以下:

@protocol Teach <NSObject>

@required; -(void)teach:(NSString *)text; // 聲明瞭一個teach的方法,而且爲必須實現,默認是@optional; @end //新建了一個名爲Teach.h的協議頭文件

  2.類能夠遵照協議(Teacher遵照teach協議)

    

  3.實現方法

-(void)teach:(NSString *)text { NSLog(@"老師教書"); }

  4.一個類只能繼承自一個父類(繼承的單根性),可是一個類能夠遵照多份協議.

7、關聯對象(Associated Object)--擴展

  1.關聯對象相似於成員變量,可是它是在運行時被添加的。(用於解決分類中添加屬性的問題,上面已經介紹了緣由,這裏不作介紹)

  2.咱們能夠把關聯對象想象成一個OC對象,這個對象經過key鏈接到一個類的實例變量上。

  3.因爲使用的是C接口,因此key是一個void指針(const void *)。咱們還須要指定一個內存管理策略,以告訴Runtime如何管理這個對象的內存。內存管理策略選項值以下:

// 當宿主對象被釋放時,會根據指定的內存管理策略來處理關聯對象。當咱們須要在多個線程中處理訪問關聯對象的多線程代碼時,這就很是有用了
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) { OBJC_ASSOCIATION_ASSIGN = 0, OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, OBJC_ASSOCIATION_COPY_NONATOMIC = 3, OBJC_ASSOCIATION_RETAIN = 01401, OBJC_ASSOCIATION_COPY = 01403 }; /* 若是指定的策略是assign,則宿主釋放時,關聯對象不會被釋放; 而若是指定的是retain或者是copy,則宿主釋放時,關聯對象會被釋放。 咱們甚至能夠選擇是不是自動retain/copy。 */

  4.使用

  1> void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)方法:將一個對象鏈接到其它對象

    參數1:源對象,通常傳self.

    參數2:key,用來表示是哪一屬性的key,可能在分類中添加不止一個屬性。

    常見有三種寫法:

    ① static void *strKey = &strKey;

    ② static NSString *strKey = @"strKey";

    ③ static char strKey;

    參數3:關聯的對象

    參數4:關聯策略

    self對象將獲取一個新的關聯的對象anObject,且內存管理策略是自動retain關聯對象,當self對象釋放時,會自動release關聯對象。另外,若是咱們使用同一個key來關聯另一個對象時,也會自動釋放以前關聯的對象,這種狀況下,先前的關聯對象會被妥善地處理掉,而且新的對象會使用它的內存

  2> id anObject = objc_getAssociatedObject(self, &myKey)方法 ,獲取關聯的對象

  3> objc_removeAssociatedObjects(self)方法,移除全部關聯

  五、應用(UIAlertView + Block分類,以自身爲delegate監控選項按鈕點擊事件,以block做爲關聯對象)

#import <UIKit/UIKit.h> typedef void(^CompleteBlock) (NSInteger buttonIndex); @interface UIAlertView (Block) // 用Block的方式回調,這時候會默認用self做爲Delegate
- (void)showAlertViewWithCompleteBlock:(CompleteBlock) block; @end

#import "UIAlertView+Block.h"
#import <objc/runtime.h>

@implementation UIAlertView (Block) static char key; // 用Block的方式回調,這時候會默認用self做爲Delegate
- (void)showAlertViewWithCompleteBlock:(CompleteBlock)block { if (block) { //移除全部關聯
 objc_removeAssociatedObjects(self); /** 1 建立關聯(源對象,關鍵字,關聯的對象和一個關聯策略。) 2 關鍵字是一個void類型的指針。每個關聯的關鍵字必須是惟一的。一般都是會採用靜態變量來做爲關鍵字。 3 關聯策略代表了相關的對象是經過賦值,保留引用仍是複製的方式進行關聯的;關聯是原子的仍是非原子的。這裏的關聯策略和聲明屬性時的很相似。 */ objc_setAssociatedObject(self, &key, block, OBJC_ASSOCIATION_COPY); //設置delegate
        self.delegate = self; }
 [self show]; } - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { ///獲取關聯的對象,經過關鍵字。
    CompleteBlock block = objc_getAssociatedObject(self, &key); if (block) { ///block傳值
 block(buttonIndex); } } /** OC中的關聯就是在已有類的基礎上添加對象參數。來擴展原有的類,須要引入#import <objc/runtime.h>頭文件。關聯是基於一個key來區分不一樣的關聯。 經常使用函數: 
objc_setAssociatedObject 設置關聯 objc_getAssociatedObject 獲取關聯 objc_removeAssociatedObjects 移除關聯
*/ //固然也能夠沒必要這麼麻煩,使用繼承一樣能夠輕易作到。 @end

8、類的加載(認識兩個方法)

/** * 這兩個方法是在程序運行一開始就被調用的方法. * 咱們能夠利用他們在類被使用前,作一些預處理工做. * 好比我碰到的就是讓類自動將自身類名保存到一個NSDictionary中. */

//而initialize是在類或者其子類的第一個方法被調用前調用。
+(void)initialize; //load方法是隻要類所在文件被引用就會被調用.
+(void)load; /** * 因此若是類沒有被引用進項目,就不會有load調用; * 但即便類文件被引用進來,可是沒有使用,那麼initialize也不會被調用 */

9、反射

  1.反射:簡而言之就是經過類名返回類的對象(使用前導入#import <objc/runtime.h>頭文件,今天前面的關聯對象也用到這個頭文件,好像關於這個runtime要學的東西有點多,複習着一會兒扒出了好多要學的新東西,- _ -,先從基礎的慢慢來吧)

// 類名返回類對象
+(NSObject *)createBean:(NSString *)className { Class tempClass = NSClassFromString(className); NSObject *obj; if (tempClass) { obj = [[tempClass alloc]init]; } return obj; }

  2.類名獲得屬性名集合

+(NSArray *)propertyOfClass:(NSString *)className { NSMutableArray *arr = [NSMutableArray arrayWithCapacity:0]; //經過類名得到類的屬性
    const char *cClassName = [className UTF8String]; id theClass = objc_getClass(cClassName); unsigned int outCount, i; objc_property_t *properties = class_copyPropertyList(theClass, &outCount); for (i = 0; i < outCount; i++) { objc_property_t property = properties[i]; NSString *propertyNameString = [[NSString alloc] initWithCString:property_getName(property) encoding:NSUTF8StringEncoding]; [arr addObject:propertyNameString]; } return arr; }

 3.經過反射(結合KVC),經過類名、數據源、約定,咱們就能夠實現json字符串轉對象。

 4.具體以後三方庫學習MJExtension中NSObject + MJClass轉模型。

10、類簇

  1.類簇是Foundation框架中普遍使用的設計模式(抽象工廠模式)。類簇將一些私有的、具體的子類組合在一個公共的、抽象的超類下面,以這種方法來組織類能夠簡化一個面向對象框架的公開架構,而又不減小功能的豐富性。

  2.類簇的概念(選自百度百科。。。)

  類簇 是一羣隱藏在通用接口下的與實現相關的類,使得咱們編寫的代碼能夠獨立於底層實現(由於接口是穩定的)。

  如建立NSString對象時,你獲得的多是NSLiteralString,NSCFString,NSSimpleCString等。即不一樣的NSString對象調用同一個接口A,接口A的實現多是不一樣的。

  在Foundation框架中,常見的類簇有NSNumber,NSString,NSArray,NSDictionary等。 想要在類簇中建立子類會困難一些,必須是抽象超類的子類,必須重載超類的原始方法,必須聲明本身的數據存儲。最方便的是使用組合或者類別來代替子類化。
  3.關於重寫init方法時候調用父類的init方法:self = [super init]和[super init]

  咋看之下,產生了一個疑問,爲何[super init]要賦值給self。今天看了下類簇的概念,終於明白了,因爲在有些狀況下,init可能會改變返回的對象,這時候必須賦值給self。

  四、不推薦繼承類簇

  當你繼承某個類簇的超類以後,子類可使用父類的方法,結果你去調用該類簇的超類的方法,你會發現程序崩潰了,緣由是因爲在超類的方法的實現裏,你不知道它實際調用的它裏面的哪個子類的方法(這些組合的子類聲明和實現都隱藏在該超類的.m文件裏面),結果你的類是直接繼承自該類簇的超類,也就是說你的類根本就不認識這個方法。

相關文章
相關標籤/搜索