欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

全面解析Objective-C中的block代碼塊的使用

 更新時間:2015年11月04日 09:32:28   作者:suiling  
這篇文章主要介紹了Objective-C中的block代碼塊的使用,包括閉包等重要特性的講解,需要的朋友可以參考下

1.相關(guān)概念

在這篇筆記開始之前,我們需要對以下概念有所了解。

1.1 操作系統(tǒng)中的棧和堆

注:這里所說的堆和棧與數(shù)據(jù)結(jié)構(gòu)中的堆和棧不是一回事。

我們先來看看一個由C/C++/OBJC編譯的程序占用內(nèi)存分布的結(jié)構(gòu):

201511492644909.jpg (844×367)

棧區(qū)(stack):由系統(tǒng)自動分配,一般存放函數(shù)參數(shù)值、局部變量的值等。由編譯器自動創(chuàng)建與釋放。其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧,即后進(jìn)先出、先進(jìn)后出的原則。

例如:在函數(shù)中申明一個局部變量int b;系統(tǒng)自動在棧中為b開辟空間。

堆區(qū)(heap):一般由程序員申請并指明大小,最終也由程序員釋放。如果程序員不釋放,程序結(jié)束時可能會由OS回收。對于堆區(qū)的管理是采用鏈表式管理的,操作系統(tǒng)有一個記錄空閑內(nèi)存地址的鏈表,當(dāng)接收到程序分配內(nèi)存的申請時,操作系統(tǒng)就會遍歷該鏈表,遍歷到一個記錄的內(nèi)存地址大于申請內(nèi)存的鏈表節(jié)點,并將該節(jié)點從該鏈表中刪除,然后將該節(jié)點記錄的內(nèi)存地址分配給程序。

例如:在C中malloc函數(shù)

復(fù)制代碼 代碼如下:

char p1;
p1 = (char )malloc(10);

但是p1本身是在棧中的。

鏈表:是一種常見的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu),一般分為單向鏈表、雙向鏈表、循環(huán)鏈表。以下為單向鏈表的結(jié)構(gòu)圖:

201511492710858.jpg (428×73)

單向鏈表是鏈表中最簡單的一種,它包含兩個區(qū)域,一個信息域和一個指針域。信息域保存或顯示關(guān)于節(jié)點的信息,指針域儲存下一個節(jié)點的地址。

上述的空閑內(nèi)存地址鏈表的信息域保存的就是空閑內(nèi)存的地址。

全局區(qū)/靜態(tài)區(qū):顧名思義,全局變量和靜態(tài)變量存儲在這個區(qū)域。只不過初始化的全局變量和靜態(tài)變量存儲在一塊,未初始化的全局變量和靜態(tài)變量存儲在一塊。程序結(jié)束后由系統(tǒng)釋放。

文字常量區(qū):這個區(qū)域主要存儲字符串常量。程序結(jié)束后由系統(tǒng)釋放。

程序代碼區(qū):這個區(qū)域主要存放函數(shù)體的二進(jìn)制代碼。

下面舉一個前輩寫的例子:

復(fù)制代碼 代碼如下:

//main.cpp
int a = 0; // 全局初始化區(qū)
char *p1; // 全局未初始化區(qū)
main {
    int b; // 棧
    char s[] = "abc"; // 棧
    char *p2; // 棧
    char *p3 = "123456"; // 123456\0在常量區(qū),p3在棧上
    static int c =0; // 全局靜態(tài)初始化區(qū)
    p1 = (char *)malloc(10);
    p2 = (char *)malloc(20); // 分配得來的10和20字節(jié)的區(qū)域就在堆區(qū)
    strcpy(p1, "123456"); // 123456\0在常量區(qū),這個函數(shù)的作用是將"123456" 這串字符串復(fù)制一份放在p1申請的10個字節(jié)的堆區(qū)域中。
    // p3指向的"123456"與這里的"123456"可能會被編譯器優(yōu)化成一個地址。
}

strcpy函數(shù)

原型聲明:extern char *strcpy(char* dest, const char *src);

功能:把從src地址開始且含有NULL結(jié)束符的字符串復(fù)制到以dest開始的地址空間。

1.2 結(jié)構(gòu)體(Struct)

