Playing with __attributes__ (四)

本文深入解析iOS中objc_designated_initializer属性的作用与使用方法,通过实例演示如何正确设置类的初始化流程,确保对象初始化过程的正确性和效率。

objc_designated_initializer

使用方法

@interface MyObject:NSObject
- (instancetype)init __attribute__((objc_designated_initializer));
@end

在iOS中也可以写成

- (instancetype)init NS_DESIGNATED_INITIALIZER;

该属性可以指定类的初始化方法。指定初识化方法并不是对使用者。而是对内部的现实。譬如,下面这种情况

实例讲解

@interface MyObject:NSObject
- (instancetype)initMyObject NS_DESIGNATED_INITIALIZER;
- (instancetype)initMyObjectNonDesignated;
@end

@implementation MyObject        //[8] 产生warning
- (instancetype)initMyObject{
    self = [super init];//[1] 没有warning
    return self;
}

- (instancetype)initMyObjectNonDesignated
{
    self = [self initMyObject];
    return self;
}
@end

@interface DerivedObject:MyObject
- (instancetype)initMyObject2;
- (instancetype)initMyObject3;
- (instancetype)initMyObject4 NS_DESIGNATED_INITIALIZER;
- (instancetype)initMyObject5;
- (instancetype)initMyObject6 NS_DESIGNATED_INITIALIZER;
- (instancetype)initMyObject7;
@end

@implementation DerivedObject   //[9] 产生warning
- (instancetype)initMyObject2{
    self = [super init];//[2] 产生warning
    return self;
}

- (instancetype)initMyObject3{
    self = [self initMyObjectNonDesignated];//[3] 没有warning
    return self;
}

- (instancetype)initMyObject4{
    self = [super initMyObject];//[4] 没有warning
    return self;
}

- (instancetype)initMyObject5{
    self = [self init];//[5] 没有warning
    return self;
}

- (instancetype)initMyObject6{
    self = [self initMyObject4];//[6] 产生warning
    return self;
}

- (instancetype)initMyObject7{
    self = [self initMyObject];//[7] 没有warning
    return self;
}
@end

解释一下:

  • 如果是DESIGNATED_INITIALIZER的初始化方法,就必须调用父类的DESIGNATED_INITIALIZER方法。

[1]没有warning,因为NSObjectinit也是DESIGNATED_INITIALIZER。[4]也同样正确,父类的initMyObject是DESIGNATED_INITIALIZER。所以[6]就不正确了,因为initMyObject4同样是DESIGNATED_INITIALIZER。

  • 如果不是DESIGNATED_INITIALIZER的初始化方法,但是该类拥有DESIGNATED_INITIALIZER初始化方法,那么:

  1. 必须调用该类的DESIGNATED_INITIALIZER方法或者非DESIGNATED_INITIALIZER方法。

  2. 不可以调用父类的任何初始化方法。

[2]调用的父类的方法 不正确,改成[5]这样就对了 [3]调用的该类的方法(从父类继承过来的),正确
[7]也调用的该类的方法(从父类继承过来,但会产生其他问题,见下面解释)

  • 如果一个类拥有DESIGNATED_INITIALIZER初始化方法,那它必须覆盖实现父类定义的DESIGNATED_INITIALIZER初始化方法。

[8] [9]都是因为没有覆盖实现父类的DESIGNATED_INITIALIZER方法

注:对于非DESIGNATED_INITIALIZER,llvm把它称为Convenience intializer

总述

这个attribute的目的其实是在初始化类的实例时,无论调用关系如何复杂,必须调用一次该类的Designated intializer(可以有多个),对于 Designated intializer,必须调用父类的Designated intializer。对于父类的父类这个规则亦然,对Designated intializer的调用一直要到根类。

对于上述例子,调用触发顺序应该为:

DerivedObject Convenience intializer-> 若干次其他DerivedObject Convenience intializer -> DerivedObject Designated intializer -> MyObject Designated intializer -> NSObject Designated intializer

其他

其实llvm还漏了一些细节,看上述代码:

- (instancetype)initMyObject3{
    self = [self initMyObjectNonDesignated];//[3] 没有warning,如果改成super 就有warning
    return self;
}

居然没有Warning!这样的话在类会跳过该类的Designated intializer。Holy High!从上述的解释来看,对Convenience intializer,llvm是没有要求所有的Convenience intializer必须调用Designated intializer,但这个attribute的设计思路要求终归要调用一次该类的Designated intializer

