52個有效方法(6) - 理解“屬性”這一概念

来源:https://www.cnblogs.com/shikaiming/archive/2019/10/15/11679172.html
-Advertisement-
Play Games

1. 可以用@property語法來定義對象中所封裝的數據。 2. 通過“修飾詞”來指定存儲數據所需的正確語義。 3. 在設置屬性所對應的實例變數時,一定要遵從該屬性所聲明的語義。 4. 開發iOS程式時應該使用nonatomic屬性,因為atomic(同步鎖)屬性嚴重影響性能。 ...


屬性

“屬性”(property)是OC的一項特性,用於封裝對象中的數據。

@property

  • @Property是聲明屬性的語法(@property = ivar + getter + setter)

    OC對象通常會把其所需的數據保存為各種實例變數(ivar)。實例變數一般通過“存取方法”(accessmethod)來訪問。

    什麼是存取方法:gettersetter 方法(access method = getter + setter),其中getter 用於獲取變數value, 而setter 用於寫入value

  • @Property可以快速方便的為實例變數創建存取器。

//  Man.h
#import <Foundation/Foundation.h>

@interface Man : NSObject
@property (nonatomic,strong)NSString *name;
@property (nonatomic,strong)NSString *sex;
@end

與下麵的寫法等效

//  Man.h
#import <Foundation/Foundation.h>

@interface Man : NSObject
{
    // 實例變數
    NSString *name;
    NSString *sex;
}
// setter
- (void)setName:(NSString *)newName;

// getter
- (NSString *)name;

// setter
- (void)setSex:(NSString *)newSex;

// getter
- (NSString *)sex;
@end
  • 通常使用“點語法” 來讓編譯器自動調用相關的存取方法(access method = getter + setter)。

    self. name = @"sky";
    NSString *name = self. name;

    點語法有什麼優勢呢?

    1. 省時,省力 :如果使用了屬性,編譯器會自動編寫訪問屬性所需的方法。這個過程由編譯器在編譯期執行,看不到這些get set 源代碼。

    2. 編譯器會自動向類中添加適當類型的實例變數,並且在屬性名前添加下劃線。

  • 如果你不想讓編譯器自動合成存取方法,則可以自己實現。如果你只實現了其中一個存取方法,那麼另一個還是會由編譯器來合成。

  • 當我們同時重寫了setter and getter方式時,系統會報錯,原因是找不到實例變數。其解決方法: 在.m的文件中使用@synthesize

@synthesize

  • @synthesize是為屬性添加一個實例變數名,或者說別名。同時會為該屬性生成 setter/getter 方法。

  • protocol中使用property只會生成settergetter方法聲明,我們使用屬性的目的,是希望遵守我協議的對象能實現該屬性。需要使用@synthesize生成settergetter

  • 當你在子類中重載了父類中的屬性,你必須 使用@synthesize來手動合成ivar

  • 當我們同時重寫了setter and getter方式時,需要在.m的文件中使用@synthesize

    //  Man.m
    #import "Man.h"
    
    @implementation Man
    @synthesize name = _name;
    // setter
    - (void)setName:(NSString *)name
    {
      _name = name;
    }
    
    // getter
    - (NSString *)name
    {
      return _name;
    }
    @end
    • **@synthesize name = _name**

      • _name是成員變數

      • name是屬性

      • 作用是告訴編譯器name屬性為_name實例變數生成setter and getter方法的實現

      • name屬性的setter方法是setName,它操作的是_name這個變數

      • @synthesize中定義與變數名不同的settergetter的命名,以此來保護變數不會被不恰當的訪問setter=<name>這種不常用,也不推薦使用)

          //setter=<name>這種不常用,也不推薦使用
        @property (nonatomic, setter = mySetter,getter = myGetter ) NSString *name;
        
        @property (nonatomic,getter = isHidden ) BOOL hidden;
  • @property有兩個對應的詞,一個是 @synthesize,一個是 @dynamic。如果 @synthesize@dynamic都沒寫,那麼預設的就是@syntheszie var = _var

  • 如果某屬性已經在某處實現了自己的 setter/getter ,可以使用 @dynamic來阻止 @synthesize 自動生成新的 setter/getter 覆蓋。