在C語言中,結(jié)構(gòu)體(struct)指的是一種數(shù)據(jù)結(jié)構(gòu)。結(jié)構(gòu)體可以被聲明為變量、指針或數(shù)組等,用以實現(xiàn)較復(fù)雜的數(shù)據(jù)結(jié)構(gòu)。結(jié)構(gòu)體同時也是一些元素的集合,這些元素稱為結(jié)構(gòu)體的成員(member),且這些成員可以為不同的類型,成員一般用名字訪問。

我們來看看結(jié)構(gòu)體的定義:

復(fù)制代碼 代碼如下:

struct tag { member-list } variable-list;

  • struct:結(jié)構(gòu)體關(guān)鍵字。
  • tag:結(jié)構(gòu)體標(biāo)簽。
  • member-list:結(jié)構(gòu)體成員列表。
  • variable-list:為結(jié)構(gòu)體聲明的變量列表。

在一般情況下,tag,member-list,variable-list這三部分至少要出現(xiàn)兩個。以下為示例:

復(fù)制代碼 代碼如下:

// 該結(jié)構(gòu)體擁有3個成員,整型的a,字符型的b,雙精度型的c
// 并且為該結(jié)構(gòu)體聲明了一個變量s1
// 該結(jié)構(gòu)體沒有標(biāo)明其標(biāo)簽
struct{
    int a;
    char b;
    double c;
} s1;
// 該結(jié)構(gòu)體擁有同樣的三個成員
// 并且該結(jié)構(gòu)體標(biāo)明了標(biāo)簽EXAMPLE
// 該結(jié)構(gòu)體沒有聲明變量
struct EXAMPLE{
    int a;
    char b;
    double c;
};
//用EXAMPLE標(biāo)簽的結(jié)構(gòu)體,另外聲明了變量t1、t2、t3
struct EXAMPLE t1, t2[20], *t3;

以上就是簡單結(jié)構(gòu)體的代碼示例。結(jié)構(gòu)體的成員可以包含其他結(jié)構(gòu)體,也可以包含指向自己結(jié)構(gòu)體類型的指針。結(jié)構(gòu)體的變量也可以是指針。

下面我們來看看結(jié)構(gòu)體成員的訪問。結(jié)構(gòu)體成員依據(jù)結(jié)構(gòu)體變量類型的不同,一般有2種訪問方式,一種為直接訪問,一種為間接訪問。直接訪問應(yīng)用于普通的結(jié)構(gòu)體變量,間接訪問應(yīng)用于指向結(jié)構(gòu)體變量的指針。直接訪問使用結(jié)構(gòu)體變量名.成員名,間接訪問使用(*結(jié)構(gòu)體指針名).成員名或者使用結(jié)構(gòu)體指針名->成員名。相同的成員名稱依靠不同的變量前綴區(qū)分。

復(fù)制代碼 代碼如下:

struct EXAMPLE{
    int a;
    char b;
};
//聲明結(jié)構(gòu)體變量s1和指向結(jié)構(gòu)體變量的指針s2
struct EXAMPLE s1, *s2;
//給變量s1和s2的成員賦值,注意s1.a和s2->a并不是同一成員
s1.a = 5;
s1.b = 6;
s2->a = 3;
s2->b = 4;

最后我們來看看結(jié)構(gòu)體成員存儲。在內(nèi)存中,編譯器按照成員列表順序分別為每個結(jié)構(gòu)體成員分配內(nèi)存。如果想確認(rèn)結(jié)構(gòu)體占多少存儲空間,則使用關(guān)鍵字sizeof,如果想得知結(jié)構(gòu)體的某個特定成員在結(jié)構(gòu)體的位置,則使用offsetof宏(定義于stddef.h)。
復(fù)制代碼 代碼如下:

struct EXAMPLE{
    int a;
    char b;
};
//獲得EXAMPLE類型結(jié)構(gòu)體所占內(nèi)存大小
int size_example = sizeof( struct EXAMPLE );
//獲得成員b相對于EXAMPLE儲存地址的偏移量
int offset_b = offsetof( struct EXAMPLE, b );

1.3 閉包(Closure)

閉包就是一個函數(shù),或者一個指向函數(shù)的指針,加上這個函數(shù)執(zhí)行的非局部變量。

說的通俗一點,就是閉包允許一個函數(shù)訪問聲明該函數(shù)運行上下文中的變量,甚至可以訪問不同運行上文中的變量。

我們用腳本語言來看一下:

復(fù)制代碼 代碼如下:

function funA(callback){
    alert(callback());
}
function funB(){
    var str = "Hello World"; // 函數(shù)funB的局部變量,函數(shù)funA的非局部變量
    funA(
        function(){
            return str;
        }
    );
}

通過上面的代碼我們可以看出,按常規(guī)思維來說,變量str是函數(shù)funB的局部變量,作用域只在函數(shù)funB中,函數(shù)funA是無法訪問到str的。但是上述代碼示例中函數(shù)funA中的callback可以訪問到str,這是為什么呢,因為閉包性。

2.blcok基礎(chǔ)知識

block實際上就是Objective-C語言對閉包的實現(xiàn)。

2.1 block的原型及定義

我們來看看block的原型:

復(fù)制代碼 代碼如下:

NSString * ( ^ myBlock )( int );

上面的代碼聲明了一個block(^)原型,名字叫做myBlock,包含一個int型的參數(shù),返回值為NSString類型的指針。

下面來看看block的定義:

復(fù)制代碼 代碼如下:

myBlock = ^( int paramA )
{
    return [ NSString stringWithFormat: @"Passed number: %i", paramA ];
};

上面的代碼中,將一個函數(shù)體賦值給了myBlock變量,其接收一個名為paramA的參數(shù),返回一個NSString對象。

注意:一定不要忘記block后面的分號。

定義好block后,就可以像使用標(biāo)準(zhǔn)函數(shù)一樣使用它了:

復(fù)制代碼 代碼如下:

myBlock(7);

由于block數(shù)據(jù)類型的語法會降低整個代碼的閱讀性,所以常使用typedef來定義block類型。例如,下面的代碼創(chuàng)建了GetPersonEducationInfo和GetPersonFamilyInfo兩個新類型,這樣我們就可以在下面的方法中使用更加有語義的數(shù)據(jù)類型。
復(fù)制代碼 代碼如下:

// Person.h
#import // Define a new type for the block
typedef NSString * (^GetPersonEducationInfo)(NSString *);
typedef NSString * (^GetPersonFamilyInfo)(NSString *);
@interface Person : NSObject
- (NSString *)getPersonInfoWithEducation:(GetPersonEducationInfo)educationInfo
    andFamily:(GetPersonFamilyInfo)familyInfo;
@end

我們用一張大師文章里的圖來總結(jié)一下block的結(jié)構(gòu):

201511492752307.png (1140×420)

2.2 將block作為參數(shù)傳遞

復(fù)制代碼 代碼如下:

// .h
-(void) testBlock:( NSString * ( ^ )( int ) )myBlock;
// .m
-(void) testBlock:( NSString * ( ^ )( int ) )myBlock
{
    NSLog(@"Block returned: %@", myBlock(7) );
}

由于Objective-C是強(qiáng)制類型語言,所以作為函數(shù)參數(shù)的block也必須要指定返回值的類型,以及相關(guān)參數(shù)類型。

2.3 閉包性

上文說過,block實際是Objc對閉包的實現(xiàn)。

我們來看看下面代碼:

復(fù)制代碼 代碼如下:

#import void logBlock( int ( ^ theBlock )( void ) )
{
    NSLog( @"Closure var X: %i", theBlock() );
}
int main( void )
{
    NSAutoreleasePool * pool;
    int ( ^ myBlock )( void );
    int x;
    pool = [ [ NSAutoreleasePool alloc ] init ];
    x = 42;
    myBlock = ^( void )
    {
        return x;
    };
    logBlock( myBlock );
    [ pool release ];
    return EXIT_SUCCESS;
}

上面的代碼在main函數(shù)中聲明了一個整型,并賦值42,另外還聲明了一個block,該block會將42返回。然后將block傳遞給logBlock函數(shù),該函數(shù)會顯示出返回的值42。即使是在函數(shù)logBlock中執(zhí)行block,而block又聲明在main函數(shù)中,但是block仍然可以訪問到x變量,并將這個值返回。

注意:block同樣可以訪問全局變量,即使是static。

2.4 block中變量的復(fù)制與修改

對于block外的變量引用,block默認(rèn)是將其復(fù)制到其數(shù)據(jù)結(jié)構(gòu)中來實現(xiàn)訪問的,如下圖:

201511492912717.jpg (319×162)

通過block進(jìn)行閉包的變量是const的。也就是說不能在block中直接修改這些變量。來看看當(dāng)block試著增加x的值時,會發(fā)生什么:

復(fù)制代碼 代碼如下:

myBlock = ^( void )
{
    x++;
    return x;
};

