Objective-C 入門篇(推薦)

前言

Objective-C is the primary programming language you use when writing software for OS X and iOS. It’s a superset of the C programming language and provides object-oriented capabilities and a dynamic runtime. Objective-C inherits the syntax, primitive types, and flow control statements of C and adds syntax for defining classes and methods. It also adds language-level support for object graph management and object literals while providing dynamic typing and binding, deferring many responsibilities until runtime.

Objective-C(下面簡稱OC)是由C語言和Smalltalk擴展出來的,是C語言的超集,最大的區別是OC是面向對象的,其火星文寫法對於之前從事Java開發的同學頗感蛋疼,OC最大特點之一是使用“消息結構”而不是“函數調用”,所以在運行時執行的代碼由運行環境決定,而Java是由編譯器決定。個人感覺有關於IOS學習的文章相較於Android質量較低,有可能是蘋果系統封閉的原因,本文側重介紹常用的語法,通過對比Java並結合本人入門的過程和經驗,幫助有需求的同學快速掌握OC基本編程,為IOS的入門奠定語言基礎。下面首先是寫出第一行代碼,恭喜正式進入OC學習階段。

int main(int argc, char *argv[])
{
    @autoreleasepool //創建自動內存釋放池
    {
    	//打印輸出
        NSLog(@"hello world ios!");
        return 0;
    }
}

下面介紹OC代碼的文件擴展名:

文件擴展名 類型
.h 頭文件,作用是對類、屬性、變量、函數等的聲明
.m 實現文件,對頭文件的生命實現或者擴展
.mm 實現文件,一般是C++代碼

如果實現文件需要引入頭文件時,推薦使用#import,跟#include作用相同,優化瞭確保相同文件隻會被引入一次,所以傾向用#import。

基本數據類型

包括:int float double char

類型 字節數 格式化輸出
char 1 %c
int 4 %i,%x,%o
unsigned int 4 %i,%x,%o
short int 2 %hi,%hx,%ho
unsigned short int 2 %hi,%hx,%ho
long int 8 %li,%lx,%lo
unsigned long int 8 %lu,%lx,%lo
long long int 8 %lli,%llx,%llo
unsigned long long int 8 %llu,%llx,%llo
float 4 %f
double 8 %f
long double 16 %Lf

其他數據類型

id類型

可以存放任何數據類型的對象,類似Java中的Object類,其被定義為指向對象的指針(本身就是指針瞭),故定義比如id instance = nil;id類型是多態和動態綁定的基礎。

BOOL類型

佈爾值為YES/NO或1/0。Java對應是true/false

nil和Nil

nil相當於Java中的null,表示一個對象,這個對象的指針指向空。Nil是定義一個指向空的類而不是對象。

NSString(不可變字符串)

字符串是非常重要常用的,務必要掌握常用的基礎用法,包括創建、截取、遍歷、比較、大小寫轉換、搜索等,語義跟基本類似Java。

//字符串
NSString *str1 = @"ABC3456789";
//拼接成新的字符串
 NSString *str2 = [str1 stringByAppendingString:@"wwww"];
NSLog(@"str = %@", str2);
//遍歷
for (int i = 0; i < [str2 length]; i++) {
    char temp = [str2 characterAtIndex:i];
    NSLog(@"字符串第 %d 位輸出 %c", i, temp);
}
//比較
// sEqualToString方法 :比較字符串是否完全相等,大小寫不一樣也無法完全匹配。
//hasPrefixe方法:逐一匹配字符串頭部。haSuffix方法:匹配字符串的尾部
if ([str2 isEqualToString:str1]) {
    NSLog(@"相等");
}
if ([str2 hasPrefix:@"www"]) {
    NSLog(@"有該頭部");
}
if ([str2 hasSuffix:@"www"]) {
    NSLog(@"有該尾部");
}
if ([str2 compare:str options:NSCaseInsensitiveSearch | NSNumericSearch] == NSOrderedSame) {
}
NSLog(@"比較結果:%d", [str2 caseInsensitiveCompare:str1]);
//大小寫轉換
NSLog(@"str3轉大寫:%@",[str2 uppercaseString]);
NSLog(@"str3轉小寫:%@",[str2 lowercaseString]);
NSLog(@"str3首字母轉大寫:%@",[str2 capitalizedString]);
//字符串截取
NSRange rang = NSMakeRange(2, 2);
NSLog(@"str3截取:%@",[str2 substringWithRange:rang]);
//搜索
NSRange rang1 = [str2 rangeOfString:@"www"];
NSLog(@"location: %d,length: %d",rang1.location,rang1.length);
//替換
//全部替換
NSString *str3 = [str2 stringByReplacingOccurrencesOfString:@" " withString:@"@"];
NSLog(@"替換後字符串為%@", str3);
//局部替換
NSString *str4 = [str2 stringByReplacingCharactersInRange:rang withString:@"met"];
NSLog(@"替換後字符串為%@", str4);

