現(xiàn)講一下屬性和成員兩變量的區(qū)別?好多人只知道@property (nonatomic,assign) NSInteger age;,其實(shí)property = getter + setter +ivar。一個(gè)getter方法,一個(gè)是setter方法,另外一個(gè)是成員變量,ivar就是成員變量。
我們定義2個(gè)property和幾個(gè)ivar,然后使用runtime取出來(lái)確認(rèn)一下他們的關(guān)系;
看代碼:
- (void)viewDidLoad {
[super viewDidLoad];
unsigned int count = 0;
objc_property_t *propertyList = class_copyPropertyList([Person class], &count);
for (int i =0; i < count; i ++) {
objc_property_t p = propertyList[i];
NSLog(@" %s",property_getName(p));
}
free(propertyList);
[self getAllIvarList];
[self getAllMethodList];
}
- (void) getAllIvarList {
unsigned int methodCount = 0;
Ivar * ivars = class_copyIvarList([Person class], &methodCount);
for (unsigned int i = 0; i < methodCount; i ++) {
Ivar ivar = ivars[i];
const char * name = ivar_getName(ivar);
const char*type = ivar_getTypeEncoding(ivar);
NSLog(@"成員變量的類(lèi)型為%s,名字為 %s ",type, name);
}
free(ivars);
}
- (void) getAllMethodList{
unsigned int count = 0;
Method *methods = class_copyMethodList([Person class], &count);
for (int i = 0; i < count; i ++) {
Method m = methods[i];
SEL sel = method_getName(m);
NSLog(@"method:%@",NSStringFromSelector(sel));
}
}
然后在看一下輸出的參數(shù):
age
name
成員變量的類(lèi)型為@"NSString",名字為 publicName
成員變量的類(lèi)型為@"NSString",名字為 privateName
成員變量的類(lèi)型為@"NSString",名字為 protectedName
成員變量的類(lèi)型為@"NSString",名字為 packageName
成員變量的類(lèi)型為q,名字為 _age
成員變量的類(lèi)型為@"NSString",名字為 _name
method:initWithDic:
method:.cxx_destruct
method:name
method:setName:
method:init
method:setAge:
method:age
通過(guò)代碼驗(yàn)證可以知道的是定義的property都是成對(duì)出現(xiàn)的setter和getter方法,而成員變量則沒(méi)生成。
那么成員變量一般寫(xiě)在匿名類(lèi)里邊,然后.m文件內(nèi)容在外部無(wú)法訪問(wèn)的,造成了修飾符沒(méi)有起到作用。如下圖:
先看一下Objec-C:
#import "Person.h"
@interface Person ()
{
@public
NSString *publicName;
@private
NSString *privateName;
@protected
NSString * protectedName;
@package
NSString *packageName;
}
@end
因?yàn)楸旧碓?m中,成員變量本身在外部就是訪問(wèn)不到的,用public修飾也沒(méi)用。
正確的是:
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
{
@public
NSString *publicName;
@private
NSString *privateName;
@protected
NSString * protectedName;
@package
NSString *packageName;
}
然后我們測(cè)試一下這4個(gè)層級(jí)的范圍:


可以看到編譯器直接報(bào)錯(cuò),private是僅僅當(dāng)前類(lèi)和類(lèi)別中可用,子類(lèi)都不能用,范圍最小。

@protected 在外部不可以訪問(wèn),只能在子類(lèi)和自己類(lèi),類(lèi)別中訪問(wèn)。

@public @package在自己生成的framework外部可以訪問(wèn),和public類(lèi)似。那么我們?cè)儆肒VC訪問(wèn)各個(gè)級(jí)別的看看能訪問(wèn)嗎?
初始化Person
- (instancetype)init{
self = [super init];
if (self) {
self->privateName = @"privateName";
self->protectedName = @"protectedName";
self->packageName = @"packageName";
self->publicName = @"publicName";
}
return self;
}
通過(guò)KVC訪問(wèn)成員變量:

使用KVC訪問(wèn)framework中的成員變量測(cè)試:

經(jīng)過(guò)測(cè)試私有和受保護(hù)的使用KVC也可以訪問(wèn)和賦值的,其實(shí)系統(tǒng)中有些成員變量我們也可以沖過(guò)使用這個(gè)方式來(lái)訪問(wèn)。
其實(shí)根據(jù)測(cè)試,OC類(lèi)中和子類(lèi)KVC都可訪問(wèn)所有成員變量。
測(cè)試版本:Xcode 10.2.1
@private私有的
代表私有,也就是只有自己有,別人誰(shuí)都不可用,不不可以繼承的。
@protected受保護(hù)的
相較上邊的private而言,就沒(méi)有那么自私了,他自己可以用,自己的子類(lèi)也是可以共享的,是可以繼承的.
@public公共的
相較上邊而言,誰(shuí)都可以用,只要你有這個(gè)類(lèi)的對(duì)象,就可以拿到public下的變量,
@package包
這個(gè)主要是用于框架類(lèi),使用@private太限制,使用@protected或者@public又太開(kāi)放,就使用這個(gè)package吧。
那么我們?cè)诳匆幌耂wift 5.0:
private
訪問(wèn)級(jí)別所修飾的屬性或者方法只能在當(dāng)前類(lèi)里訪問(wèn)
fileprivate
訪問(wèn)級(jí)別所修飾的屬性或者方法在當(dāng)前的 Swift 源文件里可以訪問(wèn)
internal
訪問(wèn)級(jí)別所修飾的屬性或方法在源代碼所在的整個(gè)模塊都可以訪問(wèn)
如果是框架或者庫(kù)代碼,則在整個(gè)框架內(nèi)部都可以訪問(wèn),框架由外部代碼所引用時(shí),則不可以訪問(wèn)
public
可以被任何人訪問(wèn)。但其他 module 中不可以被 override 和繼承,而在 module 內(nèi)可以被 override 和繼承
open
可以被任何人使用,包括 override 和繼承
從高到低排序如下:
open > public > interal > fileprivate > private
Swift測(cè)試代碼:
import Foundation
class Car: NSObject {
var name = "car_name"
private var privateName = "privateName"
public var publicName = "publicName"
fileprivate var fileprivateName = "fileprivateName"
open var openName = "openName"
}
class Car2: Car {
override init() {
super.init()
self.name = ""
self.publicName = ""
self.fileprivateName = ""
self.openName = ""
===============本行報(bào)錯(cuò) ===============
self.privateName = ""
}
func loadCar() -> Void {
let car = Car()
car.fileprivateName = ""
car.publicName = ""
car.name = ""
car.openName = ""
}
}

然后使用swift制作framework之后測(cè)試如下:
測(cè)試打包前源代碼:
open class Car: NSObject {
var name = "car_name"
private var privateName = "privateName"
public var publicName = "publicName"
fileprivate var fileprivateName = "fileprivateName"
open var openName = "openName"
open func work() -> Void{
print("car work")
}
public func publicWork() -> Void{
print("car publicwork")
}
}
public class Car2: Car {
override init() {
super.init()
self.name = ""
self.publicName = ""
self.fileprivateName = ""
self.openName = ""
// self.privateName = ""
}
override public func work() {
}
override public func publicWork() {
}
func loadCar() -> Void {
let car = Car()
car.fileprivateName = ""
car.publicName = ""
car.name = ""
car.openName = ""
}
}

編譯之前:
var name = "car_name"
private var privateName = "privateName"
public var publicName = "publicName"
fileprivate var fileprivateName = "fileprivateName"
open var openName = "openName"
編譯之后:
open class Car : NSObject {
public var publicName: String
open var openName: String
open func work()
public func publicWork()
}
打包成framework,則public和open 修飾的屬性可見(jiàn),其他的則不在model中。
參考文檔:
官方文檔