編譯器會報錯,表明在block中變量x是只讀的。

有時候確實需要在block中處理變量,怎么辦?別著急,我們可以用__block關(guān)鍵字來聲明變量,這樣就可以在block中修改變量了。

基于之前的代碼,給x變量添加__block關(guān)鍵字,如下:

復(fù)制代碼 代碼如下:

__block int x;

對于用__block修飾的外部變量引用,block是復(fù)制其引用地址來實現(xiàn)訪問的,如下圖:

201511492939578.jpg (252×143)

3.編譯器中的block

3.1 block的數(shù)據(jù)結(jié)構(gòu)定義

我們通過大師文章中的一張圖來說明:

201511493000042.jpg (510×511)

上圖這個結(jié)構(gòu)是在棧中的結(jié)構(gòu),我們來看看對應(yīng)的結(jié)構(gòu)體定義:

復(fù)制代碼 代碼如下:

struct Block_descriptor {
    unsigned long int reserved;
    unsigned long int size;
    void (*copy)(void *dst, void *src);
    void (*dispose)(void *);
};
struct Block_layout {
    void *isa;
    int flags;
    int reserved;
    void (*invoke)(void *, ...);
    struct Block_descriptor *descriptor;
    /* Imported variables. */
};

從上面代碼看出,Block_layout就是對block結(jié)構(gòu)體的定義:

isa指針:指向表明該block類型的類。

flags:按bit位表示一些block的附加信息,比如判斷block類型、判斷block引用計數(shù)、判斷block是否需要執(zhí)行輔助函數(shù)等。

reserved:保留變量,我的理解是表示block內(nèi)部的變量數(shù)。

invoke:函數(shù)指針,指向具體的block實現(xiàn)的函數(shù)調(diào)用地址。

descriptor:block的附加描述信息,比如保留變量數(shù)、block的大小、進(jìn)行copy或dispose的輔助函數(shù)指針。

variables:因為block有閉包性,所以可以訪問block外部的局部變量。這些variables就是復(fù)制到結(jié)構(gòu)體中的外部局部變量或變量的地址。

3.2 block的類型

block有幾種不同的類型,每種類型都有對應(yīng)的類,上述中isa指針就是指向這個類。這里列出常見的三種類型:

_NSConcreteGlobalBlock:全局的靜態(tài)block,不會訪問任何外部變量,不會涉及到任何拷貝,比如一個空的block。例如:

復(fù)制代碼 代碼如下:

#include int main()
{
    ^{ printf("Hello, World!\n"); } ();
    return 0;
}
_NSConcreteStackBlock:保存在棧中的block,當(dāng)函數(shù)返回時被銷毀。例如:

#include int main()
{
    char a = 'A';
    ^{ printf("%c\n",a); } ();
    return 0;
}


_NSConcreteMallocBlock:保存在堆中的block,當(dāng)引用計數(shù)為0時被銷毀。該類型的block都是由_NSConcreteStackBlock類型的block從棧中復(fù)制到堆中形成的。例如下面代碼中,在exampleB_addBlockToArray方法中的block還是_NSConcreteStackBlock類型的,在exampleB方法中就被復(fù)制到了堆中,成為_NSConcreteMallocBlock類型的block:
復(fù)制代碼 代碼如下:

void exampleB_addBlockToArray(NSMutableArray *array) {
    char b = 'B';
    [array addObject:^{
            printf("%c\n", b);
    }];
}
void exampleB() {
    NSMutableArray *array = [NSMutableArray array];
    exampleB_addBlockToArray(array);
    void (^block)() = [array objectAtIndex:0];
    block();
}

總結(jié)一下:

  1. _NSConcreteGlobalBlock類型的block要么是空block,要么是不訪問任何外部變量的block。它既不在棧中,也不在堆中,我理解為它可能在內(nèi)存的全局區(qū)。
  2. _NSConcreteStackBlock類型的block有閉包行為,也就是有訪問外部變量,并且該block只且只有有一次執(zhí)行,因為棧中的空間是可重復(fù)使用的,所以當(dāng)棧中的block執(zhí)行一次之后就被清除出棧了,所以無法多次使用。
  3. _NSConcreteMallocBlock類型的block有閉包行為,并且該block需要被多次執(zhí)行。當(dāng)需要多次執(zhí)行時,就會把該block從棧中復(fù)制到堆中,供以多次執(zhí)行。

3.3 編譯器如何編譯