NSMutableString(可變字符串)

創建對象的基本寫法是[[NSMutableString alloc]init],*號代表對象,[]代表方法調用,隻能通過類或者對象才能調用。[NSMutableString alloc]類似Java中new得到一個對象,然後再調用init初始化方法。

		//創建對象並初始化
		NSMutableString *mStr = [[NSMutableString alloc]init];
        //appendstring:向字符串尾部添加一個字符串。
        //appendFormat:向字符串尾部添加多個類型的字符串,可以添加任意數量與類型的字符串。
        [mStr appendString:@"hello world!"];
        NSLog(@"字符串創建%@", mStr);
        [mStr deleteCharactersInRange:[mStr rangeOfString:@"hello"]];
        //刪除
        NSLog(@"字符串刪除%@", mStr);
        //插入
        [mStr insertString:@"love you" atIndex: mStr.length];
        NSLog(@"字符串插入%@", mStr);

NSInteger、NSUInteger和NSNumber

NSInteger不是一個對象,而是基本數據類型中的typedef,NSUInteger是無符號的。 當需要使用int類型的變量時,推薦使用NSInteger,這樣不需要考慮設備是32位或者64位。NSNumber是一個類,用於包裝基本數據類型成為對象,可以理解為Java中的裝箱,為一些集合隻能存放對象使用,通過字面量方式非常方便將基本數據類型轉成對應的對象。例如:

//包裝
NSNumber *intNumber = [[NSNumber alloc]initWithInt:43];
//或者字面量方式
NSNumber *intNumber1 = @43;
//還原基本數據類型,解包
NSLog(@"%d",[intNumber intValue]);

集合

集合不能接受nil,nil是作為集合結束標識符。

1. NSArray(不可變)

類似Java中的ArrayList,可以存儲不同類型的對象,一般情況下數組元素的類型是相同的,特點是有序、可重復。下面展示一位數組的基本操作: 

//字面量創建方式
    NSArray *arr2 = @[@"aaa",@"bbbbb"];
//工廠方法創建
    NSArray *array = [[NSArray alloc] initWithObjects:@"1", @"2", nil];
    //取最後一個元素
    [array lastObject];
//    取第一個元素
    [array firstObject];
//    數組是否包含某個元素
    [array containsObject:@"1"];
//    數組的大小
    int count = (int) array.count;
//    第一種方式遍歷
    for (int i = 0; i < count; i++) {
        NSString *_str = [array objectAtIndex:i];
    }

那麼數據要求是多維的呢?多維數組可以理解為數組的數組,通過嵌套的方式,創建如下:

// 字面量創建二維數組並訪問
NSArray *arr2 = @[@[@11, @12, @13], @[@21, @22, @23], @[@31, @32, @33]];
// 字面量訪問方式(推薦)
NSLog(@"arr2[2][2]:%@", arr2[2][2]);
// 數組對象函數訪問
NSLog(@"arr2[2][2]:%@", [[arr2 objectAtIndex:2] objectAtIndex:2]);

2. NSMutableArray(可變的)

派生於NSArray,理解為動態數組,提供增加、刪除、插入、替換等語法糖。

		//創建,當然還有其他方式
        NSMutableArray *mutableArr = [NSMutableArray arrayWithObjects:@"one",@"two",@"three", nil];
        //添加
        [mutableArr addObject:@"hello"];
        //替換
        [mutableArr replaceObjectAtIndex:2 withObject:@"tihuan"];
        //刪除
        [mutableArr removeObjectAtIndex:1];
        //插入
        [mutableArr insertObject:@"ios" atIndex:1];

多維數組創建方式如下:

   		// 初始化作為列的數組,看做4列
NSMutableArray *columnArray = [[NSMutableArray alloc]initWithCapacity:4];

// 初始化2個一維數組,每個一維數組有4個元素,看做1行4列,2行加起來就是2行4列
NSMutableArray *rowArray1 = [[NSMutableArray alloc]initWithCapacity:4];
NSMutableArray *rowArray2 = [[NSMutableArray alloc]initWithCapacity:4];

