日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Object-C语法

發布時間:2023/12/14 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Object-C语法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

面向對象

我們平時編寫的Objective-C代碼,底層實現其實都是C\C++代碼
所以Objective-C的面向對象都是基于C\C++的數據結構實現的

Objective-C的對象、類主要是基于C\C++的什么數據結構實現的?
結構體

將Objective-C代碼轉換為C\C++代碼

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc OC源文件 -o 輸出的CPP文件 如果需要鏈接其他框架,使用-framework參數。比如-framework UIKit

一個OC對象在內存中是如何布局的

// 指針 isa typedef struct objc_class *Class;// NSObject Implementation struct NSObject_IMPL {Class isa; // 8個字節 };

指針在64位系統中占8個字節、 32位系統中 占4個字節

OC對象的本質

下面類占幾個字節內存

@interface Student : NSObject {@publicint _no;int _age; } @end@implementation Student@endint main(int argc, const char * argv[]) {@autoreleasepool {Student *stu = [[Student alloc] init];stu->_no = 4;stu->_age = 5;NSLog(@"%zd", class_getInstanceSize([Student class]));NSLog(@"%zd", malloc_size((__bridge const void *)stu));struct Student_IMPL *stuImpl = (__bridge struct Student_IMPL *)stu;NSLog(@"no is %d, age is %d", stuImpl->_no, stuImpl->_age);}return 0; }

如果繼承的話: 子類的結構中會有一個父類的結構

父類 struct OC_IMPL {Class isa; }; 子類 struct OC_11_IMPL:OC_IMPL {OC_IMPL oc_impl;int _no;int _age; };

Tips:

內存對齊: 結構體的最終大小必須是最大成員大小的倍數

屬性和方法

屬性會自動生成對應的成員變量和set get 方法

方法是存儲在類對象中的(只有一分就夠了,不需要創建那么多),所以不會影響對象的內存占用

FACE

一個NSObject對象占用多少內存

  • 系統分配了16個字節給NSObject對象(通過malloc_size函數獲得)(如果小于16則等于16)
  • 但NSObject對象內部只使用了8個字節的空間(64bit環境下,可以通過class_getInstanceSize函數獲得)
NSObject *obj = [[NSObject alloc] init];// 獲得NSObject實例對象的成員變量所占用的大小 >> 8 // #import <objc/runtime.h> NSLog(@"%zd", class_getInstanceSize([NSObject class]));// 獲得obj指針所指向內存的大小 >> 16 // #import <malloc/malloc.h> NSLog(@"%zd", malloc_size((__bridge const void *)obj));

結構體內存對齊;用來計算結構體的大小
操作系統在分配內存的時候也有一個分配對齊(最小分配大小: 有很多分好的塊 16 32 64等 都是16的倍數,靠近哪一個分給你哪一個)

三個獲取大小的方法

sizeof : 獲取這個類型的大小,他不是方法
class_getInstanceSize() 獲取類實際占用的空間大小
malloc_size() 獲取某個實例實際分配的內存大小

LLDB