@dynamic

  • @dynamic告訴編譯器:屬性的 settergetter 方法由用戶自己實現,不自動生成。(當然對於 readonly 的屬性只需提供 getter 即可)。

  • 假如一個屬性被聲明為 @dynamic var,然後你沒有提供@setter方法和 @getter 方法。編譯的時候沒問題,但是當程式運行到 instance.var = someVar,由於缺 setter 方法會導致程式崩潰。或者當運行到 someVar = var時,由於缺 getter 方法同樣會導致崩潰。

  • 編譯時沒問題,運行時才執行相應的方法,這就是所謂的動態綁定。

//  Man.h
#import <Foundation/Foundation.h>

@interface Man : NSObject
@property (nonatomic,strong)NSString *name;
@end
//  Man.m
#import "Man.h"

@implementation Man
@dynamic name;
// setter
// - (void)setName:(NSString *)name
// {
//   _name = name;
// }
// getter
- (NSString *)name
{
  return _name;
}
@end

調用時會出現崩潰

    Man *man = [[Man alloc] init];
    man.name = @"sky";//缺 setter 方法會導致程式崩潰
    NSString *name = man.name;//缺 getter 方法同樣會導致崩潰

屬性特質

原子性

  • atomic(預設):atomic意為操作是原子的,意味著只有一個線程訪問實例變數(生成的settergetter方法是一個原子操作)。atomic是線程安全的,至少在當前的存取器上是安全的。它是一個預設的特性,但是很少使用,因為比較影響效率。

  • nonatomicnonatomic意為操作是非原子的,可以被多個線程訪問。它的效率比atomic快。但不能保證在多線程環境下的安全性,開發中常用。

  • 開發iOS程式時應該使用nonatomic屬性,因為atomic(同步鎖)屬性嚴重影響性能。該屬性使用了同步鎖,會在創建時生成一些額外的代碼用於幫助編寫多線程程式,這會帶來性能問題,通過聲明nonatomic可以節省這些雖然很小但是不必要額外開銷。

存取器控制

  • readwrite(預設):readwrite是預設值,表示該屬性同時擁有settergetter

  • readonlyreadonly表示只有getter沒有setter

  • 有時候為了語意更明確可能需要自定義訪問器的名字。

//setter=<name>這種不常用,也不推薦使用
@property (nonatomic, setter = mySetter,getter = myGetter ) NSString *name;
  

@property (nonatomic,getter = isHidden ) BOOL hidden;

記憶體管理

  • assign(預設):assign用於非指針變數(值)類型,統一由系統棧進行記憶體管理。一般用於基礎類型和C數據類型,如intfloatdoubleNSIntegerCGFloat等表示單純的複製。還包括不存在所有權關係的對象,比如常見的delegate

  • retain:在setter方法中,需要對傳入的對象進行引用計數加1的操作。

  • strongstrong是在IOS引入ARC的時候引入的關鍵字,是retain的一個可選的替代。對傳入的對象的強引用,會增加對象的引用計數。strongretain的意思相同並產生相同的代碼,但是語意上更好更能體現對象的關係。

  • weak:對傳入的對象的弱引用,不增加對象的引用計數,也不持有對象,當對象消失後指針自動指向nil

  • copy:與strong類似,但區別在於copy是創建一個新對象,strong是創建一個指針,引用對象計數加1

舉例說明weakstrongcopy屬性特質的差異

  • 首先創建兩個自定義的Person類的實例變數,並分別用weakstrong修飾。
@property (nonatomic,strong) Person *strongPerson;
@property (nonatomic,weak) Person *weakPerson;
  • strongPerson屬性置nil
self.strongPerson = [[Person alloc] init];
self.weakPerson = self.strongPerson;
self.strongPerson = nil; 
NSLog(@"strongStr=%@,weakStr=%@",self.strongPerson,self.weakPerson);

輸出結果為:strongStr=(null),weakStr=(null)。說明weak修飾的屬性並不會使引用計數增加。

  • 如果使用NSString類進行上述類似操作,得到的結果是不同的。