我們通過一個簡單的示例來說明:

復(fù)制代碼 代碼如下:

#import typedef void(^BlockA)(void);
__attribute__((noinline))
void runBlockA(BlockA block) {
    block();
}
void doBlockA() {
    BlockA block = ^{
        // Empty block
    };
    runBlockA(block);
}

上面的代碼定義了一個名為BlockA的block類型,該block在函數(shù)doBlockA中實現(xiàn),并將其作為函數(shù)runBlockA的參數(shù),最后在函數(shù)doBlockA中調(diào)用函數(shù)runBloackA。

注意:如果block的創(chuàng)建和調(diào)用都在一個函數(shù)里面,那么優(yōu)化器(optimiser)可能會對代碼做優(yōu)化處理,從而導(dǎo)致我們看不到編譯器中的一些操作,所以用__attribute__((noinline))給函數(shù)runBlockA添加noinline,這樣優(yōu)化器就不會在doBlockA函數(shù)中對runBlockA的調(diào)用做內(nèi)聯(lián)優(yōu)化處理。

我們來看看編譯器做的工作內(nèi)容:

復(fù)制代碼 代碼如下:

#import __attribute__((noinline))
void runBlockA(struct Block_layout *block) {
    block->invoke();
}
void block_invoke(struct Block_layout *block) {
    // Empty block function
}
void doBlockA() {
    struct Block_descriptor descriptor;
    descriptor->reserved = 0;
    descriptor->size = 20;
    descriptor->copy = NULL;
    descriptor->dispose = NULL;
    struct Block_layout block;
    block->isa = _NSConcreteGlobalBlock;
    block->flags = 1342177280;
    block->reserved = 0;
    block->invoke = block_invoke;
    block->descriptor = descriptor;
    runBlockA(&block);
}

上面的代碼結(jié)合block的數(shù)據(jù)結(jié)構(gòu)定義,我們能很容易得理解編譯器內(nèi)部對block的工作內(nèi)容。

3.4 copy()和dispose()

上文中提到,如果我們想要在以后繼續(xù)使用某個block,就必須要對該block進(jìn)行拷貝操作,即從??臻g復(fù)制到堆空間。所以拷貝操作就需要調(diào)用Block_copy()函數(shù),block的descriptor中有一個copy()輔助函數(shù),該函數(shù)在Block_copy()中執(zhí)行,用于當(dāng)block需要拷貝對象的時候,拷貝輔助函數(shù)會retain住已經(jīng)拷貝的對象。

既然有有copy那么就應(yīng)該有release,與Block_copy()對應(yīng)的函數(shù)是Block_release(),它的作用不言而喻,就是釋放我們不需要再使用的block,block的descriptor中有一個dispose()輔助函數(shù),該函數(shù)在Block_release()中執(zhí)行,負(fù)責(zé)做和copy()輔助函數(shù)相反的操作,例如釋放掉所有在block中拷貝的變量等。

4.下面來看幾個具體的運行示例:
4.1參數(shù)是NSString*的代碼塊

復(fù)制代碼 代碼如下:

        void (^printBlock)(NSString *x);
        printBlock = ^(NSString* str)
        {
            NSLog(@"print:%@", str);
        };
        printBlock(@"hello world!");

運行結(jié)果是:print:hello world!
4.2代碼用在字符串?dāng)?shù)組排序
復(fù)制代碼 代碼如下:

        NSArray *stringArray = [NSArray arrayWithObjects:@"abc 1", @"abc 21", @"abc 12",@"abc 13",@"abc 05",nil];
        NSComparator sortBlock = ^(id string1, id string2)
        {
            return [string1 compare:string2];
        };
        NSArray *sortArray = [stringArray sortedArrayUsingComparator:sortBlock];
        NSLog(@"sortArray:%@", sortArray);

運行結(jié)果:

sortArray:(
  "abc 05",
  "abc 1",
  "abc 12",
  "abc 13",
  "abc 21"
)

4.3代碼塊的遞歸調(diào)用
代碼塊想要遞歸調(diào)用,代碼塊變量必須是全局變量或者是靜態(tài)變量,這樣在程序啟動的時候代碼塊變量就初始化了,可以遞歸調(diào)用

復(fù)制代碼 代碼如下:

        static void (^ const blocks)(int) = ^(int i)
        {
            if (i > 0) {
                NSLog(@"num:%d", i);
                blocks(i - 1);
            }
        };
        blocks(3);