打印對象或者值

  • print、p:打印
  • po:打印對象
  • 讀取內存

    memory read/數量格式字節數 內存地址
    簡稱: x
    x/數量格式字節數 內存地址

    格式

    x是16進制,f是浮點,d是10進制

    字節大小

    b:byte 1字節,h:half word 2字節
    w:word 4字節,g:giant word 8字節

    修改內存中的值

    memory write 內存地址 數值

    memory write 0x0000010 10

    OC對象的分類

    Objective-C中的對象,簡稱OC對象,主要可以分為3種

    instance對象(實例對象)

    instance對象就是通過類alloc出來的對象,每次調用alloc都會產生新的instance對象

    instance對象在內存中存儲的信息包括

    isa指針
    其他成員變量

    class對象(類對象)

    每個類在內存中有且只有一個class對象
    獲取類對象的三種方法

    Class objectClass1 = [object1 class];#import <objc/runtime.h>Class objectClass3 = object_getClass(object1);Class objectClass5 = [NSObject class];

    類對象在內存中存儲的信息包括

    isa指針
    superclass指針
    類的屬性信息(@property)、類的對象方法信息(instance method)
    類的協議信息(protocol)、類的成員變量信息(ivar)

    meta-class對象(元類對象)

    每個類在內存中有且只有一個meta-class對象
    獲取

    // 將類對象當做參數傳入,獲得元類對象#import <objc/runtime.h>Class objectMetaClass = object_getClass(objectClass5);

    meta-class對象和class對象的內存結構是一樣的,但是用途不一樣,在內存中存儲的信息主要包括

    三個函數

    1.Class objc_getClass(const char *aClassName)1> 傳入字符串類名2> 返回對應的類對象2.Class object_getClass(id obj)1> 傳入的obj可能是instance對象、class對象、meta-class對象2> 返回值a) 如果是instance對象,返回class對象b) 如果是class對象,返回meta-class對象c) 如果是meta-class對象,返回NSObject(基類)的meta-class對象3.- (Class)class、+ (Class)class1> 返回的就是類對象- (Class) {return self->isa;}+ (Class) {return self;}

    元類對象在內存中存儲的信息包括

    isa指針
    superclass指針
    類方法

    判斷對象是不是元類對象

    class_isMetaClass(objectMetaClass)

    isa

    instance的isa指向class
    當調用對象方法時,通過instance的isa找到class,最后找到對象方法的實現進行調用

    class的isa指向meta-class
    當調用類方法時,通過class的isa找到meta-class,最后找到類方法的實現進行調用

    superclass

    類對象的superclass

    繼承中 調用父類的方法就需要先獲取到自己的類對象 然后在獲取到類對象的父對象 然后發消息
    exp:
    當Student的instance對象要調用Person的對象方法時,會先通過isa找到Student的class,然后通過superclass找到Person的class,最后找到對象方法的實現進行調用

    元類對象的superclass

    當Student的class要調用Person的類方法時,會先通過isa找到Student的meta-class,然后通過superclass找到Person的meta-class,最后找到類方法的實現進行調用

    總結

    instance的isa指向class

    class的isa指向meta-class

    meta-class的isa指向基類的meta-class

    class的superclass指向父類的class
    如果沒有父類,superclass指針為nil

    meta-class的superclass指向父類的meta-class
    基類的meta-class的superclass指向基類的class

    instance調用對象方法的軌跡
    isa找到class,方法不存在,就通過superclass找父類

    class調用類方法的軌跡
    isa找meta-class,方法不存在,就通過superclass找父類

    face

    對象的isa指針指向哪里?

    instance對象的isa指向class對象
    class對象的isa指向meta-class對象
    meta-class對象的isa指向基類的meta-class對象

    OC的類信息存放在哪里?

    對象方法、屬性、成員變量、協議信息,存放在class對象中
    類方法,存放在meta-class對象中
    成員變量的具體值,存放在instance對象

    class的結構

    按理說 實例對象的isa 里存儲著 類對象的地址, 類對象的isa中存儲著元類對象的地址
    實際情況是 :
    從64bit開始,isa需要進行一次位運算,才能計算出真實地址

    https://opensource.apple.com/tarballs/objc4/

    class、meta-class對象的本質結構都是struct objc_class

    KVO

    使用

    KVO的全稱是Key-Value Observing,俗稱“鍵值監聽”,可以用于監聽某個對象屬性值的改變

    // 給person1對象添加KVO監聽NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;[self.person addObserver:self forKeyPath:@"age" options:options context:@"123"];[self.person addObserver:self forKeyPath:@"height" options:options context:@"456"];// 當監聽對象的屬性值發生改變時,就會調用 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {NSLog(@"監聽到%@的%@屬性值改變了 - %@ - %@", object, keyPath, change, context); } // 在控制器銷毀之前需要移除監聽 - (void)dealloc {[self.person removeObserver:self forKeyPath:@"age"];[self.person removeObserver:self forKeyPath:@"height"]; }

    本質

    如果你的實例對象添加KVO后, isa 指向的是另一個類對象(可以 po 打印 當前實例的isa)

    NSKVONotifying_XXPerson是使用Runtime動態創建的一個類,是XXPerson的子類

    在改變屬性值的時候會先找到NSKVONotifying_XXPerson的set方法;

    偽代碼實現

    @implementation NSKVONotifying_XXPerson- (void)setAge:(int)age {_NSSetIntValueAndNotify(); }// 偽代碼 void _NSSetIntValueAndNotify() {[self willChangeValueForKey:@"age"];// 調用了父類的set方法(原來XXPerson里面)[super setAge:age];[self didChangeValueForKey:@"age"]; }- (void)didChangeValueForKey:(NSString *)key {// 通知監聽器,某某屬性值發生了改變[oberser observeValueForKeyPath:key ofObject:self change:nil context:nil]; }@end

    lldb 打印方法的實現

    p (IMP)方法地址

    face

    iOS用什么方式實現對一個對象的KVO?(KVO的本質是什么?)

    利用RuntimeAPI動態生成一個子類,并且讓instance對象的isa指向這個全新的子類
    當修改instance對象的屬性時,會調用Foundation的_NSSetXXXValueAndNotify函數

    過程

    willChangeValueForKey:
    父類原來的setter
    didChangeValueForKey:
    內部會觸發監聽器(Oberser)的監聽方法( observeValueForKeyPath:ofObject:change:context:)

    如何手動觸發KVO?

    手動調用willChangeValueForKey:和didChangeValueForKey:

    直接修改成員變量會觸發KVO么?

    不會觸發KVO

    KVC

    KVC的全稱是Key-Value Coding,俗稱“鍵值編碼”,可以通過一個key來訪問某個屬性

    常見的API有

    - (void)setValue:(id)value forKeyPath:(NSString *)keyPath; - (void)setValue:(id)value forKey:(NSString *)key; - (id)valueForKeyPath:(NSString *)keyPath; - (id)valueForKey:(NSString *)key;

    setValue:(id)value forKey

    accessInstanceVariablesDirectly方法的默認返回值是YES

    valueForKey:的原理

    face

    通過KVC修改屬性會觸發KVO么?

    會觸發KVO

    KVC的賦值和取值過程是怎樣的?原理是什么?

    Category

    Category的底層結構

    定義在objc-runtime-new.h中

    Category的加載處理過程

    通過Runtime加載某個類的所有Category數據

    把所有Category的方法、屬性、協議數據,合并到一個大數組中
    后面參與編譯的Category數據,會在數組的前面

    將合并后的分類數據(方法、屬性、協議),插入到類原來數據的前面

    合并是在運行中而不是在編譯的時候;在編譯的時候會先將分類存儲在一個叫Category_t的結構體里面,每一個分類都對應一個結構體實例
    在運行中用到的時候合并到所屬類的類對象和元類對象

    +load方法

    +load方法會在runtime加載類、分類時調用
    每個類、分類的+load,在程序運行過程中只調用一次

    調用順序

    先調用類的+load
    按照編譯先后順序調用(先編譯,先調用)
    調用子類的+load之前會先調用父類的+load

    再調用分類的+load
    按照編譯先后順序調用(先編譯,先調用)

    +load方法是根據方法地址直接調用,并不是經過objc_msgSend函數調用

    +initialize方法

    +initialize方法會在類第一次接收到消息時調用

    調用順序

    先調用父類的+initialize,再調用子類的+initialize
    (先初始化父類,再初始化子類,每個類只會初始化1次)

    +initialize和+load的很大區別是,+initialize是通過objc_msgSend進行調用的,所以有以下特點

    如果子類沒有實現+initialize,會調用父類的+initialize(所以父類的+initialize可能會被調用 多次)
    如果分類實現了+initialize,就覆蓋類本身的+initialize調用

    Cateogry-memmove、memcpy區別

    memmove : 內存移動 可以實現向前向后移動內存(會判斷)保證以前的數據完整的保存在新的位置上
    memcpy : 內存拷貝 直接挪動 會直接覆蓋

    face

    Category的實現原理

    Category編譯之后的底層結構是struct category_t,里面存儲著分類的對象方法、類方法、屬性、協議信息
    在程序運行的時候,runtime會將Category的數據,合并到類信息中(類對象、元類對象中)

    Category和Class Extension的區別是什么?

    Class Extension在編譯的時候,它的數據就已經包含在類信息中
    Category是在運行時,才會將數據合并到類信息中

    Category中有load方法嗎?load方法是什么時候調用的?load 方法能繼承嗎?

    有load方法
    load方法在runtime加載類、分類的時候調用
    load方法可以繼承,但是一般情況下不會主動去調用load方法,都是讓系統自動調用

    load、initialize方法的區別什么?

    1.調用方式
    1> load是根據函數地址直接調用
    2> initialize是通過objc_msgSend調用

    2.調用時刻
    1> load是runtime加載類、分類的時候調用(只會調用1次)
    2> initialize是類第一次接收到消息的時候調用,每一個類只會initialize一次(父類的initialize方法可能會被調用多次)

    load、initialize的調用順序?

    1.load
    1> 先調用類的load
    a) 先編譯的類,優先調用load
    b) 調用子類的load之前,會先調用父類的load

    2> 再調用分類的load
    a) 先編譯的分類,優先調用load

    2.initialize
    1> 先初始化父類
    2> 再初始化子類(可能最終調用的是父類的initialize方法)

    關聯對象

    如何實現給分類“添加成員變量”?
    默認情況下,因為分類底層結構的限制,不能添加成員變量到分類中。但可以通過關聯對象來間接實現

    關聯對象提供了以下API

    添加關聯對象 void objc_setAssociatedObject(id object, const void * key,id value, objc_AssociationPolicy policy)獲得關聯對象 id objc_getAssociatedObject(id object, const void * key)移除所有的關聯對象 void objc_removeAssociatedObjects(id object)

    key的常見用法

    static void *MyKey = &MyKey; objc_setAssociatedObject(obj, MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC) objc_getAssociatedObject(obj, MyKey)static char MyKey; objc_setAssociatedObject(obj, &MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC) objc_getAssociatedObject(obj, &MyKey)使用屬性名作為key objc_setAssociatedObject(obj, @"property", value, OBJC_ASSOCIATION_RETAIN_NONATOMIC); objc_getAssociatedObject(obj, @"property");使用get方法的@selecor作為key objc_setAssociatedObject(obj, @selector(getter), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC) objc_getAssociatedObject(obj, @selector(getter))

    objc_AssociationPolicy

    關聯對象的原理

    實現關聯對象技術的核心對象有

    AssociationsManager
    AssociationsHashMap
    ObjectAssociationMap
    ObjcAssociation

    原理

    runtime 在底層實現了一個全局的字典來保存

    Category能否添加成員變量?如果可以,如何給Category添加成員變量?

    不能直接給Category添加成員變量,但是可以間接實現Category有成員變量的效果

    block

    block本質上也是一個OC對象,它內部也有個isa指針
    block是封裝了函數調用以及函數調用環境的OC對象

    變量

    自動變量 也是只存在于局部變量 默認就是auto: 意思就是離開作用于自動銷毀;block捕獲的時候是值傳遞

    static變量 block捕獲的時候是地址傳遞

    全局變量 block 不會進行捕獲,需要的時候直接使用就好

    類型

    block有3種類型,可以通過調用class方法或者isa指針查看具體類型,最終都是繼承自NSBlock類型
    NSGlobalBlock ( _NSConcreteGlobalBlock )
    NSStackBlock ( _NSConcreteStackBlock )
    NSMallocBlock ( _NSConcreteMallocBlock )

    每一種類型的block調用copy后的結果如下所示

    block的copy

    在ARC環境下,編譯器會根據情況自動將棧上的block復制到堆上,比如以下情況

    block作為函數返回值時
    將block賦值給__strong指針時
    block作為Cocoa API中方法名含有usingBlock的方法參數時
    block作為GCD API的方法參數時

    MRC下block屬性的建議寫法 @property (copy, nonatomic) void (^block)(void);ARC下block屬性的建議寫法 @property (strong, nonatomic) void (^block)(void); @property (copy, nonatomic) void (^block)(void);

    對象類型的auto變量

    當block內部訪問了對象類型的auto變量時

    如果block是在棧上,將不會對auto變量產生強引用

    如果block被拷貝到堆上
    會調用block內部的copy函數
    copy函數內部會調用_Block_object_assign函數
    _Block_object_assign函數會根據auto變量的修飾符(__strong、__weak、__unsafe_unretained)做出相應的操作,形成強引用(retain)或者弱引用

    如果block從堆上移除
    會調用block內部的dispose函數
    dispose函數內部會調用_Block_object_dispose函數
    _Block_object_dispose函數會自動釋放引用的auto變量(release)

    __Block

    __block可以用于解決block內部無法修改auto變量值的問題
    __block不能修飾全局變量、靜態變量(static)
    編譯器會將__block變量包裝成一個對象

    face

    block的原理是怎樣的?本質是什么?

    封裝了函數調用以及調用環境的OC對象

    __block的作用是什么?有什么使用注意點?

    block的屬性修飾詞為什么是copy?使用block有哪些使用注意?

    block一旦沒有進行copy操作,就不會在堆上
    使用注意:循環引用問題

    block在修改NSMutableArray,需不需要添加__block?

    總結

    以上是生活随笔為你收集整理的Object-C语法的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。