// 每個行依次增加數組元素
// 第一行
[rowArray1 addObject:@"11"];
[rowArray1 addObject:@"12"];
[rowArray1 addObject:@"13"];
[rowArray1 addObject:@"14"];

// 第二行
[rowArray2 addObject:@"21"];
[rowArray2 addObject:@"22"];
[rowArray2 addObject:@"23"];
[rowArray2 addObject:@"24"];

// 分別打印數組
NSLog(@"myRowArray1: %@", rowArray1);
NSLog(@"myRowArray2: %@", rowArray2);
NSLog(@"myColumnArray: %@", columnArray);

字典

類似於Java中的HashMap,是一種映射型數據結果,存儲鍵值對,有可變和不可變兩種類型。

NSDictionary

主要特點是不可變,如果集合初始化完成,將內容無法修改,無序。

	//標準創建
    NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:@"cat",@"name1",@"dog",@"name2", nil];
    //字面量創建
    NSDictionary *dict1 = @{@"name1":@"cat",@"name2":@"dog"};
	//第一種遍歷
    for (NSString *key in [dict1 allKeys]) {
        NSLog(@"key: %@,value: %@", key, dict1[key]);
    }
    //第二種遍歷方式,通過遍歷器
    NSEnumerator *rator = [dict keyEnumerator];
    NSString *temp;
    while (temp = [rator nextObject]) {
        NSLog(@"%@", temp);
    }
    //獲取元素
	dict1[@"name"];
	[dict1 objectForKey:@"name"];
	//集合元素的個數
	NSInteger count = dict1.count;
	//沙盒文件存儲和讀取Plist
	[dict5 writeToFile:@"路徑" atomically:YES];
	NSDictionary *dict7 = [NSDictionary dictionaryWithContentsOfFile:@"路徑"];

NSMutableDictionary

NSMutableDictionary是NSDictionary的子類。NSMutableDictionary是可變的,動態添加、更改、刪除元素,因此不能使用字面量方式(@{})來創建一個可變字典。如果是不可變字典,出現瞭同名的key,那麼後面的key對應的值不會被保存,反之是可變字典,出現瞭同名的key,那麼後面的值會覆蓋前面的值。

//創建
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
//添加
[dict setObject:@"dog" forKey:@"name"];
[dict setValue:@"18" forKey:@"age"];
//會將傳入字典中所有的鍵值對取出來添加到dict中
[dict setValuesForKeysWithDictionary:@{@"name1":@"dog"}];
//取元素
[dict objectForKey:@"name"];
dict[@"name"];
//刪除
[dict removeAllObjects];
[dict removeObjectForKey:@"name"];
[dict removeObjectsForKeys:@[@"name", @"age"]];
//更新,如果利用setObject方法給已經存在的key賦值,新值會覆蓋舊值
[dict setObject:@"20" forKey:@"age"];
dict[@"age"] = @"30";

NSSet && NSMutableSet

具有很好的存取和查找功能,與NSArray相比NSSet的元素沒有索引,特點是無序,不可重復,類似Java中的HashSet,其中NSMutableSet提供計算交並集的方法。

NSSet存儲元素的過程:

註意:推薦使用字面量方式創建對象,可以縮短代碼長度,增加可讀性。但是在創建數組的時候要註意,如果含有nil就會拋異常,因為字面量實際上”語法糖“,效果等同於先創建一個數組,然後再把所有的對象添加進來,保證數組不添加nil。

消息傳遞

前言提到Objective-C最大特點之一是繼承瞭Smalltalk消息傳遞模型,因此在OC中的方法調用準備的說法是消息傳遞,類別與消息關系松散,調用方法是給對象發送消息,而方法是對消息的回應,所有消息的處理直到運行時(即runtime)才會動態確定,並交由類自行決定如何處理收到的消息。總結是一個類不保證一定會回應收到的消息,當收到的一個無法處理的消息,會拋出異常。

Java或者C++方法調用:

obj.method(argument);

OC方法調用:

[obj method: argument];

我們都知道在Java或者C++中,如果類沒有定義method方法,那麼編譯肯定不會通過,但是在OC中,理解是發送method的消息給obj,obj收到消息後再決定如何回應消息,如果類內定義瞭method方法則運行,反之不存在運行期拋出異常。