運行打印結(jié)果:

num:3
num:2
num:1

4.4在代碼塊中使用局部變量和全局變量
在代碼塊中可以使用和改變?nèi)肿兞?br />

復(fù)制代碼 代碼如下:

int global = 1000;
int main(int argc, const char * argv[])
{
    @autoreleasepool {
        void(^block)(void) = ^(void)
        {
            global++;
            NSLog(@"global:%d", global);
        };
        block();
        NSLog(@"global:%d", global);
    }
    return 0;
}

運行打印結(jié)果:

global:1001
global:1001

而局部變量可以使用,但是不能改變。

復(fù)制代碼 代碼如下:

        int local = 500;
        void(^block)(void) = ^(void)
        {
//            local++;
            NSLog(@"local:%d", local);
        };
        block();
        NSLog(@"local:%d", local);

在代碼塊中改變局部變量編譯不通過。怎么在代碼塊中改變局部變量呢?在局部變量前面加上關(guān)鍵字:__block
復(fù)制代碼 代碼如下:

        __block int local = 500;
        void(^block)(void) = ^(void)
        {
            local++;
            NSLog(@"local:%d", local);
        };
        block();
        NSLog(@"local:%d", local);

運行結(jié)果:

local:501
     local:501

相關(guān)文章

  • iOS 隱藏tabbar代碼詳解

    iOS 隱藏tabbar代碼詳解

    這篇文章主要介紹了iOS 隱藏tabbar代碼詳解的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2016-07-07
  • iOS中創(chuàng)建表格類視圖WBDataGridView的實例代碼

    iOS中創(chuàng)建表格類視圖WBDataGridView的實例代碼

    這篇文章主要介紹了iOS中創(chuàng)建表格類視圖WBDataGridView的實例代碼,需要的朋友可以參考下
    2017-02-02
  • 淺談強(qiáng)大易用支持URL Rewrite的iOS路由庫FFRouter

    淺談強(qiáng)大易用支持URL Rewrite的iOS路由庫FFRouter

    FRouter 是 iOS 中一個強(qiáng)大且易用的 URL 路由庫,支持 URL Rewrite,基于匹配查找 URL,效率高。非常具有實用價值,需要的朋友可以參考下
    2018-10-10
  • Objective-C中NSNumber與NSDictionary的用法簡介

    Objective-C中NSNumber與NSDictionary的用法簡介

    這篇文章主要介紹了Objective-C中NSNumber與NSDictionary的用法簡介,雖然Objective-C即將不再是iOS的主流開發(fā)語言...well,需要的朋友可以參考下
    2015-09-09
  • iOS實現(xiàn)帶文字的圓形頭像效果

    iOS實現(xiàn)帶文字的圓形頭像效果

    隨著騰訊QQ的普及,現(xiàn)在越來越多的社交類APP在顯示頭像的時候,都選擇了圓形頭像,本文將更進(jìn)一步的介紹如何實現(xiàn)帶文字的圓形頭像效果,效果非常不錯,感興趣的朋友們可以參考借鑒,下面來一起看看吧。
    2016-10-10
  • ios基礎(chǔ)教程之常見的數(shù)組使用方法

    ios基礎(chǔ)教程之常見的數(shù)組使用方法

    這篇文章主要給大家介紹了關(guān)于ios基礎(chǔ)教程之常見的數(shù)組使用方法的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-01-01
  • Navigation bar的注意事項詳解

    Navigation bar的注意事項詳解

    本文主要介紹了Navigation bar的注意事項。具有一定的參考價值,下面跟著小編一起來看下吧
    2017-01-01
  • iOS 請求權(quán)限封裝類的實例代碼

    iOS 請求權(quán)限封裝類的實例代碼

    下面小編就為大家分享一篇iOS 請求權(quán)限封裝類的實例代碼,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-01-01
  • IOS 開發(fā)之NSURL基本操作

    IOS 開發(fā)之NSURL基本操作

    這篇文章主要介紹了IOS 開發(fā)之NSURL基本操作的相關(guān)資料,需要的朋友可以參考下
    2017-03-03
  • IOS 詳解socket編程[oc]粘包、半包處理

    IOS 詳解socket編程[oc]粘包、半包處理

    這篇文章主要介紹了IOS 詳解socket編程[oc]粘包、半包處理的相關(guān)資料,需要的朋友可以參考下
    2017-02-02

最新評論