对于上述情况,我能想到的解释就是llvm还没智能到能分析较为复杂的情况。如不考虑继承。一个类的Convenience intializer总会有一个会调用Designated intializer,不然就会有循环调用的可能,所以基于这个假设,llvm没有对Convenience intializer调用Convenience intializer的情况抛出Warning,但却漏了继承过来的Convenience intializer情况。

当然这只是我的猜测。

原作写于segmentfault 链接

float AudioPolicyManager::adjustDeviceAttenuationForAbsVolume(IVolumeCurves &curves, 10761 VolumeSource volumeSource, 10762 int index, 10763 const DeviceTypeSet &deviceTypes) 10764 { 10765 audio_devices_t volumeDevice = Volume::getDeviceForVolume(deviceTypes); 10766 device_category deviceCategory = Volume::getDeviceCategory({volumeDevice}); 10767 float volumeDb = curves.volIndexToDb(deviceCategory, index); 10768 10769 const auto it = mAbsoluteVolumeDrivingStreams.find(volumeDevice); 10770 if (it != mAbsoluteVolumeDrivingStreams.end()) { 10771 audio_attributes_t attributesToDriveAbs = it->second; 10772 auto groupToDriveAbs = mEngine->getVolumeGroupForAttributes(attributesToDriveAbs); 10773 if (groupToDriveAbs == VOLUME_GROUP_NONE) { 10774 ALOGD("%s: no group matching with %s", __FUNCTION__, 10775 toString(attributesToDriveAbs).c_str()); 10776 return volumeDb; 10777 } 10778 10779 float volumeDbMax = curves.volIndexToDb(deviceCategory, curves.getVolumeIndexMax()); 10780 // #ifdef OPLUS_FEATURE_A2DP_SMALLVOLUME 10781 // Chenji.Lei@MULTIMEDIA.AUDIOSERVER.BASE, 2025/05/12, add for A2DP small volume 10782 if (isSteplessVolumeSupport() || mAudioPolicyManagerExt->isA2dpSmallVolumeSupport()) { 10783 int targetIndex = mAudioPolicyManagerExt->getPreScaleAbsVolumeIndex(index, curves.getVolumeIndexMax(), deviceCategory); 10784 ALOGD("%s: index %d for category %d targetIndex %d", __func__, index, deviceCategory, targetIndex); 10785 volumeDbMax = curves.volIndexToDb(deviceCategory, targetIndex); 10786 } 10787 // #endif /* OPLUS_FEATURE_A2DP_SMALLVOLUME */ 10788 VolumeSource vsToDriveAbs = toVolumeSource(groupToDriveAbs); 10789 if (vsToDriveAbs == volumeSource) { 10790 // attenuation is applied by the abs volume controller 10791 // do not mute LE broadcast to allow the secondary device to continue playing 10792 return (index != 0 || volumeDevice == AUDIO_DEVICE_OUT_BLE_BROADCAST) ? volumeDbMax 10793 : volumeDb; 10794 } else { 10795 IVolumeCurves &curvesAbs = getVolumeCurves(vsToDriveAbs); 10796 int indexAbs = curvesAbs.getVolumeIndex({volumeDevice}); 10797 float volumeDbAbs = curvesAbs.volIndexToDb(deviceCategory, indexAbs); 10798 float volumeDbAbsMax = curvesAbs.volIndexToDb(deviceCategory, 10799 curvesAbs.getVolumeIndexMax()); 10800 // #ifdef OPLUS_FEATURE_A2DP_SMALLVOLUME 10801 // Chenji.Lei@MULTIMEDIA.AUDIOSERVER.BASE, 2025/05/12, add for A2DP small volume 10802 if (isSteplessVolumeSupport() || mAudioPolicyManagerExt->isA2dpSmallVolumeSupport()) { 10803 int targetIndex = mAudioPolicyManagerExt->getPreScaleAbsVolumeIndex(index, curves.getVolumeIndexMax(), deviceCategory); 10804 ALOGD("%s: index %d for category %d targetIndex %d", __func__, index, deviceCategory, targetIndex); 10805 volumeDbAbsMax = curves.volIndexToDb(deviceCategory, targetIndex); 10806 } 10807 // #endif /* OPLUS_FEATURE_A2DP_SMALLVOLUME */ 10808 float newVolumeDb = fminf(volumeDb + volumeDbAbsMax - volumeDbAbs, volumeDbMax); 10809 ALOGV("%s: abs vol stream %d with attenuation %f is adjusting stream %d from " 10810 "attenuation %f to attenuation %f %f", __func__, vsToDriveAbs, volumeDbAbs, 10811 volumeSource, volumeDb, newVolumeDb, volumeDbMax); 10812 return newVolumeDb; 10813 } 10814 } 10815 return volumeDb; 10816 }
07-09
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值