@property (nonatomic,strong) NSString *strongStr;
@property (nonatomic,weak) NSString *weakStr;
···
self.strongStr = @"string";
self.weakStr = self.strongStr;
self.strongStr = nil;
NSLog(@"strongStr=%@,weakStr=%@",self.strongStr,self.weakStr);

輸出結果為:strongStr=(null),weakStr=string。這裡主要是因為NSString類型的賦值預設會加上copy,而copy會創建一個新的對象。這裡的賦值語句其實是

self.strongStr = [@"string" copy];
self.weakStr = [self.strongStr copy];
  • weakPerson屬性置nil
self.strongPerson = [[Person alloc] init];
self.weakPerson = self.strongPerson;
self.weakPerson = nil;
NSLog(@"strongStr=%@,weakStr=%@",self.strongPerson,self.weakPerson);

輸出結果如下:strongStr=<Person: 0x600000007d50>,weakStr=(null)。說明weak修飾的屬性只是對對象的弱引用,並不會真正的持有該對象。

  • 新建一個Person類實例變數p,賦值strongPerson後將pnil
Person *p = [[Person alloc] init];
self.strongPerson = p;
self.weakPerson = self.strongPerson;
p = nil; 
NSLog(@"strongStr=%@,weakStr=%@",self.strongPerson,self.weakPerson);

輸出結果為:strongStr=<Person: 0x600000200b50>,weakStr=<Person: 0x600000200b50>。因為strong屬性會強引用該對象並使該對象的引用計數+1,所以即使把p設置為nil,該對象也並沒有釋放,要想釋放該對象,還得把strongStr設置為nil:self.strongPerson = nil;。這樣輸出結果才為 strongStr=(null),weakStr=(null)

  • 在給Person類加了一個name屬性。並用copy修飾 :(@property (nonatomic,copy) NSString *name)。
NSString *a = @"xiaoming";
Person *p = [[Person alloc] init];
p.name = a;
NSLog(@"before p.name=%@",p.name);
a = @"xiaohua";
NSLog(@"after p.name=%@",p.name);

輸出結果:before p.name=xiaomingafter p.name=xiaoming。因為copy關鍵字修飾的屬性是將對象拷貝一份賦值,所以你改變原對象並不會對拷貝後的對象有任何改變。

註:用@property聲明 NSStringNSArrayNSDictionary 經常使用copy關鍵字,是因為他們有對應的可變類型:NSMutableStringNSMutableArrayNSMutableDictionary,他們之間可能進行賦值操作,為確保對象中的字元串值不會無意間變動,應該在設置新屬性值時拷貝一份.

要點

  1. 可以用@property語法來定義對象中所封裝的數據。

  2. 通過“修飾詞”來指定存儲數據所需的正確語義。

  3. 在設置屬性所對應的實例變數時,一定要遵從該屬性所聲明的語義。

  4. 開發iOS程式時應該使用nonatomic屬性,因為atomic(同步鎖)屬性嚴重影響性能。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 今天在檢查oracle rac集群時,突然才發現伺服器的根目錄下麵占用了很多空間,照道理不應該出現這種情況,初步猜想可能是哪個日誌或跟蹤文件太大導致。切換到跟目錄,使用du -sh *來一層一層查看到底是哪個文件占用了這麼多空間,最後定位到目錄/u01/app/11.2.0/grid/crf/db/ ...
  • 創建資料庫 在MySQL中,使用 CREATE DATABASE 或 CREATE SCHEMA 語句創建資料庫 語法結構: : 表示為可選 : 用於分隔花括弧中的選項,表示任選一項語法 : 標識具體的資料庫命名,必須符合操作系統文件夾命名規則,在MySQL中不區分大小寫 : 預設值 : 指定資料庫 ...
  • 現需要限定特定的用戶只能查看並訪問特定的資料庫,防止多個用戶對資料庫操作時一些誤操作。 參考i6first的如何讓用戶只能訪問特定的資料庫(MSSQL)博文 1.新建登錄用戶 以管理員身份登陸資料庫(許可權最高的身份如sa),點擊安全性->登錄名,右鍵新建登錄名,輸入登錄名和密碼,取消強制實施密碼策略 ...
  • 1、去官網查找最新(你需要的)安裝包版本 # https://dev.mysql.com/downloads/repo/yum/ 2、下載MySQL安裝包 # wget http://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.r ...
  • 概述 數據完整性指資料庫中數據的 正確性、相容性和一致性 。包括現實世界中的應用需求的完整性。數據的完整性由完整性規則來定義。 關係模型的完整性規則是對關係的某種約束,提供一種手段來保證用戶對資料庫的修改時不會破壞資料庫中數據的完整性。保證數據是有意義的。 關係模型分三類約束:實體完整性約束、參照完 ...
  • 在SQL Server中重建索引(Rebuild Index)與重組索引(Reorganize Index)會觸發統計信息更新嗎? 那麼我們先來測試、驗證一下: 我們以AdventureWorks2014為測試環境,如下所示: Person.Person表的統計信息最後一次更新為2014-07-17... ...
  • 本文基於 Android 9.0 , 代碼倉庫地址 : "android_9.0.0_r45" 文中源碼鏈接: "SystemServer.java" "ActivityManagerService.java" "Process.java" "ZygoteProcess.java" 對 和 啟動流程 ...
  • 1. 在對象內部讀取數據時,應該直接通過實例變數來讀,而寫入數據時,則應通過屬性來寫。 2. 在初始化方法及dealloc方法中,總是應該直接通過實例變數來讀寫數據。 3. 使用Lazy Initialization配置的數據,應該通過屬性來讀取數據。 4. 不要在setter/g... ...