所有面向對象的編程都有類的概念,用於封裝數據,這樣的語言特性都有封裝、繼承和多態。OC對象是類在運行期的實例,包含瞭類聲明的實例變量、內存拷貝、類成員的指針等。由於OC是C語言的超集,類由兩個部分組成,分別是定義(interface)和實現(implementation),下面舉個?。新建一個People類,@interface是接口聲明的開始,@end終止結束,所有的OC編譯指令都是以”@“開始的。類的實現是通過@implementation指令開頭以@end結束。對應People.h和People.m兩份文件,下圖是類聲明(People.h)的展示,主要包括繼承關系、成員變量、屬性、方法聲明等,方法的具體實現是在People.m。

下圖是方法聲明的展示:

當然不止Interface區塊可以定義變量,Implementation區塊也可以定義,兩者區別是訪問權限不一。

前者默認權限為protected,而implementation區塊的實體變量則默認為private,所以類別私有可以放在implementation區塊。

訪問修飾符

  • @public:任何位置可以訪問。
  • @protected:默認情況下成員變量的修飾符。
  • @private:變量隻限於聲明它的類訪問,不允許被繼承。
  • @package:限定在當前包內,類似於Java包的概念。

屬性

成員變量是給類內使用的,屬性是作為類外訪問成員變量的接口,用於封裝對象的數據,通過@property聲明,編譯器自動生成setter和getter方法,此過程稱為”自動合成“。類實現文件中@synthesize語法可以指定實例變量的名字,一般不推薦這樣做。@dynamic語法是告訴編譯器不要自動合成,在OC中訪問修飾符很少用到,主要是靠聲明屬性取值。

屬性有五個常用的特質修飾:

assign:針對基本數據類型賦值操作。
strong:定義一種”擁有關系“,屬性設置新值時,先保留新值,並釋放舊值,然後再將新值設置。
weak:跟strong相反,屬性所指的對象銷毀時,屬性值也會清空。
copy:設置方法不保留新值,而是拷貝一份。
nonatomic:非原子,非線程安全類型。

Q&A:為什麼NSString 、 NSArray、 NSDictionary的屬性要用copy,集合的深淺拷貝是怎樣的?

copy屬性作用是為變量賦值的時候系統自動copy一份內存出來,修改新變量不會影響舊變量。在Apple規范中,NSString,NSArray,NSDictonary,推薦使用copy屬性,而其NSMubtableString,NSMutableArray, NSMutableDictonary屬性則使用strong屬性。

NSString *sourceString = [NSString stringWithFormat:@"hello ios"];
//不產生新的內存空間
NSString *copyStr = [sourceString copy];
//產生新的內存空間
NSMutableString *mutableStr = [sourceString mutableCopy];
NSLog(@"sourceString : %@   %p",sourceString,sourceString);
NSLog(@"copyStr : %@    %p",copyStr,copyStr);
NSLog(@"mutableStr : %@     %p",mutableStr,mutableStr);

使用strong這個屬性就有可能指向一個可變對象,如果這個可變對象在外部被修改瞭,那麼會影響該屬性。例如:

//代碼塊   
NSMutableString *string = [NSMutableString stringWithString:@"origin"];//copy
NSString *stringCopy = [string copy];
NSLog(@"string address is: %p",string);
NSLog(@"stringCopy address is: %p",stringCopy);

結果:內存地址不同

NSMutableString *string = [NSMutableString stringWithString:@"origin"];
//NSString *stringCopy = [string copy];
NSString *stringCopy = string;
[string appendString:@"change"];
NSLog(@"string address is: %p",string);
NSLog(@"stringCopy address is: %p",stringCopy);

結果:內存地址相同

結論:

可變對象指向不可變對象會導致不可變對象的值被篡改,所以需要copy屬性。用@property聲明NSString、NSArray、NSDictionary 經常使用copy關鍵字,是因為他們有對應的可變類型NSMutableString、NSMutableArray、NSMutableDictionary,彼此之間可能進行賦值操作,為瞭不可變對象中的內容不會被無意間變動,應該在設置新屬性值時拷貝一份。

淺拷貝:

在Java中淺拷貝如果是基本數據,則拷貝的是基本數據的值;如果是對象,則拷貝的是內存地址,修改該對象會影響另外一個對象。在OC中是對指針的拷貝,拷貝後的指針和原本對象的指針指向同一塊內存地址,故同樣會相互影響。

深拷貝:

OC中不僅拷貝指針,而且拷貝指針指向的內容,指針指向不同的內存地址,故修改不會相互影響原本對象。

非集合類對象中:對immutable對象進行copy操作,是指針復制(淺拷貝),mutableCopy操作時內容復制;對mutable對象進行copy和mutableCopy都是內容復制(深拷貝)。