一周排行
    -Advertisement-
    Play Games
  • 前言 在我們開發過程中基本上不可或缺的用到一些敏感機密數據,比如SQL伺服器的連接串或者是OAuth2的Secret等,這些敏感數據在代碼中是不太安全的,我們不應該在源代碼中存儲密碼和其他的敏感數據,一種推薦的方式是通過Asp.Net Core的機密管理器。 機密管理器 在 ASP.NET Core ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 順序棧的介面程式 目錄順序棧的介面程式頭文件創建順序棧入棧出棧利用棧將10進位轉16進位數驗證 頭文件 #include <stdio.h> #include <stdbool.h> #include <stdlib.h> 創建順序棧 // 指的是順序棧中的元素的數據類型,用戶可以根據需要進行修改 ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • C總結與剖析:關鍵字篇 -- <<C語言深度解剖>> 目錄C總結與剖析:關鍵字篇 -- <<C語言深度解剖>>程式的本質:二進位文件變數1.變數:記憶體上的某個位置開闢的空間2.變數的初始化3.為什麼要有變數4.局部變數與全局變數5.變數的大小由類型決定6.任何一個變數,記憶體賦值都是從低地址開始往高地 ...
  • 如果讓你來做一個有狀態流式應用的故障恢復,你會如何來做呢? 單機和多機會遇到什麼不同的問題? Flink Checkpoint 是做什麼用的?原理是什麼? ...
  • C++ 多級繼承 多級繼承是一種面向對象編程(OOP)特性,允許一個類從多個基類繼承屬性和方法。它使代碼更易於組織和維護,並促進代碼重用。 多級繼承的語法 在 C++ 中,使用 : 符號來指定繼承關係。多級繼承的語法如下: class DerivedClass : public BaseClass1 ...
  • 前言 什麼是SpringCloud? Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的開發便利性簡化了分散式系統的開發,比如服務註冊、服務發現、網關、路由、鏈路追蹤等。Spring Cloud 並不是重覆造輪子,而是將市面上開發得比較好的模塊集成進去,進行封裝,從 ...
  • class_template 類模板和函數模板的定義和使用類似,我們已經進行了介紹。有時,有兩個或多個類,其功能是相同的,僅僅是數據類型不同。類模板用於實現類所需數據的類型參數化 template<class NameType, class AgeType> class Person { publi ...
  • 目錄system v IPC簡介共用記憶體需要用到的函數介面shmget函數--獲取對象IDshmat函數--獲得映射空間shmctl函數--釋放資源共用記憶體實現思路註意 system v IPC簡介 消息隊列、共用記憶體和信號量統稱為system v IPC(進程間通信機制),V是羅馬數字5,是UNI ...