方法

通過”+“、”-“分別聲明類方法和實例方法,方法如果帶有多個參數,參數在方法名之後接冒號定義,多個參數由空格隔開,如果參數個數可變,使用逗號接省略號。例如:

//無參數
- (void)print;
//有參數
- (void)print:(int)a andB:(int)b;

構造方法

第一種是重寫init方法,第二種是自定義。

/**
 重寫初始化方法
 **/
- (instancetype)init
{
    self = [super init];
    if (self) {
        _peopleName = @"hello ios";
    }
    return self;
}

/**
 自定義初始化方法
 **/
- (instancetype)initWithNameAndAge:(NSString *)name andAge:(int)age
{
    self = [super init];
    if (self) {
        _peopleName = name;
        _peopleAge = age;
    }
    return self;
}

創建類對象

所有對象和類的引用都是通過指針實現,嚴格地說指針就是一個地址,是一個常量,而指針變量可以被賦值不同的指針值,創建的對象就是一個指針變量,通過[People alloc]創建一個People對象,分配瞭內存,init是初始化對象。構造方法有兩種方式,第一種是重寫init方法,第二種是自定義。

People *p1 = [[People alloc] init];
//調用自定義的構造方法
People *p3 = [[People alloc] initWithNameAndAge:@"mingzi" andAge:12];
//調用方法
[p3 print];

在OC 2.0中,如果創建的對象不需要參數,可以直接使用new:

People *p1 = [People new];

self

作為OC的一個關鍵字,代表當前類的對象,類似Java中的this,最大的作用是讓類中的一個方法調用該類另外一個方法或者成員變量,可以理解”當前類誰調用瞭這個方法,self就代表誰“。

繼承

同Java一樣隻能單繼承,隻允許最多有一個直接父類。例如:定義一個父類Computer和子類MacBook。註意方法重寫類似Java,子類要重寫父類方法不需要重新聲明重寫方法,在實現部分直接重寫目標方法即可。如果需要子類調用父類的方法,可以通過super關鍵字調用。

//Computer.h文件
#import <Foundation/Foundation.h>
@interface Computer : NSObject
@property(nonatomic,strong)NSString *name;
-(void)calculate;
@end

//  Computer.m
#import "Computer.h"
@implementation Computer
@synthesize name;
-(void) calculate{
    NSLog(@"i can calculate");
}
@end

//  MacBook.h
#import "Computer.h"
@interface MacBook : Computer
@end

//  MacBook.m
#import "MacBook.h"
@implementation MacBook
@end

//main.m
int main(int argc, char *argv[])
{
    @autoreleasepool
    {
        MacBook *macBook = [[MacBook alloc] init];
        macBook.name = @"mac";
        [macBook calculate];
    }
}

多態

封裝、繼承和多態是面向對象編程語言的三大特性,OC的多態是不同對象對同一消息的不同響應方式,實際過程主要分為三種:

  • 繼承
  • 重寫
  • 指向子類的指針指向父類

可以看出跟Java的多態類似,理解起來應該比較容易,註意是沒有方法重載的,在OC中不允許。

Runtime

實例:用Runtime新增一個類Person, person有name屬性,有sayHi方法

#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import <objc/runtime.h>
#import <objc/message.h>
void sayHi(id self, IMP _cmd, id some)
{
    //self指的是調用該方法傳過來的類
    NSLog(@"%@說:%@,我%@歲", [self valueForKey:@"name"], some, object_getIvar(self, class_getInstanceVariable([self class], "_age")));
}

int main(int argc, char *argv[])
{
    @autoreleasepool
    {
        //該方法動態創建一個類,arg1:繼承自哪個類 arg2:新建類的名稱 arg3:extraBytes
        Class Person = objc_allocateClassPair([NSObject class], "Person", 0);
        //添加兩個實例變量name和age,arg2:變量名稱,arg3:內存地址大小,arg5:變量類型
        class_addIvar(Person, "_name", sizeof(NSString *), log2(sizeof(NSString *)), @encode(NSString *));
        class_addIvar(Person, "_age", sizeof(int), sizeof(int), @encode(int));
        //註冊方法名
        SEL s = sel_registerName("say:");
        //arg3:IMP是“implementation”的縮寫,這個函數指針決定瞭最終執行哪段代碼
        //arg4:方法的參數及返回值
        class_addMethod(Person, s, (IMP)sayHi, "v@:@");
        //通過該類創建一個實體的對象
        id peopleInstance = [[Person alloc]init];
        //給對象的 name 實例變量賦值,下面是第二種賦值方式
        [peopleInstance setValue:@"XQM" forKey:@"name"];
        //Ivar nameIvar = class_getInstanceVariable(Person, "_name");
        //object_setIvar(peopleInstance, nameIvar, @"XQM");
        //獲取實例變量
        Ivar ageIvar = class_getInstanceVariable(Person, "_age");
        //為變量賦值
        object_setIvar(peopleInstance, ageIvar, @21);
        //調用sayHi方法,arg2:註冊指定的方法;arg3:帶上有一個字符串的參數
        ((void(*)(id, SEL, id))objc_msgSend)(peopleInstance, s, @"大傢好");
        //調用完成,將對象置為空
        peopleInstance = nil;
        //通過 objc 銷毀類,銷毀的是一個類不是對象
        objc_disposeClassPair(Person);
    }
}

主要流程是:

定義類的方法->objc_allocateClassPair創建類->class_addIvar給類添加成員變量->sel_registerName註冊方法名->class_addMethod給類添加定義的方法->註冊該類->創建類對象->class_getInstanceVariable獲取成員變量,並通過object_setIvar賦值->objc_msgSend調用方法->釋放對象,銷毀類

Category(類別)

Objective-C借用並擴展瞭Smalltalk實現中的”分類”概念,用以幫助達到分解代碼的目的。類別主要特點是不能增加屬性或者成員變量、增加類功能和分離類實現,舉個例子: 在UIImageView增加瞭圖片異步加載的功能

@interface UIImageView (ImageViewLoader) <AysncImageDownloaderDelegate>
- (void)setOnlineImage:(NSString *)url placeholderImage:(UIImage *)image withRow:(NSNumber *)row;
@end

@implementation UIImageView (ImageViewLoader)

- (void)setOnlineImage:(NSString *)url placeholderImage:(UIImage *)image withRow:(NSNumber *)row;
{
    self.image = image;
    AsyncImageDownLoader *downloader = [AsyncImageDownLoader sharedImageDownloader];
    [downloader startWithUrl:url delegate:self withRow:row];
}
@end

Extension(拓展)

拓展也經常用到,主要特點是增加ivar、用於接口分離等。例如:ViewController的實現文件增加@interface ViewController (),支持定義屬性等。

@interface ViewController ()
@property (nonatomic, copy) block b;
@end

@implementation ViewController
@end

異常處理

OC的異常處理極其類似Java中的,包括4個指示符,分別是@try、@catch、@throw、@finally。可能存在異常的代碼寫在@try塊,異常處理邏輯寫在@catch,@finally塊的代碼總是要執行的,@throw作用是拋出異常。

協議

類似Java中的接口(interface),類似多重繼承功能,支持協議繼承協議,通過定義一系列方法,然後由遵從協議的類實現這些方法,協議方法可以用@optional關鍵字標記為可選,@required關鍵字標記為必選,編譯器會出現檢查警告,一般來說還是可以編譯通過。下面看下語法:

@protocol ClickDelegate
- (void)click;
- (void)unclick;
@end

協議最常應用在委托,分為委托方和代理方,委托方負責定義協議、通過id類型持有協議和調用協議的方法,而代理方則遵從協議、設置協議代理對象以及實現協議方法即可。

block

類似Java中的Lambda表達式,比較復雜,筆者的理解還未達到一定解說程度,所以這裡先不做解釋,放到後續的文章中介紹。

內存管理

Java的內存管理是由垃圾回收器負責,OC中引入自動引用計數(ARC),內存管理交由編譯器決定。引用計數是每個對象都有一個計數器,如果對象繼續存活,計數器遞增其引用計數,用完之後遞減引用計數,如果計數變為0,表示對象可以被釋放瞭。NSObject協議聲明瞭Retain、release和autorelease方法用於操作計數器,分別是遞增、遞減、自動釋放操作,所有的對象都是收集器的工作對象。

ARC:自動引用計數,編譯器自動生成retain/release
MRC:手動管理引用計數,舊版本使用
autoreleasepool:延遲自動釋放

strong/weak/assgin最佳實踐

基本類型:assgin;
delegate->week;
集合和block用copy;
其他用strong;
block中的self用weak打破循環引用。

參考資料

https://www.jianshu.com/p/eb713b1f22dc
https://www.jianshu.com/p/6ebda3cd8052
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtForwarding.html

推薦閱讀: